From ffd4a48187a8f0158758d6205210bbef9601fe52 Mon Sep 17 00:00:00 2001 From: James Baker Date: Wed, 18 Sep 2019 10:57:25 +0100 Subject: [PATCH 01/23] Nested metric registries don't cause a sortedmap creation This might seem like an unnecessary optimization, but profiling internally implies this uses plenty of CPU and allocates gigabytes and gigabytes and gigabytes (about 1.8GB of allocations to report metrics). BLUF: With nested tags, our tooling spends all of its time recreating nested sorted maps and rehashing, which is very expensive. That said, breaking binary backcompat here is super expensive, as we know from past experience. This PR solves this issue. We no longer use the immutables generated class, though we do use the builder. Instead, we use our own implementation, which hashes consistently and uses two map implementations to ensure that in the cases we see, equality, hash, and extending with another tag are cheap. --- .../AbstractTaggedMetricRegistry.java | 6 +- .../metrics/registry/ExtraEntrySortedMap.java | 239 ++++++++++++++++++ .../tritium/metrics/registry/MetricName.java | 10 +- .../metrics/registry/PrehashedSortedMap.java | 45 ++++ .../metrics/registry/RealMetricName.java | 83 ++++++ 5 files changed, 376 insertions(+), 7 deletions(-) create mode 100644 tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/ExtraEntrySortedMap.java create mode 100644 tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/PrehashedSortedMap.java create mode 100644 tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/RealMetricName.java diff --git a/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/AbstractTaggedMetricRegistry.java b/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/AbstractTaggedMetricRegistry.java index 9f0d83de5..e8a09ddaf 100644 --- a/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/AbstractTaggedMetricRegistry.java +++ b/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/AbstractTaggedMetricRegistry.java @@ -152,11 +152,7 @@ public final Map getMetrics() { result.putAll(registry); taggedRegistries.forEach((tag, metrics) -> metrics.getMetrics() .forEach((metricName, metric) -> result.put( - MetricName.builder() - .from(metricName) - .putSafeTags(tag.getKey(), tag.getValue()) - .build(), - metric))); + RealMetricName.create(metricName, tag.getKey(), tag.getValue()), metric))); return result.build(); } diff --git a/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/ExtraEntrySortedMap.java b/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/ExtraEntrySortedMap.java new file mode 100644 index 000000000..c9dbbe29a --- /dev/null +++ b/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/ExtraEntrySortedMap.java @@ -0,0 +1,239 @@ +/* + * (c) Copyright 2019 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.palantir.tritium.metrics.registry; + +import com.google.common.collect.AbstractIterator; +import com.google.common.collect.Iterators; +import com.google.common.collect.Maps; +import com.google.common.collect.Ordering; +import com.google.common.collect.PeekingIterator; +import java.util.AbstractCollection; +import java.util.AbstractMap; +import java.util.AbstractSet; +import java.util.Collection; +import java.util.Comparator; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; + +final class ExtraEntrySortedMap extends AbstractMap implements SortedMap { + private final Ordering ordering; + private final SortedMap left; + private final K extraKey; + private final V extraValue; + private final int extraEntryHashCode; + + ExtraEntrySortedMap(SortedMap left, K extraKey, V extraValue) { + this.left = left; + this.extraKey = extraKey; + this.extraValue = extraValue; + this.ordering = Ordering.from(left.comparator()); + this.extraEntryHashCode = Maps.immutableEntry(extraKey, extraValue).hashCode(); + } + + @Override + public Comparator comparator() { + return left.comparator(); + } + + @Override + public SortedMap subMap(K fromKey, K toKey) { + SortedMap delegate = left.subMap(fromKey, toKey); + if (ordering.compare(fromKey, extraKey) <= 0 && ordering.compare(toKey, extraKey) > 0) { + return new ExtraEntrySortedMap<>(delegate, extraKey, extraValue); + } + return delegate; + } + + @Override + public SortedMap headMap(K toKey) { + SortedMap delegate = left.headMap(toKey); + if (ordering.compare(toKey, extraKey) > 0) { + return new ExtraEntrySortedMap<>(delegate, extraKey, extraValue); + } + return delegate; + } + + @Override + public SortedMap tailMap(K fromKey) { + SortedMap delegate = left.tailMap(fromKey); + if (ordering.compare(fromKey, extraKey) <= 0) { + return new ExtraEntrySortedMap<>(delegate, extraKey, extraValue); + } + return delegate; + } + + @Override + public K firstKey() { + if (left.isEmpty()) { + return extraKey; + } + return ordering.min(left.firstKey(), extraKey); + } + + @Override + public K lastKey() { + if (left.isEmpty()) { + return extraKey; + } + return ordering.max(left.lastKey(), extraKey); + } + + @Override + public int size() { + return left.size() + 1; + } + + @Override + public boolean isEmpty() { + return left.isEmpty(); + } + + @Override + public boolean containsKey(Object key) { + return extraKey.equals(key) || left.containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return extraValue.equals(value) || left.containsValue(value); + } + + @Override + public V get(Object key) { + if (extraKey.equals(key)) { + return extraValue; + } + return left.get(key); + } + + @Override + public V put(K key, V value) { + throw new UnsupportedOperationException(); + } + + @Override + public V remove(Object key) { + throw new UnsupportedOperationException(); + } + + @Override + public void putAll(Map other) { + throw new UnsupportedOperationException(); + } + + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + + @Override + public Set keySet() { + return new AbstractSet() { + @Override + public Iterator iterator() { + return new MergeIterator<>(left.keySet().iterator(), extraKey, ordering); + } + + @Override + public int size() { + return left.size() + 1; + } + }; + } + + @Override + public Collection values() { + return new AbstractCollection() { + @Override + public Iterator iterator() { + return keySet().stream().map(ExtraEntrySortedMap.this::get).iterator(); + } + + @Override + public int size() { + return left.values().size() + 1; + } + }; + } + + @Override + public Set> entrySet() { + return new AbstractSet>() { + @Override + public Iterator> iterator() { + return new MergeIterator<>( + left.entrySet().iterator(), + Maps.immutableEntry(extraKey, extraValue), + ordering.onResultOf(Map.Entry::getKey)); + } + + @Override + public int size() { + return left.size() + 1; + } + }; + } + + @Override + public boolean equals(Object other) { + if (other instanceof ExtraEntrySortedMap) { + ExtraEntrySortedMap otherMap = (ExtraEntrySortedMap) other; + if (extraKey.equals(otherMap.extraKey) && extraValue.equals(otherMap.extraValue)) { + return left.equals(otherMap.left); + } + } + return super.equals(other); + } + + @Override + public int hashCode() { + return left.hashCode() + extraEntryHashCode; + } + + private static final class MergeIterator extends AbstractIterator { + private final PeekingIterator delegate; + private final T option; + private final Comparator comparator; + private boolean usedOption = false; + + private MergeIterator(Iterator delegate, T option, Comparator comparator) { + this.delegate = Iterators.peekingIterator(delegate); + this.option = option; + this.comparator = comparator; + } + + @Override + protected T computeNext() { + if (!delegate.hasNext() && usedOption) { + return endOfData(); + } else if (!delegate.hasNext()) { + usedOption = true; + return option; + } else if (usedOption) { + return delegate.next(); + } + T element = delegate.peek(); + if (comparator.compare(element, option) >= 0) { + usedOption = true; + return option; + } + return delegate.next(); + } + } +} diff --git a/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/MetricName.java b/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/MetricName.java index d79dc5237..8cf3e9358 100644 --- a/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/MetricName.java +++ b/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/MetricName.java @@ -20,7 +20,7 @@ import java.util.SortedMap; import org.immutables.value.Value; -@Value.Immutable(prehash = true) +@Value.Immutable @Value.Style( get = {"get*", "is*"}, overshadowImplementation = true, @@ -46,5 +46,11 @@ static Builder builder() { return new Builder(); } - class Builder extends ImmutableMetricName.Builder {} + class Builder extends ImmutableMetricName.Builder { + // We cannot use the immutables implementation because it causes too much hashcode pain. + @Override + public MetricName build() { + return RealMetricName.create(super.build()); + } + } } diff --git a/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/PrehashedSortedMap.java b/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/PrehashedSortedMap.java new file mode 100644 index 000000000..c24fae3a6 --- /dev/null +++ b/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/PrehashedSortedMap.java @@ -0,0 +1,45 @@ +/* + * (c) Copyright 2019 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.palantir.tritium.metrics.registry; + +import com.google.common.collect.ForwardingSortedMap; +import java.util.SortedMap; + +/** + * A sorted map implementation which prehashes for faster usage in hashmaps. + * This is only safe for immutable underlying maps. + */ +final class PrehashedSortedMap extends ForwardingSortedMap { + private final SortedMap delegate; + private final int hashCode; + + PrehashedSortedMap(SortedMap delegate) { + this.delegate = delegate; + this.hashCode = delegate.hashCode(); + } + + @Override + protected SortedMap delegate() { + return delegate; + } + + @SuppressWarnings("checkstyle:EqualsHashCode") + @Override + public int hashCode() { + return hashCode; + } +} diff --git a/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/RealMetricName.java b/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/RealMetricName.java new file mode 100644 index 000000000..4a4389b5a --- /dev/null +++ b/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/RealMetricName.java @@ -0,0 +1,83 @@ +/* + * (c) Copyright 2019 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.palantir.tritium.metrics.registry; + +import com.google.common.collect.ImmutableSortedMap; +import java.util.SortedMap; + +final class RealMetricName implements MetricName { + private final String safeName; + private final SortedMap safeTags; + private final int hashCode; + + private RealMetricName(String safeName, SortedMap safeTags) { + this.safeName = safeName; + this.safeTags = safeTags; + this.hashCode = computeHashCode(); + } + + private int computeHashCode() { + int hash = 5381; + hash += (hash << 5) + safeName().hashCode(); + hash += (hash << 5) + safeTags().hashCode(); + return hash; + } + + @Override + public String safeName() { + return safeName; + } + + @Override + public SortedMap safeTags() { + return safeTags; + } + + @Override + public int hashCode() { + return hashCode; + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof MetricName)) { + return false; + } + if (this == other) { + return true; + } + MetricName otherMetric = (MetricName) other; + return safeName().equals(otherMetric.safeName()) && safeTags().equals(otherMetric.safeTags()); + } + + static MetricName create(MetricName other) { + return new RealMetricName(other.safeName(), prehash(other.safeTags())); + } + + static MetricName create(MetricName other, String extraTagName, String extraTagValue) { + return new RealMetricName( + other.safeName(), + new ExtraEntrySortedMap<>(prehash(other.safeTags()), extraTagName, extraTagValue)); + } + + private static SortedMap prehash(SortedMap map) { + if (map instanceof PrehashedSortedMap) { + return map; + } + return new PrehashedSortedMap<>(ImmutableSortedMap.copyOfSorted(map)); + } +} From 31021df5fbe45522fcce13f9e117ff8a4c13fabe Mon Sep 17 00:00:00 2001 From: James Baker Date: Wed, 18 Sep 2019 11:42:29 +0100 Subject: [PATCH 02/23] variable names --- .../metrics/registry/ExtraEntrySortedMap.java | 76 +++++++++---------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/ExtraEntrySortedMap.java b/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/ExtraEntrySortedMap.java index c9dbbe29a..eb3f7dc26 100644 --- a/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/ExtraEntrySortedMap.java +++ b/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/ExtraEntrySortedMap.java @@ -33,85 +33,85 @@ final class ExtraEntrySortedMap extends AbstractMap implements SortedMap { private final Ordering ordering; - private final SortedMap left; + private final SortedMap base; private final K extraKey; private final V extraValue; private final int extraEntryHashCode; - ExtraEntrySortedMap(SortedMap left, K extraKey, V extraValue) { - this.left = left; + ExtraEntrySortedMap(SortedMap base, K extraKey, V extraValue) { + this.base = base; this.extraKey = extraKey; this.extraValue = extraValue; - this.ordering = Ordering.from(left.comparator()); + this.ordering = Ordering.from(base.comparator()); this.extraEntryHashCode = Maps.immutableEntry(extraKey, extraValue).hashCode(); } @Override public Comparator comparator() { - return left.comparator(); + return base.comparator(); } @Override public SortedMap subMap(K fromKey, K toKey) { - SortedMap delegate = left.subMap(fromKey, toKey); + SortedMap newBase = base.subMap(fromKey, toKey); if (ordering.compare(fromKey, extraKey) <= 0 && ordering.compare(toKey, extraKey) > 0) { - return new ExtraEntrySortedMap<>(delegate, extraKey, extraValue); + return new ExtraEntrySortedMap<>(newBase, extraKey, extraValue); } - return delegate; + return newBase; } @Override public SortedMap headMap(K toKey) { - SortedMap delegate = left.headMap(toKey); + SortedMap newBase = base.headMap(toKey); if (ordering.compare(toKey, extraKey) > 0) { - return new ExtraEntrySortedMap<>(delegate, extraKey, extraValue); + return new ExtraEntrySortedMap<>(newBase, extraKey, extraValue); } - return delegate; + return newBase; } @Override public SortedMap tailMap(K fromKey) { - SortedMap delegate = left.tailMap(fromKey); + SortedMap newBase = base.tailMap(fromKey); if (ordering.compare(fromKey, extraKey) <= 0) { - return new ExtraEntrySortedMap<>(delegate, extraKey, extraValue); + return new ExtraEntrySortedMap<>(newBase, extraKey, extraValue); } - return delegate; + return newBase; } @Override public K firstKey() { - if (left.isEmpty()) { + if (base.isEmpty()) { return extraKey; } - return ordering.min(left.firstKey(), extraKey); + return ordering.min(base.firstKey(), extraKey); } @Override public K lastKey() { - if (left.isEmpty()) { + if (base.isEmpty()) { return extraKey; } - return ordering.max(left.lastKey(), extraKey); + return ordering.max(base.lastKey(), extraKey); } @Override public int size() { - return left.size() + 1; + return base.size() + 1; } @Override public boolean isEmpty() { - return left.isEmpty(); + return base.isEmpty(); } @Override public boolean containsKey(Object key) { - return extraKey.equals(key) || left.containsKey(key); + return extraKey.equals(key) || base.containsKey(key); } @Override public boolean containsValue(Object value) { - return extraValue.equals(value) || left.containsValue(value); + return extraValue.equals(value) || base.containsValue(value); } @Override @@ -119,7 +119,7 @@ public V get(Object key) { if (extraKey.equals(key)) { return extraValue; } - return left.get(key); + return base.get(key); } @Override @@ -147,12 +147,12 @@ public Set keySet() { return new AbstractSet() { @Override public Iterator iterator() { - return new MergeIterator<>(left.keySet().iterator(), extraKey, ordering); + return new MergeIterator<>(base.keySet().iterator(), extraKey, ordering); } @Override public int size() { - return left.size() + 1; + return base.size() + 1; } }; } @@ -167,7 +167,7 @@ public Iterator iterator() { @Override public int size() { - return left.values().size() + 1; + return base.values().size() + 1; } }; } @@ -178,14 +178,14 @@ public Set> entrySet() { @Override public Iterator> iterator() { return new MergeIterator<>( - left.entrySet().iterator(), + base.entrySet().iterator(), Maps.immutableEntry(extraKey, extraValue), ordering.onResultOf(Map.Entry::getKey)); } @Override public int size() { - return left.size() + 1; + return base.size() + 1; } }; } @@ -195,7 +195,7 @@ public boolean equals(Object other) { if (other instanceof ExtraEntrySortedMap) { ExtraEntrySortedMap otherMap = (ExtraEntrySortedMap) other; if (extraKey.equals(otherMap.extraKey) && extraValue.equals(otherMap.extraValue)) { - return left.equals(otherMap.left); + return base.equals(otherMap.base); } } return super.equals(other); @@ -203,37 +203,37 @@ public boolean equals(Object other) { @Override public int hashCode() { - return left.hashCode() + extraEntryHashCode; + return base.hashCode() + extraEntryHashCode; } private static final class MergeIterator extends AbstractIterator { - private final PeekingIterator delegate; + private final PeekingIterator newBase; private final T option; private final Comparator comparator; private boolean usedOption = false; - private MergeIterator(Iterator delegate, T option, Comparator comparator) { - this.delegate = Iterators.peekingIterator(delegate); + private MergeIterator(Iterator newBase, T option, Comparator comparator) { + this.newBase = Iterators.peekingIterator(newBase); this.option = option; this.comparator = comparator; } @Override protected T computeNext() { - if (!delegate.hasNext() && usedOption) { + if (!newBase.hasNext() && usedOption) { return endOfData(); - } else if (!delegate.hasNext()) { + } else if (!newBase.hasNext()) { usedOption = true; return option; } else if (usedOption) { - return delegate.next(); + return newBase.next(); } - T element = delegate.peek(); + T element = newBase.peek(); if (comparator.compare(element, option) >= 0) { usedOption = true; return option; } - return delegate.next(); + return newBase.next(); } } } From 6c114f265bf95e072069588ffa0ddb6cb8177013 Mon Sep 17 00:00:00 2001 From: James Baker Date: Wed, 18 Sep 2019 10:42:29 +0000 Subject: [PATCH 03/23] Add generated changelog entries --- changelog/@unreleased/pr-430.v2.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelog/@unreleased/pr-430.v2.yml diff --git a/changelog/@unreleased/pr-430.v2.yml b/changelog/@unreleased/pr-430.v2.yml new file mode 100644 index 000000000..d45ac5dc7 --- /dev/null +++ b/changelog/@unreleased/pr-430.v2.yml @@ -0,0 +1,5 @@ +type: improvement +improvement: + description: Nested metric registries cause fewer allocations. + links: + - https://github.com/palantir/tritium/pull/430 From 672cc356e5ed73c3e4957f9c1baf87f675fce638 Mon Sep 17 00:00:00 2001 From: James Baker Date: Wed, 18 Sep 2019 11:49:07 +0100 Subject: [PATCH 04/23] return false --- .../palantir/tritium/metrics/registry/ExtraEntrySortedMap.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/ExtraEntrySortedMap.java b/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/ExtraEntrySortedMap.java index eb3f7dc26..146bbeaa6 100644 --- a/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/ExtraEntrySortedMap.java +++ b/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/ExtraEntrySortedMap.java @@ -101,7 +101,7 @@ public int size() { @Override public boolean isEmpty() { - return base.isEmpty(); + return false; } @Override From a2333e012596670b32b57f4ea53e145d03a5dfba Mon Sep 17 00:00:00 2001 From: James Baker Date: Wed, 18 Sep 2019 11:58:31 +0100 Subject: [PATCH 05/23] Small refactors --- .../metrics/registry/ExtraEntrySortedMap.java | 43 +++---------------- 1 file changed, 5 insertions(+), 38 deletions(-) diff --git a/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/ExtraEntrySortedMap.java b/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/ExtraEntrySortedMap.java index 146bbeaa6..7331a17a7 100644 --- a/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/ExtraEntrySortedMap.java +++ b/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/ExtraEntrySortedMap.java @@ -16,11 +16,11 @@ package com.palantir.tritium.metrics.registry; -import com.google.common.collect.AbstractIterator; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; import com.google.common.collect.Iterators; import com.google.common.collect.Maps; import com.google.common.collect.Ordering; -import com.google.common.collect.PeekingIterator; import java.util.AbstractCollection; import java.util.AbstractMap; import java.util.AbstractSet; @@ -147,7 +147,8 @@ public Set keySet() { return new AbstractSet() { @Override public Iterator iterator() { - return new MergeIterator<>(base.keySet().iterator(), extraKey, ordering); + return Iterables.mergeSorted(ImmutableList.of(base.keySet(), ImmutableList.of(extraKey)), ordering) + .iterator(); } @Override @@ -177,10 +178,7 @@ public Set> entrySet() { return new AbstractSet>() { @Override public Iterator> iterator() { - return new MergeIterator<>( - base.entrySet().iterator(), - Maps.immutableEntry(extraKey, extraValue), - ordering.onResultOf(Map.Entry::getKey)); + return Iterators.transform(keySet().iterator(), key -> Maps.immutableEntry(key, get(key))); } @Override @@ -205,35 +203,4 @@ public boolean equals(Object other) { public int hashCode() { return base.hashCode() + extraEntryHashCode; } - - private static final class MergeIterator extends AbstractIterator { - private final PeekingIterator newBase; - private final T option; - private final Comparator comparator; - private boolean usedOption = false; - - private MergeIterator(Iterator newBase, T option, Comparator comparator) { - this.newBase = Iterators.peekingIterator(newBase); - this.option = option; - this.comparator = comparator; - } - - @Override - protected T computeNext() { - if (!newBase.hasNext() && usedOption) { - return endOfData(); - } else if (!newBase.hasNext()) { - usedOption = true; - return option; - } else if (usedOption) { - return newBase.next(); - } - T element = newBase.peek(); - if (comparator.compare(element, option) >= 0) { - usedOption = true; - return option; - } - return newBase.next(); - } - } } From c8a2198c5d5935784e8f558352816042299d994b Mon Sep 17 00:00:00 2001 From: James Baker Date: Wed, 18 Sep 2019 12:07:11 +0100 Subject: [PATCH 06/23] Benchmark --- .../NestedMetricsBenchmark.java | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 tritium-jmh/src/jmh/java/com/palantir/tritium/microbenchmarks/NestedMetricsBenchmark.java diff --git a/tritium-jmh/src/jmh/java/com/palantir/tritium/microbenchmarks/NestedMetricsBenchmark.java b/tritium-jmh/src/jmh/java/com/palantir/tritium/microbenchmarks/NestedMetricsBenchmark.java new file mode 100644 index 000000000..a74d00309 --- /dev/null +++ b/tritium-jmh/src/jmh/java/com/palantir/tritium/microbenchmarks/NestedMetricsBenchmark.java @@ -0,0 +1,77 @@ +/* + * Copyright 2019 Palantir Technologies, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.palantir.tritium.microbenchmarks; + +import com.codahale.metrics.Meter; +import com.palantir.tritium.metrics.registry.DefaultTaggedMetricRegistry; +import com.palantir.tritium.metrics.registry.MetricName; +import com.palantir.tritium.metrics.registry.TaggedMetricRegistry; +import java.util.concurrent.TimeUnit; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS) +@Fork(1) +@State(Scope.Benchmark) +@SuppressWarnings({"designforextension", "NullAway"}) +public class NestedMetricsBenchmark { + private TaggedMetricRegistry metrics; + + @Setup + public void before() { + metrics = constructBaseRegistry(); + } + + @Benchmark + public void benchmark(Blackhole blackhole) { + metrics.getMetrics().forEach((name, metric) -> { + blackhole.consume(name); + blackhole.consume(metric); + }); + } + + private static TaggedMetricRegistry constructBaseRegistry() { + TaggedMetricRegistry registry = new DefaultTaggedMetricRegistry(); + for (int i = 0; i < 50; i++) { + registry.addMetrics("registry id", Integer.toString(i), constructSubRegistry()); + } + return registry; + } + + private static TaggedMetricRegistry constructSubRegistry() { + TaggedMetricRegistry registry = new DefaultTaggedMetricRegistry(); + for (int i = 0; i < 100; i++) { + Meter meter = registry.meter(MetricName.builder().safeName("some metric " + i).build()); + for (int j = 0; j < 1000; j++) { + meter.mark(); + } + } + return registry; + } +} From 27c36863277df6a627201ec6ff3f634ff95d9f5f Mon Sep 17 00:00:00 2001 From: James Baker Date: Wed, 18 Sep 2019 12:13:01 +0100 Subject: [PATCH 07/23] Add a forEach method on metrics --- .../microbenchmarks/NestedMetricsBenchmark.java | 10 +++++++++- .../registry/AbstractTaggedMetricRegistry.java | 13 +++++++++++++ .../metrics/registry/DropwizardTaggedMetricSet.java | 7 +++++++ .../tritium/metrics/registry/TaggedMetricSet.java | 5 +++++ 4 files changed, 34 insertions(+), 1 deletion(-) diff --git a/tritium-jmh/src/jmh/java/com/palantir/tritium/microbenchmarks/NestedMetricsBenchmark.java b/tritium-jmh/src/jmh/java/com/palantir/tritium/microbenchmarks/NestedMetricsBenchmark.java index a74d00309..37ef3a2c6 100644 --- a/tritium-jmh/src/jmh/java/com/palantir/tritium/microbenchmarks/NestedMetricsBenchmark.java +++ b/tritium-jmh/src/jmh/java/com/palantir/tritium/microbenchmarks/NestedMetricsBenchmark.java @@ -49,13 +49,21 @@ public void before() { } @Benchmark - public void benchmark(Blackhole blackhole) { + public void benchmarkGetMetrics(Blackhole blackhole) { metrics.getMetrics().forEach((name, metric) -> { blackhole.consume(name); blackhole.consume(metric); }); } + @Benchmark + public void benchmarkForEach(Blackhole blackhole) { + metrics.forEachMetric((name, metric) -> { + blackhole.consume(name); + blackhole.consume(metric); + }); + } + private static TaggedMetricRegistry constructBaseRegistry() { TaggedMetricRegistry registry = new DefaultTaggedMetricRegistry(); for (int i = 0; i < 50; i++) { diff --git a/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/AbstractTaggedMetricRegistry.java b/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/AbstractTaggedMetricRegistry.java index e8a09ddaf..6a7546192 100644 --- a/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/AbstractTaggedMetricRegistry.java +++ b/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/AbstractTaggedMetricRegistry.java @@ -32,6 +32,7 @@ import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.BiConsumer; import java.util.function.Supplier; import javax.annotation.Nonnull; @@ -157,6 +158,18 @@ public final Map getMetrics() { return result.build(); } + @Override + public final void forEachMetric(BiConsumer consumer) { + registry.forEach(consumer); + taggedRegistries.forEach((tag, metrics) -> metrics.forEachMetric((metricName, metric) -> + consumer.accept( + MetricName.builder() + .from(metricName) + .putSafeTags(tag.getKey(), tag.getValue()) + .build(), + metric))); + } + @Override public final Optional remove(MetricName metricName) { return Optional.ofNullable(registry.remove(metricName)); diff --git a/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/DropwizardTaggedMetricSet.java b/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/DropwizardTaggedMetricSet.java index adb04939e..85b6639b4 100644 --- a/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/DropwizardTaggedMetricSet.java +++ b/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/DropwizardTaggedMetricSet.java @@ -21,6 +21,7 @@ import com.codahale.metrics.Metric; import com.codahale.metrics.MetricSet; import java.util.Map; +import java.util.function.BiConsumer; public final class DropwizardTaggedMetricSet implements TaggedMetricSet { private final MetricSet metricSet; @@ -34,4 +35,10 @@ public Map getMetrics() { return metricSet.getMetrics().entrySet().stream() .collect(toMap(entry -> MetricName.builder().safeName(entry.getKey()).build(), Map.Entry::getValue)); } + + @Override + public void forEachMetric(BiConsumer consumer) { + metricSet.getMetrics() + .forEach((name, metric) -> consumer.accept(MetricName.builder().safeName(name).build(), metric)); + } } diff --git a/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/TaggedMetricSet.java b/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/TaggedMetricSet.java index 7af6a115a..8a989502e 100644 --- a/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/TaggedMetricSet.java +++ b/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/TaggedMetricSet.java @@ -18,6 +18,7 @@ import com.codahale.metrics.Metric; import java.util.Map; +import java.util.function.BiConsumer; public interface TaggedMetricSet { /** @@ -26,4 +27,8 @@ public interface TaggedMetricSet { * @return map of metrics */ Map getMetrics(); + + default void forEachMetric(BiConsumer consumer) { + getMetrics().forEach(consumer); + } } From 161a4edce2ff460a5c6f76ff8c3fba4917fcc7f9 Mon Sep 17 00:00:00 2001 From: James Baker Date: Wed, 18 Sep 2019 12:22:03 +0100 Subject: [PATCH 08/23] Foreach optimized --- .../metrics/registry/AbstractTaggedMetricRegistry.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/AbstractTaggedMetricRegistry.java b/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/AbstractTaggedMetricRegistry.java index 6a7546192..f6ec10061 100644 --- a/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/AbstractTaggedMetricRegistry.java +++ b/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/AbstractTaggedMetricRegistry.java @@ -163,10 +163,7 @@ public final void forEachMetric(BiConsumer consumer) { registry.forEach(consumer); taggedRegistries.forEach((tag, metrics) -> metrics.forEachMetric((metricName, metric) -> consumer.accept( - MetricName.builder() - .from(metricName) - .putSafeTags(tag.getKey(), tag.getValue()) - .build(), + RealMetricName.create(metricName, tag.getKey(), tag.getValue()), metric))); } From 8a352e51f863b111e77b9a6489bc8ba7d4942a17 Mon Sep 17 00:00:00 2001 From: James Baker Date: Wed, 18 Sep 2019 12:22:11 +0100 Subject: [PATCH 09/23] Add gc profiler --- .../microbenchmarks/NestedMetricsBenchmark.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tritium-jmh/src/jmh/java/com/palantir/tritium/microbenchmarks/NestedMetricsBenchmark.java b/tritium-jmh/src/jmh/java/com/palantir/tritium/microbenchmarks/NestedMetricsBenchmark.java index 37ef3a2c6..241c22cdf 100644 --- a/tritium-jmh/src/jmh/java/com/palantir/tritium/microbenchmarks/NestedMetricsBenchmark.java +++ b/tritium-jmh/src/jmh/java/com/palantir/tritium/microbenchmarks/NestedMetricsBenchmark.java @@ -32,6 +32,10 @@ import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.Warmup; import org.openjdk.jmh.infra.Blackhole; +import org.openjdk.jmh.profile.GCProfiler; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) @@ -82,4 +86,12 @@ private static TaggedMetricRegistry constructSubRegistry() { } return registry; } + + public static void main(String[] args) throws Exception { + Options opt = new OptionsBuilder() + .include(NestedMetricsBenchmark.class.getSimpleName()) + .addProfiler(GCProfiler.class) + .build(); + new Runner(opt).run(); + } } From c17f1812213853566c152842cea9e0b0cee98b4b Mon Sep 17 00:00:00 2001 From: James Baker Date: Wed, 18 Sep 2019 12:27:56 +0100 Subject: [PATCH 10/23] Actually have another tag --- .../tritium/microbenchmarks/NestedMetricsBenchmark.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tritium-jmh/src/jmh/java/com/palantir/tritium/microbenchmarks/NestedMetricsBenchmark.java b/tritium-jmh/src/jmh/java/com/palantir/tritium/microbenchmarks/NestedMetricsBenchmark.java index 241c22cdf..00e57a83c 100644 --- a/tritium-jmh/src/jmh/java/com/palantir/tritium/microbenchmarks/NestedMetricsBenchmark.java +++ b/tritium-jmh/src/jmh/java/com/palantir/tritium/microbenchmarks/NestedMetricsBenchmark.java @@ -79,7 +79,10 @@ private static TaggedMetricRegistry constructBaseRegistry() { private static TaggedMetricRegistry constructSubRegistry() { TaggedMetricRegistry registry = new DefaultTaggedMetricRegistry(); for (int i = 0; i < 100; i++) { - Meter meter = registry.meter(MetricName.builder().safeName("some metric " + i).build()); + Meter meter = registry.meter(MetricName.builder() + .safeName("some metric " + i) + .putSafeTags("some tag", "some tag value") + .build()); for (int j = 0; j < 1000; j++) { meter.mark(); } From 391b6dd7a3d5d47b8d50ca9911a0488c5ca72968 Mon Sep 17 00:00:00 2001 From: James Baker Date: Wed, 18 Sep 2019 13:15:52 +0100 Subject: [PATCH 11/23] errorprone --- .../palantir/tritium/metrics/registry/ExtraEntrySortedMap.java | 1 + 1 file changed, 1 insertion(+) diff --git a/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/ExtraEntrySortedMap.java b/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/ExtraEntrySortedMap.java index 7331a17a7..b7b7861ff 100644 --- a/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/ExtraEntrySortedMap.java +++ b/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/ExtraEntrySortedMap.java @@ -31,6 +31,7 @@ import java.util.Set; import java.util.SortedMap; +@SuppressWarnings({"JdkObsolete", "NullableDereference"}) final class ExtraEntrySortedMap extends AbstractMap implements SortedMap { private final Ordering ordering; private final SortedMap base; From 4843289aff95dee25bab80a9460f891faa1adaa4 Mon Sep 17 00:00:00 2001 From: James Baker Date: Wed, 18 Sep 2019 13:19:10 +0100 Subject: [PATCH 12/23] extra constructor --- .../tritium/metrics/registry/DropwizardTaggedMetricSet.java | 3 +-- .../palantir/tritium/metrics/registry/RealMetricName.java | 5 +++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/DropwizardTaggedMetricSet.java b/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/DropwizardTaggedMetricSet.java index 85b6639b4..e30d2549e 100644 --- a/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/DropwizardTaggedMetricSet.java +++ b/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/DropwizardTaggedMetricSet.java @@ -38,7 +38,6 @@ public Map getMetrics() { @Override public void forEachMetric(BiConsumer consumer) { - metricSet.getMetrics() - .forEach((name, metric) -> consumer.accept(MetricName.builder().safeName(name).build(), metric)); + metricSet.getMetrics().forEach((name, metric) -> consumer.accept(RealMetricName.create(name), metric)); } } diff --git a/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/RealMetricName.java b/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/RealMetricName.java index 4a4389b5a..eeccacdb5 100644 --- a/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/RealMetricName.java +++ b/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/RealMetricName.java @@ -20,6 +20,7 @@ import java.util.SortedMap; final class RealMetricName implements MetricName { + private static final SortedMap EMPTY = prehash(ImmutableSortedMap.of()); private final String safeName; private final SortedMap safeTags; private final int hashCode; @@ -64,6 +65,10 @@ public boolean equals(Object other) { return safeName().equals(otherMetric.safeName()) && safeTags().equals(otherMetric.safeTags()); } + static MetricName create(String safeName) { + return new RealMetricName(safeName, EMPTY); + } + static MetricName create(MetricName other) { return new RealMetricName(other.safeName(), prehash(other.safeTags())); } From f8295fdd19b5bd670c71a26c59d602e96ceef8ed Mon Sep 17 00:00:00 2001 From: James Baker Date: Wed, 18 Sep 2019 13:22:18 +0100 Subject: [PATCH 13/23] Deal with painful thing --- .../palantir/tritium/metrics/registry/ExtraEntrySortedMap.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/ExtraEntrySortedMap.java b/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/ExtraEntrySortedMap.java index b7b7861ff..fc24ca54b 100644 --- a/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/ExtraEntrySortedMap.java +++ b/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/ExtraEntrySortedMap.java @@ -31,7 +31,7 @@ import java.util.Set; import java.util.SortedMap; -@SuppressWarnings({"JdkObsolete", "NullableDereference"}) +@SuppressWarnings({"JdkObsolete", "NullableDereference", "NullAway"}) final class ExtraEntrySortedMap extends AbstractMap implements SortedMap { private final Ordering ordering; private final SortedMap base; From bea4cb9d1105d2cfd3007c1abcf068f811e6180d Mon Sep 17 00:00:00 2001 From: Callum Rogers Date: Wed, 18 Sep 2019 13:38:00 +0100 Subject: [PATCH 14/23] Add property based test for ExtraEntrySortedMap --- .gitignore | 3 + build.gradle | 4 +- tritium-registry/build.gradle | 1 + .../registry/ExtraEntrySortedMapTest.java | 92 +++++++++++++++++++ versions.props | 2 + 5 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 tritium-registry/src/test/java/com/palantir/tritium/metrics/registry/ExtraEntrySortedMapTest.java diff --git a/.gitignore b/.gitignore index dd164f5bb..d1eecfbed 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,6 @@ out/ # Gc logs build*.gc.log + +# Jqwik property based testing +.jqwik-database diff --git a/build.gradle b/build.gradle index ddbdfb5d7..ed87d456f 100644 --- a/build.gradle +++ b/build.gradle @@ -125,7 +125,9 @@ allprojects { }) tasks.withType(Test) { - useJUnitPlatform() + useJUnitPlatform { + includeEngines 'jqwik' + } maxParallelForks Runtime.getRuntime().availableProcessors() testLogging { events "passed", "skipped", "failed" diff --git a/tritium-registry/build.gradle b/tritium-registry/build.gradle index 5da998697..fe759f98c 100644 --- a/tritium-registry/build.gradle +++ b/tritium-registry/build.gradle @@ -19,6 +19,7 @@ dependencies { testImplementation 'org.junit.jupiter:junit-jupiter-params' testImplementation 'org.mockito:mockito-core' testImplementation 'org.mockito:mockito-junit-jupiter' + testImplementation 'net.jqwik:jqwik' // Work around https://github.com/immutables/immutables/issues/291 compileOnly 'org.immutables:value::annotations' diff --git a/tritium-registry/src/test/java/com/palantir/tritium/metrics/registry/ExtraEntrySortedMapTest.java b/tritium-registry/src/test/java/com/palantir/tritium/metrics/registry/ExtraEntrySortedMapTest.java new file mode 100644 index 000000000..71b2085ad --- /dev/null +++ b/tritium-registry/src/test/java/com/palantir/tritium/metrics/registry/ExtraEntrySortedMapTest.java @@ -0,0 +1,92 @@ +/* + * (c) Copyright 2019 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.palantir.tritium.metrics.registry; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSortedMap; +import com.google.common.collect.Iterables; +import java.util.Map; +import java.util.SortedMap; +import java.util.function.Function; +import net.jqwik.api.Assume; +import net.jqwik.api.ForAll; +import net.jqwik.api.Property; +import net.jqwik.api.constraints.IntRange; +import net.jqwik.api.constraints.Size; + +class ExtraEntrySortedMapTest { + + @Property(tries = 10_000, seed = "3619154246571270871") + void check_ExtraEntrySortedMap_has_the_same_behaviour_as_an_ImmutableSortedMap_with_an_extra_entry( + @ForAll @Size(max = 10) Map initialValues, + @ForAll Short extraKey, + @ForAll Byte extraValue, + @ForAll @IntRange(max = 5) int paramKeyIndex1, + @ForAll @IntRange(min = 5, max = 10) int paramKeyIndex2) { + + Assume.that(!initialValues.containsKey(extraKey)); + Assume.that(paramKeyIndex1 <= paramKeyIndex2); + Assume.that(paramKeyIndex1 < initialValues.size()); + Assume.that(paramKeyIndex2 < initialValues.size()); + + SortedMap base = ImmutableSortedMap.copyOf(initialValues); + + SortedMap guavaWithExtra = ImmutableSortedMap.naturalOrder() + .putAll(base) + .put(extraKey, extraValue) + .build(); + + SortedMap extraMap = new ExtraEntrySortedMap<>(base, extraKey, extraValue); + + assertThat(extraMap).isEqualTo(guavaWithExtra); + assertThat(extraMap).hasSameHashCodeAs(guavaWithExtra); + + Short paramKey1 = Iterables.get(guavaWithExtra.keySet(), paramKeyIndex1); + Byte paramValue1 = guavaWithExtra.get(paramKey1); + Short paramKey2 = Iterables.get(guavaWithExtra.keySet(), paramKeyIndex2); + + Map, Object>> methodCalls = + ImmutableMap., Object>>builder() + .put("subMap", sortedMap -> sortedMap.subMap(paramKey1, paramKey2)) + .put("headMap", sortedMap -> sortedMap.headMap(paramKey1)) + .put("tailMap", sortedMap -> sortedMap.tailMap(paramKey1)) + .put("containsKey", sortedMap -> sortedMap.containsKey(paramKey1)) + .put("containsValue", sortedMap -> sortedMap.containsValue(paramValue1)) + .put("get", sortedMap -> sortedMap.get(paramKey1)) + .put("firstKey", SortedMap::firstKey) + .put("lastKey", SortedMap::lastKey) + .put("size", SortedMap::size) + .put("isEmpty", SortedMap::isEmpty) + .put("keySet", SortedMap::keySet) + .put("entrySet", SortedMap::entrySet) + .put("values", shortByteSortedMap -> ImmutableList.copyOf(shortByteSortedMap.values())) + .build(); + + methodCalls.forEach((methodCallName, methodCall) -> { + assertThat(methodCall.apply(extraMap)) + .describedAs( + "%s() applied to both extra map %s and guava map %s", + methodCallName, + extraMap, + guavaWithExtra) + .isEqualTo(methodCall.apply(guavaWithExtra)); + }); + } +} diff --git a/versions.props b/versions.props index 9762351d7..b3184fd3b 100644 --- a/versions.props +++ b/versions.props @@ -26,6 +26,8 @@ org.mockito:* = 3.0.0 org.mpierce.metrics.reservoir:* = 1.1.2 org.openjdk.jmh:* = 1.21 org.slf4j:* = 1.7.26 +net.jqwik:jqwik = 1.2.0 + # dependency-upgrader:OFF io.dropwizard.metrics:* = 3.2.5 From dfa341f0d55693f12df3bcb3096815cfc36623f6 Mon Sep 17 00:00:00 2001 From: James Baker Date: Wed, 18 Sep 2019 14:46:16 +0100 Subject: [PATCH 15/23] PR comments --- .../metrics/registry/ExtraEntrySortedMap.java | 23 +++++++++++-------- .../metrics/registry/PrehashedSortedMap.java | 7 +++--- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/ExtraEntrySortedMap.java b/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/ExtraEntrySortedMap.java index fc24ca54b..4e1249ca1 100644 --- a/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/ExtraEntrySortedMap.java +++ b/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/ExtraEntrySortedMap.java @@ -16,7 +16,11 @@ package com.palantir.tritium.metrics.registry; +import static com.palantir.logsafe.Preconditions.checkArgument; +import static com.palantir.logsafe.Preconditions.checkNotNull; + import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSortedMap; import com.google.common.collect.Iterables; import com.google.common.collect.Iterators; import com.google.common.collect.Maps; @@ -34,17 +38,18 @@ @SuppressWarnings({"JdkObsolete", "NullableDereference", "NullAway"}) final class ExtraEntrySortedMap extends AbstractMap implements SortedMap { private final Ordering ordering; - private final SortedMap base; + private final ImmutableSortedMap base; private final K extraKey; private final V extraValue; private final int extraEntryHashCode; - ExtraEntrySortedMap(SortedMap base, K extraKey, V extraValue) { - this.base = base; - this.extraKey = extraKey; - this.extraValue = extraValue; + ExtraEntrySortedMap(ImmutableSortedMap base, K extraKey, V extraValue) { + this.base = checkNotNull(base, "base"); + this.extraKey = checkNotNull(extraKey, "extraKey"); + this.extraValue = checkNotNull(extraValue, "extraValue"); this.ordering = Ordering.from(base.comparator()); this.extraEntryHashCode = Maps.immutableEntry(extraKey, extraValue).hashCode(); + checkArgument(!base.containsKey(extraKey), "Base must not contain the extra key that is to be added"); } @Override @@ -54,7 +59,7 @@ public Comparator comparator() { @Override public SortedMap subMap(K fromKey, K toKey) { - SortedMap newBase = base.subMap(fromKey, toKey); + ImmutableSortedMap newBase = base.subMap(fromKey, toKey); if (ordering.compare(fromKey, extraKey) <= 0 && ordering.compare(toKey, extraKey) > 0) { return new ExtraEntrySortedMap<>(newBase, extraKey, extraValue); } @@ -63,7 +68,7 @@ public SortedMap subMap(K fromKey, K toKey) { @Override public SortedMap headMap(K toKey) { - SortedMap newBase = base.headMap(toKey); + ImmutableSortedMap newBase = base.headMap(toKey); if (ordering.compare(toKey, extraKey) > 0) { return new ExtraEntrySortedMap<>(newBase, extraKey, extraValue); } @@ -72,7 +77,7 @@ public SortedMap headMap(K toKey) { @Override public SortedMap tailMap(K fromKey) { - SortedMap newBase = base.tailMap(fromKey); + ImmutableSortedMap newBase = base.tailMap(fromKey); if (ordering.compare(fromKey, extraKey) <= 0) { return new ExtraEntrySortedMap<>(newBase, extraKey, extraValue); } @@ -164,7 +169,7 @@ public Collection values() { return new AbstractCollection() { @Override public Iterator iterator() { - return keySet().stream().map(ExtraEntrySortedMap.this::get).iterator(); + return Iterators.transform(keySet().iterator(), key -> get(key)); } @Override diff --git a/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/PrehashedSortedMap.java b/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/PrehashedSortedMap.java index c24fae3a6..b8609f9c2 100644 --- a/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/PrehashedSortedMap.java +++ b/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/PrehashedSortedMap.java @@ -17,17 +17,18 @@ package com.palantir.tritium.metrics.registry; import com.google.common.collect.ForwardingSortedMap; +import com.google.common.collect.ImmutableSortedMap; import java.util.SortedMap; /** * A sorted map implementation which prehashes for faster usage in hashmaps. - * This is only safe for immutable underlying maps. + * This is only safe for immutable underlying maps (both the map implementation and the entries must be immutable). */ final class PrehashedSortedMap extends ForwardingSortedMap { - private final SortedMap delegate; + private final ImmutableSortedMap delegate; private final int hashCode; - PrehashedSortedMap(SortedMap delegate) { + PrehashedSortedMap(ImmutableSortedMap delegate) { this.delegate = delegate; this.hashCode = delegate.hashCode(); } From b9db5cb838c15345527b8e55842e2a010d2fa9d8 Mon Sep 17 00:00:00 2001 From: James Baker Date: Wed, 18 Sep 2019 14:54:27 +0100 Subject: [PATCH 16/23] More PR comments --- .../registry/DropwizardTaggedMetricSet.java | 2 +- .../metrics/registry/ExtraEntrySortedMap.java | 15 ++++++++------- .../metrics/registry/PrehashedSortedMap.java | 8 ++++---- .../tritium/metrics/registry/RealMetricName.java | 4 +++- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/DropwizardTaggedMetricSet.java b/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/DropwizardTaggedMetricSet.java index e30d2549e..9c2aed804 100644 --- a/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/DropwizardTaggedMetricSet.java +++ b/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/DropwizardTaggedMetricSet.java @@ -33,7 +33,7 @@ public DropwizardTaggedMetricSet(MetricSet metricSet) { @Override public Map getMetrics() { return metricSet.getMetrics().entrySet().stream() - .collect(toMap(entry -> MetricName.builder().safeName(entry.getKey()).build(), Map.Entry::getValue)); + .collect(toMap(entry -> RealMetricName.create(entry.getKey()), Map.Entry::getValue)); } @Override diff --git a/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/ExtraEntrySortedMap.java b/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/ExtraEntrySortedMap.java index 4e1249ca1..cd32343b6 100644 --- a/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/ExtraEntrySortedMap.java +++ b/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/ExtraEntrySortedMap.java @@ -20,7 +20,6 @@ import static com.palantir.logsafe.Preconditions.checkNotNull; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSortedMap; import com.google.common.collect.Iterables; import com.google.common.collect.Iterators; import com.google.common.collect.Maps; @@ -34,16 +33,17 @@ import java.util.Map; import java.util.Set; import java.util.SortedMap; +import javax.annotation.Nullable; -@SuppressWarnings({"JdkObsolete", "NullableDereference", "NullAway"}) +@SuppressWarnings("JdkObsolete") final class ExtraEntrySortedMap extends AbstractMap implements SortedMap { private final Ordering ordering; - private final ImmutableSortedMap base; + private final SortedMap base; private final K extraKey; private final V extraValue; private final int extraEntryHashCode; - ExtraEntrySortedMap(ImmutableSortedMap base, K extraKey, V extraValue) { + ExtraEntrySortedMap(SortedMap base, K extraKey, V extraValue) { this.base = checkNotNull(base, "base"); this.extraKey = checkNotNull(extraKey, "extraKey"); this.extraValue = checkNotNull(extraValue, "extraValue"); @@ -59,7 +59,7 @@ public Comparator comparator() { @Override public SortedMap subMap(K fromKey, K toKey) { - ImmutableSortedMap newBase = base.subMap(fromKey, toKey); + SortedMap newBase = base.subMap(fromKey, toKey); if (ordering.compare(fromKey, extraKey) <= 0 && ordering.compare(toKey, extraKey) > 0) { return new ExtraEntrySortedMap<>(newBase, extraKey, extraValue); } @@ -68,7 +68,7 @@ public SortedMap subMap(K fromKey, K toKey) { @Override public SortedMap headMap(K toKey) { - ImmutableSortedMap newBase = base.headMap(toKey); + SortedMap newBase = base.headMap(toKey); if (ordering.compare(toKey, extraKey) > 0) { return new ExtraEntrySortedMap<>(newBase, extraKey, extraValue); } @@ -77,7 +77,7 @@ public SortedMap headMap(K toKey) { @Override public SortedMap tailMap(K fromKey) { - ImmutableSortedMap newBase = base.tailMap(fromKey); + SortedMap newBase = base.tailMap(fromKey); if (ordering.compare(fromKey, extraKey) <= 0) { return new ExtraEntrySortedMap<>(newBase, extraKey, extraValue); } @@ -121,6 +121,7 @@ public boolean containsValue(Object value) { } @Override + @Nullable public V get(Object key) { if (extraKey.equals(key)) { return extraValue; diff --git a/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/PrehashedSortedMap.java b/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/PrehashedSortedMap.java index b8609f9c2..4964acb1c 100644 --- a/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/PrehashedSortedMap.java +++ b/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/PrehashedSortedMap.java @@ -28,13 +28,13 @@ final class PrehashedSortedMap extends ForwardingSortedMap { private final ImmutableSortedMap delegate; private final int hashCode; - PrehashedSortedMap(ImmutableSortedMap delegate) { - this.delegate = delegate; - this.hashCode = delegate.hashCode(); + PrehashedSortedMap(SortedMap delegate) { + this.delegate = ImmutableSortedMap.copyOf(delegate); + this.hashCode = this.delegate.hashCode(); } @Override - protected SortedMap delegate() { + protected ImmutableSortedMap delegate() { return delegate; } diff --git a/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/RealMetricName.java b/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/RealMetricName.java index eeccacdb5..10fce5d51 100644 --- a/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/RealMetricName.java +++ b/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/RealMetricName.java @@ -16,6 +16,8 @@ package com.palantir.tritium.metrics.registry; +import static com.palantir.logsafe.Preconditions.checkNotNull; + import com.google.common.collect.ImmutableSortedMap; import java.util.SortedMap; @@ -66,7 +68,7 @@ public boolean equals(Object other) { } static MetricName create(String safeName) { - return new RealMetricName(safeName, EMPTY); + return new RealMetricName(checkNotNull(safeName, "safeName"), EMPTY); } static MetricName create(MetricName other) { From 75fbcb069b79eb97cd610a2071ab3e81bcf8556f Mon Sep 17 00:00:00 2001 From: David Schlosnagle Date: Wed, 18 Sep 2019 09:14:35 -0400 Subject: [PATCH 17/23] Stricter null checks --- .../palantir/tritium/metrics/registry/ExtraEntrySortedMap.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/ExtraEntrySortedMap.java b/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/ExtraEntrySortedMap.java index cd32343b6..773b5cb1c 100644 --- a/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/ExtraEntrySortedMap.java +++ b/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/ExtraEntrySortedMap.java @@ -120,8 +120,8 @@ public boolean containsValue(Object value) { return extraValue.equals(value) || base.containsValue(value); } - @Override @Nullable + @Override public V get(Object key) { if (extraKey.equals(key)) { return extraValue; From 5f5f61cf1db147c4261eae578c39632ef7e8d598 Mon Sep 17 00:00:00 2001 From: David Schlosnagle Date: Wed, 18 Sep 2019 09:22:46 -0400 Subject: [PATCH 18/23] Suppress JDK Obsolete for SortedMap test --- .../tritium/metrics/registry/ExtraEntrySortedMapTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/tritium-registry/src/test/java/com/palantir/tritium/metrics/registry/ExtraEntrySortedMapTest.java b/tritium-registry/src/test/java/com/palantir/tritium/metrics/registry/ExtraEntrySortedMapTest.java index 71b2085ad..72d7b0fe7 100644 --- a/tritium-registry/src/test/java/com/palantir/tritium/metrics/registry/ExtraEntrySortedMapTest.java +++ b/tritium-registry/src/test/java/com/palantir/tritium/metrics/registry/ExtraEntrySortedMapTest.java @@ -31,6 +31,7 @@ import net.jqwik.api.constraints.IntRange; import net.jqwik.api.constraints.Size; +@SuppressWarnings("JdkObsolete") class ExtraEntrySortedMapTest { @Property(tries = 10_000, seed = "3619154246571270871") From d8390ff4b2ea6d861b4b2e078a3b7b74d626e722 Mon Sep 17 00:00:00 2001 From: David Schlosnagle Date: Wed, 18 Sep 2019 09:23:14 -0400 Subject: [PATCH 19/23] Fix copyright header --- .../tritium/microbenchmarks/NestedMetricsBenchmark.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tritium-jmh/src/jmh/java/com/palantir/tritium/microbenchmarks/NestedMetricsBenchmark.java b/tritium-jmh/src/jmh/java/com/palantir/tritium/microbenchmarks/NestedMetricsBenchmark.java index 00e57a83c..5207b0400 100644 --- a/tritium-jmh/src/jmh/java/com/palantir/tritium/microbenchmarks/NestedMetricsBenchmark.java +++ b/tritium-jmh/src/jmh/java/com/palantir/tritium/microbenchmarks/NestedMetricsBenchmark.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 Palantir Technologies, Inc. All rights reserved. + * (c) Copyright 2019 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From 31dc183779f43837fc475bc84a392aa68f02cf89 Mon Sep 17 00:00:00 2001 From: David Schlosnagle Date: Wed, 18 Sep 2019 09:23:25 -0400 Subject: [PATCH 20/23] write locks --- versions.lock | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/versions.lock b/versions.lock index 8123cfcdc..23800c693 100644 --- a/versions.lock +++ b/versions.lock @@ -35,7 +35,10 @@ com.squareup.okhttp3:okhttp:4.0.0 (1 constraints: 0605ff35) com.squareup.okio:okio:2.2.2 (1 constraints: 500c29fd) io.undertow:undertow-core:2.0.20.Final (1 constraints: 4e07ff60) net.bytebuddy:byte-buddy-agent:1.9.10 (1 constraints: 450b52de) -org.apiguardian:apiguardian-api:1.1.0 (5 constraints: 0654a8a8) +net.jqwik:jqwik:1.2.0 (1 constraints: 0505f635) +net.jqwik:jqwik-api:1.2.0 (2 constraints: ce116f10) +net.jqwik:jqwik-engine:1.2.0 (1 constraints: 9607e86c) +org.apiguardian:apiguardian-api:1.1.0 (8 constraints: cc6e0d03) org.assertj:assertj-core:3.13.2 (1 constraints: 3b05403b) org.awaitility:awaitility:4.0.0 (1 constraints: 0605ff35) org.hamcrest:hamcrest:2.1 (1 constraints: 6f0b2cce) @@ -49,10 +52,10 @@ org.junit.jupiter:junit-jupiter:5.5.1 (1 constraints: 0d051436) org.junit.jupiter:junit-jupiter-api:5.5.1 (4 constraints: 2b3e5efe) org.junit.jupiter:junit-jupiter-engine:5.5.1 (1 constraints: 080ed53b) org.junit.jupiter:junit-jupiter-params:5.5.1 (2 constraints: 1413d83b) -org.junit.platform:junit-platform-commons:1.5.1 (2 constraints: d520334a) -org.junit.platform:junit-platform-engine:1.5.1 (1 constraints: a7101db4) +org.junit.platform:junit-platform-commons:1.5.1 (4 constraints: 11348fab) +org.junit.platform:junit-platform-engine:1.5.1 (2 constraints: e31ab64d) org.mockito:mockito-core:3.0.0 (2 constraints: c2132a64) org.mockito:mockito-junit-jupiter:3.0.0 (1 constraints: 0505fa35) org.objenesis:objenesis:2.6 (2 constraints: 271650ee) -org.opentest4j:opentest4j:1.2.0 (2 constraints: cd205b49) +org.opentest4j:opentest4j:1.2.0 (4 constraints: 01343da7) org.slf4j:slf4j-simple:1.7.26 (1 constraints: 4205493b) From b26ad69761d0a7cd434b7568b49dbcbce320a56a Mon Sep 17 00:00:00 2001 From: James Baker Date: Wed, 18 Sep 2019 15:01:24 +0100 Subject: [PATCH 21/23] perf regression --- .../palantir/tritium/metrics/registry/ExtraEntrySortedMap.java | 1 + 1 file changed, 1 insertion(+) diff --git a/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/ExtraEntrySortedMap.java b/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/ExtraEntrySortedMap.java index cd32343b6..b714779fd 100644 --- a/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/ExtraEntrySortedMap.java +++ b/tritium-registry/src/main/java/com/palantir/tritium/metrics/registry/ExtraEntrySortedMap.java @@ -49,6 +49,7 @@ final class ExtraEntrySortedMap extends AbstractMap implements Sorte this.extraValue = checkNotNull(extraValue, "extraValue"); this.ordering = Ordering.from(base.comparator()); this.extraEntryHashCode = Maps.immutableEntry(extraKey, extraValue).hashCode(); + // This line of code is roughly a 50% perf regression for iterating through metrics. Remove if causing issues. checkArgument(!base.containsKey(extraKey), "Base must not contain the extra key that is to be added"); } From 969f8c0ce4e7618e60b1c93a320b9456336cebb5 Mon Sep 17 00:00:00 2001 From: James Baker Date: Wed, 18 Sep 2019 12:07:11 +0100 Subject: [PATCH 22/23] Benchmark --- .../NestedMetricsBenchmark.java | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 tritium-jmh/src/jmh/java/com/palantir/tritium/microbenchmarks/NestedMetricsBenchmark.java diff --git a/tritium-jmh/src/jmh/java/com/palantir/tritium/microbenchmarks/NestedMetricsBenchmark.java b/tritium-jmh/src/jmh/java/com/palantir/tritium/microbenchmarks/NestedMetricsBenchmark.java new file mode 100644 index 000000000..a74d00309 --- /dev/null +++ b/tritium-jmh/src/jmh/java/com/palantir/tritium/microbenchmarks/NestedMetricsBenchmark.java @@ -0,0 +1,77 @@ +/* + * Copyright 2019 Palantir Technologies, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.palantir.tritium.microbenchmarks; + +import com.codahale.metrics.Meter; +import com.palantir.tritium.metrics.registry.DefaultTaggedMetricRegistry; +import com.palantir.tritium.metrics.registry.MetricName; +import com.palantir.tritium.metrics.registry.TaggedMetricRegistry; +import java.util.concurrent.TimeUnit; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS) +@Fork(1) +@State(Scope.Benchmark) +@SuppressWarnings({"designforextension", "NullAway"}) +public class NestedMetricsBenchmark { + private TaggedMetricRegistry metrics; + + @Setup + public void before() { + metrics = constructBaseRegistry(); + } + + @Benchmark + public void benchmark(Blackhole blackhole) { + metrics.getMetrics().forEach((name, metric) -> { + blackhole.consume(name); + blackhole.consume(metric); + }); + } + + private static TaggedMetricRegistry constructBaseRegistry() { + TaggedMetricRegistry registry = new DefaultTaggedMetricRegistry(); + for (int i = 0; i < 50; i++) { + registry.addMetrics("registry id", Integer.toString(i), constructSubRegistry()); + } + return registry; + } + + private static TaggedMetricRegistry constructSubRegistry() { + TaggedMetricRegistry registry = new DefaultTaggedMetricRegistry(); + for (int i = 0; i < 100; i++) { + Meter meter = registry.meter(MetricName.builder().safeName("some metric " + i).build()); + for (int j = 0; j < 1000; j++) { + meter.mark(); + } + } + return registry; + } +} From 2b014f01800755b957ad2d32a17c44c2a365cafe Mon Sep 17 00:00:00 2001 From: James Baker Date: Wed, 18 Sep 2019 20:12:04 +0100 Subject: [PATCH 23/23] baseline... --- .../tritium/microbenchmarks/NestedMetricsBenchmark.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tritium-jmh/src/jmh/java/com/palantir/tritium/microbenchmarks/NestedMetricsBenchmark.java b/tritium-jmh/src/jmh/java/com/palantir/tritium/microbenchmarks/NestedMetricsBenchmark.java index 5207b0400..9a122aed9 100644 --- a/tritium-jmh/src/jmh/java/com/palantir/tritium/microbenchmarks/NestedMetricsBenchmark.java +++ b/tritium-jmh/src/jmh/java/com/palantir/tritium/microbenchmarks/NestedMetricsBenchmark.java @@ -90,7 +90,7 @@ private static TaggedMetricRegistry constructSubRegistry() { return registry; } - public static void main(String[] args) throws Exception { + public static void main(String[] _args) throws Exception { Options opt = new OptionsBuilder() .include(NestedMetricsBenchmark.class.getSimpleName()) .addProfiler(GCProfiler.class)