-
Notifications
You must be signed in to change notification settings - Fork 331
Expand file tree
/
Copy pathAtomicsBenchmark.java
More file actions
165 lines (141 loc) · 5.51 KB
/
AtomicsBenchmark.java
File metadata and controls
165 lines (141 loc) · 5.51 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
package datadog.trace.util;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.function.Supplier;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Threads;
import org.openjdk.jmh.annotations.Warmup;
/**
* <ul>
* <li>(RECOMMENDED) AtomicFieldUpdater - especially, when containing object is frequently constructed
* <li>Atomic - usually, performs similarly to AtomicFieldUpdater - worse inside commonly constructed objects
* </ul>
*
* Instead of introucing an Atomic field into a class, a volatile field with an AtomicFieldUpdater is preferred when possible.
* <ul>Types with AtomicFieldUpdaters are...
* <li>int
* <li>long
* <li>reference (e.g. Object types)
* </ul>
*
* While the performance of Atomic is on par with AtomicFieldUpdater (and sometimes slightly better) inside a frequently
* constructed object, the impact of the extra allocation on garbage collection is detrimental to the application as a whole.
*
* <code> Java 17 - MacBook M1 - 8 threads
* Benchmark Mode Cnt Score Error Units
* AtomicsBenchmark.atomicFieldUpdater_construction thrpt 6 4442811623.924 ± 293568588.782 ops/s
* AtomicsBenchmark.atomicFieldUpdater_construction:gc.alloc.rate.norm thrpt 6 16.000 ± 0.001 B/op
*
* AtomicsBenchmark.atomicFieldUpdater_get thrpt 6 1849238178.083 ± 29719143.488 ops/s
* AtomicsBenchmark.atomicFieldUpdater_get:gc.alloc.rate.norm thrpt 6 ≈ 10⁻⁶ B/op
*
* AtomicsBenchmark.atomicFieldUpdater_getVolatile thrpt 6 1814698391.230 ± 54378746.492 ops/s
* AtomicsBenchmark.atomicFieldUpdater_getVolatile:gc.alloc.rate.norm thrpt 6 ≈ 10⁻⁶ B/op
*
* AtomicsBenchmark.atomicFieldUpdater_incrementAndGet thrpt 6 15871753.651 ± 432451.444 ops/s
* AtomicsBenchmark.atomicFieldUpdater_incrementAndGet:gc.alloc.rate.norm thrpt 6 ≈ 10⁻⁴ B/op
*
* AtomicsBenchmark.atomic_construction thrpt 6 2532095943.171 ± 87644803.894 ops/s
* AtomicsBenchmark.atomic_construction:gc.alloc.rate.norm thrpt 6 32.000 ± 0.001 B/op
*
* AtomicsBenchmark.atomic_incrementAndGet thrpt 6 16416635.546 ± 751137.083 ops/s
* AtomicsBenchmark.atomic_incrementAndGet:gc.alloc.rate.norm thrpt 6 ≈ 10⁻⁴ B/op
*
* AtomicsBenchmark.atomic_read thrpt 6 1943297600.062 ± 43802387.143 ops/s
* AtomicsBenchmark.atomic_read:gc.alloc.rate.norm thrpt 6 ≈ 10⁻⁶ B/op
*/
@Fork(2)
@Warmup(iterations = 2)
@Measurement(iterations = 3)
@Threads(8)
public class AtomicsBenchmark {
static int SIZE = 32;
static final class AtomicHolder {
final AtomicInteger atomic;
AtomicHolder(int num) {
this.atomic = new AtomicInteger(num);
}
int get() {
return this.atomic.get();
}
int incrementAndGet() {
return this.atomic.incrementAndGet();
}
}
static final class FieldHolder {
static final AtomicIntegerFieldUpdater<FieldHolder> AFU_FIELD =
AtomicIntegerFieldUpdater.newUpdater(FieldHolder.class, "field");
volatile int field;
FieldHolder(int num) {
this.field = num;
}
int getVolatile() {
return this.field;
}
int get() {
return AFU_FIELD.get(this);
}
int incrementAndGet() {
return AFU_FIELD.incrementAndGet(this);
}
}
static final AtomicHolder[] atomicHolders =
init(
() -> {
AtomicHolder[] holders = new AtomicHolder[SIZE];
for (int i = 0; i < holders.length; ++i) {
holders[i] = new AtomicHolder(i * 2);
}
return holders;
});
static final FieldHolder[] fieldHolders =
init(
() -> {
FieldHolder[] holders = new FieldHolder[SIZE];
for (int i = 0; i < holders.length; ++i) {
holders[i] = new FieldHolder(i * 2);
}
return holders;
});
static final <T> T init(Supplier<T> supplier) {
return supplier.get();
}
static int sharedLookupIndex = 0;
static <T> T next(T[] holders) {
int localIndex = ++sharedLookupIndex;
if (localIndex >= holders.length) {
sharedLookupIndex = localIndex = 0;
}
return holders[localIndex];
}
@Benchmark
public Object atomic_construction() {
return new AtomicHolder(0);
}
@Benchmark
public int atomic_incrementAndGet() {
return next(atomicHolders).incrementAndGet();
}
@Benchmark
public Object atomic_read() {
return next(atomicHolders).get();
}
@Benchmark
public Object atomicFieldUpdater_construction() {
return new FieldHolder(0);
}
@Benchmark
public Object atomicFieldUpdater_getVolatile() {
return next(fieldHolders).getVolatile();
}
@Benchmark
public Object atomicFieldUpdater_get() {
return next(fieldHolders).get();
}
@Benchmark
public int atomicFieldUpdater_incrementAndGet() {
return next(fieldHolders).incrementAndGet();
}
}