-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Skip redundant tag deduplication #3902
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,18 +13,24 @@ configurations { | |
dependencies { | ||
// Use the baseline to avoid using new APIs in the benchmarks | ||
compileOnly libs.reactor.perfBaseline.core | ||
compileOnly libs.reactor.perfBaseline.coreMicrometer | ||
compileOnly libs.jsr305 | ||
|
||
implementation "org.openjdk.jmh:jmh-core:$jmhVersion" | ||
implementation libs.reactor.perfBaseline.extra, { | ||
exclude group: 'io.projectreactor', module: 'reactor-core' | ||
} | ||
implementation platform(libs.micrometer.bom) | ||
annotationProcessor "org.openjdk.jmh:jmh-generator-annprocess:$jmhVersion" | ||
|
||
current project(':reactor-core') | ||
current project(':reactor-core-micrometer') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please also modify the baseline source set and add the reactor-core-micrometer module with appropriate version. The idea here is that we can have a comparison of performance in the previously released version against the current version. That's why there's a "current" and a "baseline" source set. |
||
baseline libs.reactor.perfBaseline.core, { | ||
changing = true | ||
} | ||
baseline libs.reactor.perfBaseline.coreMicrometer, { | ||
changing = true | ||
} | ||
} | ||
|
||
task jmhProfilers(type: JavaExec, description:'Lists the available profilers for the jmh task', group: 'Development') { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
/* | ||
* Copyright (c) 2024 VMware Inc. or its affiliates, 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 | ||
* | ||
* https://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 reactor.core.observability.micrometer; | ||
|
||
import io.micrometer.core.instrument.Tags; | ||
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.Level; | ||
import org.openjdk.jmh.annotations.Measurement; | ||
import org.openjdk.jmh.annotations.Mode; | ||
import org.openjdk.jmh.annotations.OutputTimeUnit; | ||
import org.openjdk.jmh.annotations.Param; | ||
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.reactivestreams.Publisher; | ||
import reactor.core.publisher.Mono; | ||
|
||
@BenchmarkMode({Mode.AverageTime}) | ||
@Warmup(iterations = 5, time = 5, timeUnit = TimeUnit.SECONDS) | ||
@Measurement(iterations = 5, time = 5, timeUnit = TimeUnit.SECONDS) | ||
@Fork(value = 1) | ||
@OutputTimeUnit(TimeUnit.NANOSECONDS) | ||
@State(Scope.Benchmark) | ||
public class MicrometerMeterListenerConfigurationResolveTagsBenchmark { | ||
@Param({"1|1", "1|2", "1|5", "1|10", "2|2", "2|5", "2|10", "5|5", "5|10", "10|10"}) | ||
private String testCase; | ||
|
||
private Publisher<Void> publisher; | ||
|
||
@Setup(Level.Iteration) | ||
public void setup() { | ||
String[] arguments = testCase.split("\\|", -1); | ||
int distinctTagCount = Integer.parseInt(arguments[0]); | ||
int totalTagCount = Integer.parseInt(arguments[1]); | ||
|
||
publisher = addTags(Mono.empty(), distinctTagCount, totalTagCount); | ||
} | ||
|
||
@SuppressWarnings("unused") | ||
@Benchmark | ||
public Tags measureThroughput() { | ||
return MicrometerMeterListenerConfiguration.resolveTags(publisher, Tags.of("k", "v")); | ||
} | ||
Comment on lines
+57
to
+61
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Benchmark results: Before
After
This shows that the new code is faster (up to 55%) and allocates less memory (up to 50%) in the common case, where few or no keys are duplicated. In cases where keys are heavily duplicated ( There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. micrometer-metrics/micrometer#4959 was just merged yesterday. I'm really curious how that performs together with your changes. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for sharing! Nice timing, and thanks for pushing that @mstyura 💪 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I re-ran the before- and after-benchmarks against the latest Micrometer snapshot. Before
After
We see that in all cases less memory is allocated. The |
||
|
||
private static <T> Mono<T> addTags(Mono<T> source, int distinct, int total) { | ||
if (total == 0) { | ||
return source; | ||
} | ||
|
||
return addTags(source.tag("k-" + total % distinct, "v-" + total), distinct, total - 1); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
/* | ||
* Copyright (c) 2018-2022 VMware Inc. or its affiliates, All Rights Reserved. | ||
* Copyright (c) 2018-2024 VMware Inc. or its affiliates, 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. | ||
|
@@ -295,9 +295,9 @@ static Tags resolveTags(Publisher<?> source, Tags tags) { | |
Scannable scannable = Scannable.from(source); | ||
|
||
if (scannable.isScanAvailable()) { | ||
List<Tag> discoveredTags = scannable.tagsDeduplicated() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The method There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't suspect There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Deprecating the method SGTM; indeed a GitHub search does not seem to turn up additional usages. I've proposed a |
||
.entrySet().stream() | ||
.map(e -> Tag.of(e.getKey(), e.getValue())) | ||
// `Tags#and` deduplicates tags by key, retaining the last value as required. | ||
List<Tag> discoveredTags = scannable.tags() | ||
.map(t -> Tag.of(t.getT1(), t.getT2())) | ||
.collect(Collectors.toList()); | ||
return tags.and(discoveredTags); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not too familiar with Gradle, so happy to receive feedback if I should have modified this file (or
gradle/libs.versions.toml
below) in some other way.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, this line together with the versions catalog are ok. I'll comment below what needs to be adjusted.