Skip to content

Commit 5130e00

Browse files
authored
Check scope existence lock free first (#110)
1 parent 3164eb6 commit 5130e00

4 files changed

Lines changed: 90 additions & 15 deletions

File tree

core/build.gradle

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,20 +42,27 @@ task runJmhTests(type: JavaExec, dependsOn: jmhClasses) {
4242
args '-rff', resultFile
4343

4444
// Profile using GC, Threading profilers
45-
args '-prof', 'gc'
46-
args '-prof', 'hs_thr'
45+
if (project.properties.get('busegc', 'true') == 'true') {
46+
args '-prof', 'gc'
47+
args '-prof', 'hs_thr'
48+
49+
// Force GC after every iterations, to make sure that one iteration
50+
// doesn't affect the other one
51+
args '-gc', 'true'
52+
}
53+
4754

4855
// Profile using async-profiling
4956
//
5057
// NOTE: For this to work you need to make sure that async-profiler's library is either
5158
// - Available in LD_LIBRARY_PATH (Linux), DYLD_LIBRARY_PATH (Mac)
5259
// - Available in '-Djava.library.path'
5360
// - Explicitly specified with 'async:libPath=</path/libasyncProfiler.so>'
54-
args '-prof', 'async:event=cpu;direction=forward;output=flamegraph'
61+
args '-prof', project.properties.get('bprof', 'async:event=cpu;direction=forward;output=flamegraph')
5562

56-
// Force GC after every iterations, to make sure that one iteration
57-
// doesn't affect the other one
58-
args '-gc', 'true'
63+
args '-bm', project.properties.get('bm', 'thrpt')
64+
args '-t', project.properties.get('bthreads', '1')
65+
args 'com.uber.m3.tally.' + project.properties.get('benchclass', '')
5966
}
6067

61-
classes.finalizedBy(jmhClasses)
68+
classes.finalizedBy(jmhClasses)

core/jmhFixtures.gradle

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,4 +54,8 @@ dependencies {
5454
jmhFixturesUsageCompile project(project.path)
5555
jmhFixturesCompile('org.openjdk.jmh:jmh-core:1.27')
5656
jmhFixturesCompile('org.openjdk.jmh:jmh-generator-annprocess:1.27')
57+
58+
if (project.gradle.gradleVersion > '5') {
59+
jmhAnnotationProcessor("org.openjdk.jmh:jmh-generator-annprocess:1.27")
60+
}
5761
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package com.uber.m3.tally;
2+
3+
import com.uber.m3.util.Duration;
4+
import com.uber.m3.util.ImmutableMap;
5+
import org.openjdk.jmh.annotations.*;
6+
import org.openjdk.jmh.infra.Blackhole;
7+
8+
import java.util.concurrent.TimeUnit;
9+
10+
@BenchmarkMode(Mode.Throughput)
11+
@OutputTimeUnit(TimeUnit.MILLISECONDS)
12+
@Fork(value = 2, jvmArgsAppend = { "-server", "-XX:+UseG1GC" })
13+
public class ScopeImplConcurrent {
14+
private static String KEYS[] = new String[]{
15+
" ", "0", "@", "P",
16+
};
17+
18+
@Benchmark
19+
public void hotkeyLockContention(Blackhole bh, BenchmarkState state) {
20+
ImmutableMap<String, String> common = new ImmutableMap.Builder<String, String>().build();
21+
for (int i = 0; i < 10000; i++) {
22+
23+
for (String key : KEYS) {
24+
Scope scope = state.scope.computeSubscopeIfAbsent(key, common);
25+
assert scope != null;
26+
bh.consume(scope);
27+
}
28+
}
29+
}
30+
31+
@State(org.openjdk.jmh.annotations.Scope.Benchmark)
32+
public static class BenchmarkState {
33+
34+
private ScopeImpl scope;
35+
36+
@Setup
37+
public void setup() {
38+
this.scope =
39+
(ScopeImpl) new RootScopeBuilder()
40+
.reporter(new TestStatsReporter())
41+
.reportEvery(Duration.MAX_VALUE);
42+
43+
for (String key : KEYS) {
44+
scope.computeSubscopeIfAbsent(key, new ImmutableMap.Builder<String, String>().build());
45+
}
46+
}
47+
48+
@TearDown
49+
public void teardown() {
50+
scope.close();
51+
}
52+
}
53+
}

core/src/main/java/com/uber/m3/tally/ScopeImpl.java

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ public Histogram histogram(String name, @Nullable Buckets buckets) {
101101
);
102102
}
103103

104+
104105
@Override
105106
public Scope tagged(Map<String, String> tags) {
106107
return subScopeHelper(prefix, tags);
@@ -280,15 +281,25 @@ private Scope subScopeHelper(String prefix, Map<String, String> tags) {
280281

281282
String key = keyForPrefixedStringMap(prefix, mergedTags);
282283

284+
return computeSubscopeIfAbsent(key, mergedTags);
285+
}
286+
287+
// This method must only be called on unit tests or benchmarks
288+
protected Scope computeSubscopeIfAbsent(String key, ImmutableMap<String, String> mergedTags) {
289+
Scope scope = registry.subscopes.get(key);
290+
if (scope != null) {
291+
return scope;
292+
}
293+
283294
return registry.subscopes.computeIfAbsent(
284-
key,
285-
(k) -> new ScopeBuilder(scheduler, registry)
286-
.reporter(reporter)
287-
.prefix(prefix)
288-
.separator(separator)
289-
.tags(mergedTags)
290-
.defaultBuckets(defaultBuckets)
291-
.build()
295+
key,
296+
(k) -> new ScopeBuilder(scheduler, registry)
297+
.reporter(reporter)
298+
.prefix(prefix)
299+
.separator(separator)
300+
.tags(mergedTags)
301+
.defaultBuckets(defaultBuckets)
302+
.build()
292303
);
293304
}
294305

0 commit comments

Comments
 (0)