Skip to content

Commit cdb382b

Browse files
authored
Merge branch 'master' into sarahchen6/migrate-feature-flagging-tests-to-java
2 parents f8a8e04 + 8206fbc commit cdb382b

1 file changed

Lines changed: 172 additions & 0 deletions

File tree

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
package datadog.trace.util;
2+
3+
import java.util.concurrent.atomic.AtomicInteger;
4+
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
5+
import java.util.function.Supplier;
6+
import org.openjdk.jmh.annotations.Benchmark;
7+
import org.openjdk.jmh.annotations.Fork;
8+
import org.openjdk.jmh.annotations.Measurement;
9+
import org.openjdk.jmh.annotations.Scope;
10+
import org.openjdk.jmh.annotations.State;
11+
import org.openjdk.jmh.annotations.Threads;
12+
import org.openjdk.jmh.annotations.Warmup;
13+
14+
/**
15+
* The choice between {@link AtomicInteger} and {@link AtomicIntegerFieldUpdater} depends on the
16+
* access pattern:
17+
*
18+
* <ul>
19+
* <li><b>Frequently constructed objects</b>: prefer AtomicFieldUpdater. It saves 16 B/op at
20+
* construction (one fewer object) — the GC impact of that allocation compounds over the
21+
* lifetime of the application.
22+
* <li><b>Long-lived objects with heavy incrementAndGet use</b>: AtomicInteger is ~33% faster for
23+
* incrementAndGet (121M vs 91M ops/s). AtomicIntegerFieldUpdater carries overhead from its
24+
* reflective field-access path that C2 cannot intrinsify as cleanly.
25+
* <li><b>Read-heavy paths</b>: essentially a wash (both ~2 B ops/s).
26+
* </ul>
27+
*
28+
* AtomicFieldUpdater supports {@code int}, {@code long}, and reference types.
29+
*
30+
* <p><b>Future:</b> {@code VarHandle} (Java 9+) is the modern replacement for
31+
* AtomicIntegerFieldUpdater. It avoids the reflective field-access overhead, which should close
32+
* the incrementAndGet gap with AtomicInteger while retaining the construction allocation advantage.
33+
* Not available here because internal-api targets Java 8.
34+
*
35+
* <code> Java 17 - MacBook M1 Pro Max - 8 threads
36+
* Benchmark Mode Cnt Score Error Units
37+
* AtomicsBenchmark.atomicFieldUpdater_construction thrpt 6 2215272588.708 ± 88556141.052 ops/s
38+
* AtomicsBenchmark.atomicFieldUpdater_construction:gc.alloc.rate.norm thrpt 6 16.000 ± 0.001 B/op
39+
*
40+
* AtomicsBenchmark.atomicFieldUpdater_get thrpt 6 2174739788.040 ± 56980971.014 ops/s
41+
* AtomicsBenchmark.atomicFieldUpdater_get:gc.alloc.rate.norm thrpt 6 ≈ 10⁻⁶ B/op
42+
*
43+
* AtomicsBenchmark.atomicFieldUpdater_getVolatile thrpt 6 2157331061.707 ± 136900932.336 ops/s
44+
* AtomicsBenchmark.atomicFieldUpdater_getVolatile:gc.alloc.rate.norm thrpt 6 ≈ 10⁻⁶ B/op
45+
*
46+
* AtomicsBenchmark.atomicFieldUpdater_incrementAndGet thrpt 6 90785783.320 ± 7650837.727 ops/s
47+
* AtomicsBenchmark.atomicFieldUpdater_incrementAndGet:gc.alloc.rate.norm thrpt 6 ≈ 10⁻⁴ B/op
48+
*
49+
* AtomicsBenchmark.atomic_construction thrpt 6 1872153219.594 ± 83252749.463 ops/s
50+
* AtomicsBenchmark.atomic_construction:gc.alloc.rate.norm thrpt 6 32.000 ± 0.001 B/op
51+
*
52+
* AtomicsBenchmark.atomic_incrementAndGet thrpt 6 120835704.294 ± 23025991.947 ops/s
53+
* AtomicsBenchmark.atomic_incrementAndGet:gc.alloc.rate.norm thrpt 6 ≈ 10⁻⁴ B/op
54+
*
55+
* AtomicsBenchmark.atomic_read thrpt 6 1968266961.596 ± 57765039.412 ops/s
56+
* AtomicsBenchmark.atomic_read:gc.alloc.rate.norm thrpt 6 ≈ 10⁻⁶ B/op
57+
*/
58+
@Fork(2)
59+
@Warmup(iterations = 2)
60+
@Measurement(iterations = 3)
61+
@Threads(8)
62+
public class AtomicsBenchmark {
63+
static int SIZE = 32;
64+
65+
static final class AtomicHolder {
66+
final AtomicInteger atomic;
67+
68+
AtomicHolder(int num) {
69+
this.atomic = new AtomicInteger(num);
70+
}
71+
72+
int get() {
73+
return this.atomic.get();
74+
}
75+
76+
int incrementAndGet() {
77+
return this.atomic.incrementAndGet();
78+
}
79+
}
80+
81+
static final class FieldHolder {
82+
static final AtomicIntegerFieldUpdater<FieldHolder> AFU_FIELD =
83+
AtomicIntegerFieldUpdater.newUpdater(FieldHolder.class, "field");
84+
85+
volatile int field;
86+
87+
FieldHolder(int num) {
88+
this.field = num;
89+
}
90+
91+
int getVolatile() {
92+
return this.field;
93+
}
94+
95+
int get() {
96+
return AFU_FIELD.get(this);
97+
}
98+
99+
int incrementAndGet() {
100+
return AFU_FIELD.incrementAndGet(this);
101+
}
102+
}
103+
104+
static final AtomicHolder[] atomicHolders =
105+
init(
106+
() -> {
107+
AtomicHolder[] holders = new AtomicHolder[SIZE];
108+
for (int i = 0; i < holders.length; ++i) {
109+
holders[i] = new AtomicHolder(i * 2);
110+
}
111+
return holders;
112+
});
113+
114+
static final FieldHolder[] fieldHolders =
115+
init(
116+
() -> {
117+
FieldHolder[] holders = new FieldHolder[SIZE];
118+
for (int i = 0; i < holders.length; ++i) {
119+
holders[i] = new FieldHolder(i * 2);
120+
}
121+
return holders;
122+
});
123+
124+
static final <T> T init(Supplier<T> supplier) {
125+
return supplier.get();
126+
}
127+
128+
@State(Scope.Thread)
129+
public static class BenchmarkState {
130+
int index = 0;
131+
132+
<T> T next(T[] holders) {
133+
if (++index >= holders.length) index = 0;
134+
return holders[index];
135+
}
136+
}
137+
138+
@Benchmark
139+
public Object atomic_construction() {
140+
return new AtomicHolder(0);
141+
}
142+
143+
@Benchmark
144+
public int atomic_incrementAndGet(BenchmarkState state) {
145+
return state.next(atomicHolders).incrementAndGet();
146+
}
147+
148+
@Benchmark
149+
public Object atomic_read(BenchmarkState state) {
150+
return state.next(atomicHolders).get();
151+
}
152+
153+
@Benchmark
154+
public Object atomicFieldUpdater_construction() {
155+
return new FieldHolder(0);
156+
}
157+
158+
@Benchmark
159+
public Object atomicFieldUpdater_getVolatile(BenchmarkState state) {
160+
return state.next(fieldHolders).getVolatile();
161+
}
162+
163+
@Benchmark
164+
public Object atomicFieldUpdater_get(BenchmarkState state) {
165+
return state.next(fieldHolders).get();
166+
}
167+
168+
@Benchmark
169+
public int atomicFieldUpdater_incrementAndGet(BenchmarkState state) {
170+
return state.next(fieldHolders).incrementAndGet();
171+
}
172+
}

0 commit comments

Comments
 (0)