list-benchmarks: ArrayList vs LinkedList JMH benchmarks (add/get/iterate/remove) and when ArrayDeque fits instead of either
This commit is contained in:
@@ -20,6 +20,7 @@ article; each module's own README has that article's version table, quickstart,
|
||||
| [`strings`](strings/) | Java Strings Deep Dive: Interning, StringBuilder, Text Blocks, and the Concatenation Benchmark |
|
||||
| [`collections`](collections/) | How to Sort a HashMap by Value (and Key) in Java |
|
||||
| [`nio2`](nio2/) | Java NIO.2 File API: Files, Path, WatchService, and Streaming Large Files Without OOM |
|
||||
| [`list-benchmarks`](list-benchmarks/) | ArrayList vs LinkedList in 2026: JMH Benchmarks and Why LinkedList Rarely Wins |
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
# list-benchmarks
|
||||
|
||||
Companion code for the ankurm.com post *"ArrayList vs LinkedList in 2026: JMH Benchmarks and Why
|
||||
LinkedList Rarely Wins."* Module in `java-core-examples`, the Java-core series.
|
||||
|
||||
## Versions this was built and tested against
|
||||
|
||||
| Component | Version | Notes |
|
||||
|---|---|---|
|
||||
| JDK | 25.0.4.1+1 (Temurin, LTS) | |
|
||||
| JMH | 1.37 | |
|
||||
| JUnit Jupiter | 5.11.0 | Correctness tests only - do not read these as throughput proof. |
|
||||
| Maven | 3.9.11 | |
|
||||
| Hardware | shared multi-tenant container | See the honesty note below - this is not an isolated benchmarking box. |
|
||||
|
||||
## Quickstart
|
||||
|
||||
```bash
|
||||
export JAVA_HOME=/path/to/jdk-25
|
||||
mvn package
|
||||
java -jar target/benchmarks.jar ListOpsBenchmark
|
||||
java -jar target/benchmarks.jar DequeBenchmark
|
||||
java -cp target/classes com.ankurm.listbenchmarks.IteratorRemovalDemo
|
||||
```
|
||||
|
||||
`scripts/run-all.sh` regenerates every file in `output/`. `scripts/run.sh <Class> [jmh-args]` runs
|
||||
one benchmark ad hoc.
|
||||
|
||||
## What's in here
|
||||
|
||||
| File | What it shows |
|
||||
|---|---|
|
||||
| `src/main/java/.../ListOpsBenchmark.java` | `ArrayList` vs `LinkedList`: append, front-insert, middle-insert, random `get`, full iteration. Each mutating benchmark inserts then immediately removes, so list size stays constant across the whole run. |
|
||||
| `src/main/java/.../DequeBenchmark.java` | `ArrayDeque` vs `LinkedList` vs `ArrayList` used as a stack/queue: push/pop at the head, offer/poll at the tail. |
|
||||
| `src/main/java/.../IteratorRemovalDemo.java` | The one case `LinkedList` can win: removing through an `Iterator` cursor instead of by index. Plain `nanoTime()`, not JMH - read as illustrative. |
|
||||
| `src/test/java/.../CorrectnessTest.java` | Sanity checks: both lists agree on contents and ordering; `ArrayDeque` genuinely does not implement `List`. |
|
||||
| `output/01-list-ops-sweep.txt` | Full JMH report for `ListOpsBenchmark`, sizes 1,000 and 100,000. |
|
||||
| `output/02-deque-ops-sweep.txt` | Full JMH report for `DequeBenchmark`, sizes 1,000 and 100,000. |
|
||||
| `output/03-iterator-removal-demo.txt` | Captured run of the iterator-removal illustration. |
|
||||
| `output/04-correctness-test.txt` | JUnit run (4/4 passing). |
|
||||
|
||||
## Reading the numbers honestly (shared container, not an isolated box)
|
||||
|
||||
This run shares CPU with five other concurrent build/benchmark workloads on the same host - JMH
|
||||
was still run one class at a time, back to back, never overlapping with itself, but it cannot see
|
||||
or control what else is scheduled on the machine. Several rows carry an error bar wider than the
|
||||
mean (`DequeBenchmark.offerPollTail ArrayDeque @1000`: 203,926 ± 985,600 ops/ms), which is
|
||||
the signature of scheduler noise, not of the JVM doing something strange. Treat every number here
|
||||
as **directional**, and prefer the **ratios** between rows measured in the same run over the
|
||||
absolute ops/ms.
|
||||
|
||||
With that caveat:
|
||||
|
||||
- **`addFront` is the one operation where `LinkedList` clearly and consistently wins** - flat
|
||||
~43,000 ops/ms at both 1,000 and 100,000 elements, because it is an O(1) pointer write
|
||||
regardless of size. `ArrayList.addFront` falls from ~6,600 ops/ms at 1,000 elements to ~53
|
||||
ops/ms at 100,000 - two orders of magnitude - because every call shifts the whole array by one.
|
||||
- **`addMiddle` favors `ArrayList` by roughly 20-25x at both sizes**, even though both
|
||||
implementations are doing O(n) work to get there. `ArrayList`'s half is a single
|
||||
`System.arraycopy`; `LinkedList`'s is a pointer-chasing walk node by node to find the midpoint,
|
||||
and that walk is the slower half of "O(n) to reach the middle, O(1) to unlink."
|
||||
- **`getRandom` and `iterateSum` both favor `ArrayList`**, the first by ~2,000x at 100,000
|
||||
elements (random access on `LinkedList` is a full traversal per call), the second by ~4-5x even
|
||||
though iteration is O(n) either way - this is the cache-locality gap the post discusses: one
|
||||
contiguous `int[]`-backed array versus following a chain of individually-allocated `Node`
|
||||
objects scattered across the heap.
|
||||
- **For stack/queue-shaped work, `ArrayDeque` is the one to reach for, not `LinkedList`** -
|
||||
`pushPopHead` favors `ArrayDeque` over `LinkedList` by ~3.6x at 1,000 elements, and `ArrayDeque`
|
||||
never degrades to `ArrayList`'s O(n) head-shift problem (`ArrayList.pushPopHead` collapses to 56
|
||||
ops/ms at 100,000 elements, a 1,800x gap from `ArrayDeque`'s 103,406).
|
||||
- **The iterator-removal illustration (`output/03`) is the sharpest number in this repository**:
|
||||
removing every third element of a 200,000-element list through an `Iterator` cursor takes
|
||||
**1,314 ms with `ArrayList` and 5.0 ms with `LinkedList` - a 262x gap** - because
|
||||
`ArrayList.remove(index)` still shifts the tail of the array on every call even when the index
|
||||
came from an iterator already sitting on it, while `LinkedList.Iterator.remove()` unlinks the
|
||||
node the cursor already holds. This is also, honestly, close to the *only* realistic case where
|
||||
reaching for `LinkedList` over `ArrayList` is the right call in 2026 code.
|
||||
|
||||
## License
|
||||
|
||||
MIT - see the [repo-wide LICENSE](../LICENSE).
|
||||
@@ -0,0 +1,576 @@
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 2 iterations, 1 s each
|
||||
# Measurement: 3 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Throughput, ops/time
|
||||
# Benchmark: com.ankurm.listbenchmarks.ListOpsBenchmark.addEnd
|
||||
# Parameters: (impl = ArrayList, size = 1000)
|
||||
|
||||
# Run progress: 0.00% complete, ETA 00:01:40
|
||||
# Fork: 1 of 1
|
||||
# Warmup Iteration 1: 162114.256 ops/ms
|
||||
# Warmup Iteration 2: 163227.532 ops/ms
|
||||
Iteration 1: 174081.389 ops/ms
|
||||
Iteration 2: 181016.166 ops/ms
|
||||
Iteration 3: 178653.294 ops/ms
|
||||
|
||||
|
||||
Result "com.ankurm.listbenchmarks.ListOpsBenchmark.addEnd":
|
||||
177916.950 ±(99.9%) 64318.989 ops/ms [Average]
|
||||
(min, avg, max) = (174081.389, 177916.950, 181016.166), stdev = 3525.541
|
||||
CI (99.9%): [113597.961, 242235.939] (assumes normal distribution)
|
||||
|
||||
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 2 iterations, 1 s each
|
||||
# Measurement: 3 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Throughput, ops/time
|
||||
# Benchmark: com.ankurm.listbenchmarks.ListOpsBenchmark.addEnd
|
||||
# Parameters: (impl = ArrayList, size = 100000)
|
||||
|
||||
# Run progress: 5.00% complete, ETA 00:01:43
|
||||
# Fork: 1 of 1
|
||||
# Warmup Iteration 1: 85814.984 ops/ms
|
||||
# Warmup Iteration 2: 77594.269 ops/ms
|
||||
Iteration 1: 84356.568 ops/ms
|
||||
Iteration 2: 83619.315 ops/ms
|
||||
Iteration 3: 72410.302 ops/ms
|
||||
|
||||
|
||||
Result "com.ankurm.listbenchmarks.ListOpsBenchmark.addEnd":
|
||||
80128.728 ±(99.9%) 122132.789 ops/ms [Average]
|
||||
(min, avg, max) = (72410.302, 80128.728, 84356.568), stdev = 6694.510
|
||||
CI (99.9%): [≈ 0, 202261.517] (assumes normal distribution)
|
||||
|
||||
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 2 iterations, 1 s each
|
||||
# Measurement: 3 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Throughput, ops/time
|
||||
# Benchmark: com.ankurm.listbenchmarks.ListOpsBenchmark.addEnd
|
||||
# Parameters: (impl = LinkedList, size = 1000)
|
||||
|
||||
# Run progress: 10.00% complete, ETA 00:01:37
|
||||
# Fork: 1 of 1
|
||||
# Warmup Iteration 1: 74571.629 ops/ms
|
||||
# Warmup Iteration 2: 42845.531 ops/ms
|
||||
Iteration 1: 44822.469 ops/ms
|
||||
Iteration 2: 46759.673 ops/ms
|
||||
Iteration 3: 47030.988 ops/ms
|
||||
|
||||
|
||||
Result "com.ankurm.listbenchmarks.ListOpsBenchmark.addEnd":
|
||||
46204.377 ±(99.9%) 21973.304 ops/ms [Average]
|
||||
(min, avg, max) = (44822.469, 46204.377, 47030.988), stdev = 1204.431
|
||||
CI (99.9%): [24231.073, 68177.680] (assumes normal distribution)
|
||||
|
||||
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 2 iterations, 1 s each
|
||||
# Measurement: 3 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Throughput, ops/time
|
||||
# Benchmark: com.ankurm.listbenchmarks.ListOpsBenchmark.addEnd
|
||||
# Parameters: (impl = LinkedList, size = 100000)
|
||||
|
||||
# Run progress: 15.00% complete, ETA 00:01:32
|
||||
# Fork: 1 of 1
|
||||
# Warmup Iteration 1: 82182.548 ops/ms
|
||||
# Warmup Iteration 2: 48790.149 ops/ms
|
||||
Iteration 1: 41766.671 ops/ms
|
||||
Iteration 2: 42867.682 ops/ms
|
||||
Iteration 3: 38072.881 ops/ms
|
||||
|
||||
|
||||
Result "com.ankurm.listbenchmarks.ListOpsBenchmark.addEnd":
|
||||
40902.411 ±(99.9%) 45819.499 ops/ms [Average]
|
||||
(min, avg, max) = (38072.881, 40902.411, 42867.682), stdev = 2511.521
|
||||
CI (99.9%): [≈ 0, 86721.910] (assumes normal distribution)
|
||||
|
||||
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 2 iterations, 1 s each
|
||||
# Measurement: 3 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Throughput, ops/time
|
||||
# Benchmark: com.ankurm.listbenchmarks.ListOpsBenchmark.addFront
|
||||
# Parameters: (impl = ArrayList, size = 1000)
|
||||
|
||||
# Run progress: 20.00% complete, ETA 00:01:26
|
||||
# Fork: 1 of 1
|
||||
# Warmup Iteration 1: 5358.049 ops/ms
|
||||
# Warmup Iteration 2: 5705.109 ops/ms
|
||||
Iteration 1: 6860.775 ops/ms
|
||||
Iteration 2: 7014.548 ops/ms
|
||||
Iteration 3: 5879.974 ops/ms
|
||||
|
||||
|
||||
Result "com.ankurm.listbenchmarks.ListOpsBenchmark.addFront":
|
||||
6585.099 ±(99.9%) 11228.597 ops/ms [Average]
|
||||
(min, avg, max) = (5879.974, 6585.099, 7014.548), stdev = 615.477
|
||||
CI (99.9%): [≈ 0, 17813.697] (assumes normal distribution)
|
||||
|
||||
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 2 iterations, 1 s each
|
||||
# Measurement: 3 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Throughput, ops/time
|
||||
# Benchmark: com.ankurm.listbenchmarks.ListOpsBenchmark.addFront
|
||||
# Parameters: (impl = ArrayList, size = 100000)
|
||||
|
||||
# Run progress: 25.00% complete, ETA 00:01:21
|
||||
# Fork: 1 of 1
|
||||
# Warmup Iteration 1: 35.381 ops/ms
|
||||
# Warmup Iteration 2: 45.342 ops/ms
|
||||
Iteration 1: 53.127 ops/ms
|
||||
Iteration 2: 53.514 ops/ms
|
||||
Iteration 3: 53.857 ops/ms
|
||||
|
||||
|
||||
Result "com.ankurm.listbenchmarks.ListOpsBenchmark.addFront":
|
||||
53.499 ±(99.9%) 6.659 ops/ms [Average]
|
||||
(min, avg, max) = (53.127, 53.499, 53.857), stdev = 0.365
|
||||
CI (99.9%): [46.840, 60.158] (assumes normal distribution)
|
||||
|
||||
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 2 iterations, 1 s each
|
||||
# Measurement: 3 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Throughput, ops/time
|
||||
# Benchmark: com.ankurm.listbenchmarks.ListOpsBenchmark.addFront
|
||||
# Parameters: (impl = LinkedList, size = 1000)
|
||||
|
||||
# Run progress: 30.00% complete, ETA 00:01:16
|
||||
# Fork: 1 of 1
|
||||
# Warmup Iteration 1: 79349.690 ops/ms
|
||||
# Warmup Iteration 2: 46393.515 ops/ms
|
||||
Iteration 1: 42718.334 ops/ms
|
||||
Iteration 2: 41957.209 ops/ms
|
||||
Iteration 3: 44017.076 ops/ms
|
||||
|
||||
|
||||
Result "com.ankurm.listbenchmarks.ListOpsBenchmark.addFront":
|
||||
42897.540 ±(99.9%) 19001.947 ops/ms [Average]
|
||||
(min, avg, max) = (41957.209, 42897.540, 44017.076), stdev = 1041.561
|
||||
CI (99.9%): [23895.592, 61899.487] (assumes normal distribution)
|
||||
|
||||
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 2 iterations, 1 s each
|
||||
# Measurement: 3 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Throughput, ops/time
|
||||
# Benchmark: com.ankurm.listbenchmarks.ListOpsBenchmark.addFront
|
||||
# Parameters: (impl = LinkedList, size = 100000)
|
||||
|
||||
# Run progress: 35.00% complete, ETA 00:01:11
|
||||
# Fork: 1 of 1
|
||||
# Warmup Iteration 1: 23976.443 ops/ms
|
||||
# Warmup Iteration 2: 30056.587 ops/ms
|
||||
Iteration 1: 44990.603 ops/ms
|
||||
Iteration 2: 44365.967 ops/ms
|
||||
Iteration 3: 40586.360 ops/ms
|
||||
|
||||
|
||||
Result "com.ankurm.listbenchmarks.ListOpsBenchmark.addFront":
|
||||
43314.310 ±(99.9%) 43475.313 ops/ms [Average]
|
||||
(min, avg, max) = (40586.360, 43314.310, 44990.603), stdev = 2383.029
|
||||
CI (99.9%): [≈ 0, 86789.623] (assumes normal distribution)
|
||||
|
||||
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 2 iterations, 1 s each
|
||||
# Measurement: 3 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Throughput, ops/time
|
||||
# Benchmark: com.ankurm.listbenchmarks.ListOpsBenchmark.addMiddle
|
||||
# Parameters: (impl = ArrayList, size = 1000)
|
||||
|
||||
# Run progress: 40.00% complete, ETA 00:01:05
|
||||
# Fork: 1 of 1
|
||||
# Warmup Iteration 1: 16685.105 ops/ms
|
||||
# Warmup Iteration 2: 15174.527 ops/ms
|
||||
Iteration 1: 15288.973 ops/ms
|
||||
Iteration 2: 13588.771 ops/ms
|
||||
Iteration 3: 14183.387 ops/ms
|
||||
|
||||
|
||||
Result "com.ankurm.listbenchmarks.ListOpsBenchmark.addMiddle":
|
||||
14353.710 ±(99.9%) 15740.742 ops/ms [Average]
|
||||
(min, avg, max) = (13588.771, 14353.710, 15288.973), stdev = 862.803
|
||||
CI (99.9%): [≈ 0, 30094.453] (assumes normal distribution)
|
||||
|
||||
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 2 iterations, 1 s each
|
||||
# Measurement: 3 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Throughput, ops/time
|
||||
# Benchmark: com.ankurm.listbenchmarks.ListOpsBenchmark.addMiddle
|
||||
# Parameters: (impl = ArrayList, size = 100000)
|
||||
|
||||
# Run progress: 45.00% complete, ETA 00:00:59
|
||||
# Fork: 1 of 1
|
||||
# Warmup Iteration 1: 66.502 ops/ms
|
||||
# Warmup Iteration 2: 108.785 ops/ms
|
||||
Iteration 1: 127.539 ops/ms
|
||||
Iteration 2: 124.636 ops/ms
|
||||
Iteration 3: 124.909 ops/ms
|
||||
|
||||
|
||||
Result "com.ankurm.listbenchmarks.ListOpsBenchmark.addMiddle":
|
||||
125.695 ±(99.9%) 29.247 ops/ms [Average]
|
||||
(min, avg, max) = (124.636, 125.695, 127.539), stdev = 1.603
|
||||
CI (99.9%): [96.447, 154.942] (assumes normal distribution)
|
||||
|
||||
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 2 iterations, 1 s each
|
||||
# Measurement: 3 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Throughput, ops/time
|
||||
# Benchmark: com.ankurm.listbenchmarks.ListOpsBenchmark.addMiddle
|
||||
# Parameters: (impl = LinkedList, size = 1000)
|
||||
|
||||
# Run progress: 50.00% complete, ETA 00:00:54
|
||||
# Fork: 1 of 1
|
||||
# Warmup Iteration 1: 536.060 ops/ms
|
||||
# Warmup Iteration 2: 606.294 ops/ms
|
||||
Iteration 1: 605.980 ops/ms
|
||||
Iteration 2: 605.421 ops/ms
|
||||
Iteration 3: 614.996 ops/ms
|
||||
|
||||
|
||||
Result "com.ankurm.listbenchmarks.ListOpsBenchmark.addMiddle":
|
||||
608.799 ±(99.9%) 98.048 ops/ms [Average]
|
||||
(min, avg, max) = (605.421, 608.799, 614.996), stdev = 5.374
|
||||
CI (99.9%): [510.751, 706.847] (assumes normal distribution)
|
||||
|
||||
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 2 iterations, 1 s each
|
||||
# Measurement: 3 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Throughput, ops/time
|
||||
# Benchmark: com.ankurm.listbenchmarks.ListOpsBenchmark.addMiddle
|
||||
# Parameters: (impl = LinkedList, size = 100000)
|
||||
|
||||
# Run progress: 55.00% complete, ETA 00:00:49
|
||||
# Fork: 1 of 1
|
||||
# Warmup Iteration 1: 4.930 ops/ms
|
||||
# Warmup Iteration 2: 4.993 ops/ms
|
||||
Iteration 1: 5.047 ops/ms
|
||||
Iteration 2: 5.149 ops/ms
|
||||
Iteration 3: 5.061 ops/ms
|
||||
|
||||
|
||||
Result "com.ankurm.listbenchmarks.ListOpsBenchmark.addMiddle":
|
||||
5.086 ±(99.9%) 1.014 ops/ms [Average]
|
||||
(min, avg, max) = (5.047, 5.086, 5.149), stdev = 0.056
|
||||
CI (99.9%): [4.072, 6.100] (assumes normal distribution)
|
||||
|
||||
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 2 iterations, 1 s each
|
||||
# Measurement: 3 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Throughput, ops/time
|
||||
# Benchmark: com.ankurm.listbenchmarks.ListOpsBenchmark.getRandom
|
||||
# Parameters: (impl = ArrayList, size = 1000)
|
||||
|
||||
# Run progress: 60.00% complete, ETA 00:00:43
|
||||
# Fork: 1 of 1
|
||||
# Warmup Iteration 1: 72989.593 ops/ms
|
||||
# Warmup Iteration 2: 73568.287 ops/ms
|
||||
Iteration 1: 74557.252 ops/ms
|
||||
Iteration 2: 76233.965 ops/ms
|
||||
Iteration 3: 75917.857 ops/ms
|
||||
|
||||
|
||||
Result "com.ankurm.listbenchmarks.ListOpsBenchmark.getRandom":
|
||||
75569.691 ±(99.9%) 16253.874 ops/ms [Average]
|
||||
(min, avg, max) = (74557.252, 75569.691, 76233.965), stdev = 890.930
|
||||
CI (99.9%): [59315.818, 91823.565] (assumes normal distribution)
|
||||
|
||||
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 2 iterations, 1 s each
|
||||
# Measurement: 3 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Throughput, ops/time
|
||||
# Benchmark: com.ankurm.listbenchmarks.ListOpsBenchmark.getRandom
|
||||
# Parameters: (impl = ArrayList, size = 100000)
|
||||
|
||||
# Run progress: 65.00% complete, ETA 00:00:37
|
||||
# Fork: 1 of 1
|
||||
# Warmup Iteration 1: 39087.453 ops/ms
|
||||
# Warmup Iteration 2: 42258.929 ops/ms
|
||||
Iteration 1: 41705.407 ops/ms
|
||||
Iteration 2: 37824.238 ops/ms
|
||||
Iteration 3: 41843.150 ops/ms
|
||||
|
||||
|
||||
Result "com.ankurm.listbenchmarks.ListOpsBenchmark.getRandom":
|
||||
40457.598 ±(99.9%) 41624.817 ops/ms [Average]
|
||||
(min, avg, max) = (37824.238, 40457.598, 41843.150), stdev = 2281.597
|
||||
CI (99.9%): [≈ 0, 82082.415] (assumes normal distribution)
|
||||
|
||||
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 2 iterations, 1 s each
|
||||
# Measurement: 3 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Throughput, ops/time
|
||||
# Benchmark: com.ankurm.listbenchmarks.ListOpsBenchmark.getRandom
|
||||
# Parameters: (impl = LinkedList, size = 1000)
|
||||
|
||||
# Run progress: 70.00% complete, ETA 00:00:32
|
||||
# Fork: 1 of 1
|
||||
# Warmup Iteration 1: 2573.620 ops/ms
|
||||
# Warmup Iteration 2: 2593.939 ops/ms
|
||||
Iteration 1: 2578.054 ops/ms
|
||||
Iteration 2: 1819.430 ops/ms
|
||||
Iteration 3: 2350.190 ops/ms
|
||||
|
||||
|
||||
Result "com.ankurm.listbenchmarks.ListOpsBenchmark.getRandom":
|
||||
2249.225 ±(99.9%) 7101.544 ops/ms [Average]
|
||||
(min, avg, max) = (1819.430, 2249.225, 2578.054), stdev = 389.260
|
||||
CI (99.9%): [≈ 0, 9350.769] (assumes normal distribution)
|
||||
|
||||
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 2 iterations, 1 s each
|
||||
# Measurement: 3 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Throughput, ops/time
|
||||
# Benchmark: com.ankurm.listbenchmarks.ListOpsBenchmark.getRandom
|
||||
# Parameters: (impl = LinkedList, size = 100000)
|
||||
|
||||
# Run progress: 75.00% complete, ETA 00:00:26
|
||||
# Fork: 1 of 1
|
||||
# Warmup Iteration 1: 19.457 ops/ms
|
||||
# Warmup Iteration 2: 19.681 ops/ms
|
||||
Iteration 1: 19.396 ops/ms
|
||||
Iteration 2: 19.827 ops/ms
|
||||
Iteration 3: 18.270 ops/ms
|
||||
|
||||
|
||||
Result "com.ankurm.listbenchmarks.ListOpsBenchmark.getRandom":
|
||||
19.165 ±(99.9%) 14.662 ops/ms [Average]
|
||||
(min, avg, max) = (18.270, 19.165, 19.827), stdev = 0.804
|
||||
CI (99.9%): [4.503, 33.826] (assumes normal distribution)
|
||||
|
||||
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 2 iterations, 1 s each
|
||||
# Measurement: 3 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Throughput, ops/time
|
||||
# Benchmark: com.ankurm.listbenchmarks.ListOpsBenchmark.iterateSum
|
||||
# Parameters: (impl = ArrayList, size = 1000)
|
||||
|
||||
# Run progress: 80.00% complete, ETA 00:00:21
|
||||
# Fork: 1 of 1
|
||||
# Warmup Iteration 1: 2001.245 ops/ms
|
||||
# Warmup Iteration 2: 1969.018 ops/ms
|
||||
Iteration 1: 1869.923 ops/ms
|
||||
Iteration 2: 1798.606 ops/ms
|
||||
Iteration 3: 1915.867 ops/ms
|
||||
|
||||
|
||||
Result "com.ankurm.listbenchmarks.ListOpsBenchmark.iterateSum":
|
||||
1861.466 ±(99.9%) 1077.953 ops/ms [Average]
|
||||
(min, avg, max) = (1798.606, 1861.466, 1915.867), stdev = 59.086
|
||||
CI (99.9%): [783.513, 2939.418] (assumes normal distribution)
|
||||
|
||||
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 2 iterations, 1 s each
|
||||
# Measurement: 3 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Throughput, ops/time
|
||||
# Benchmark: com.ankurm.listbenchmarks.ListOpsBenchmark.iterateSum
|
||||
# Parameters: (impl = ArrayList, size = 100000)
|
||||
|
||||
# Run progress: 85.00% complete, ETA 00:00:16
|
||||
# Fork: 1 of 1
|
||||
# Warmup Iteration 1: 13.767 ops/ms
|
||||
# Warmup Iteration 2: 14.572 ops/ms
|
||||
Iteration 1: 14.687 ops/ms
|
||||
Iteration 2: 13.892 ops/ms
|
||||
Iteration 3: 12.981 ops/ms
|
||||
|
||||
|
||||
Result "com.ankurm.listbenchmarks.ListOpsBenchmark.iterateSum":
|
||||
13.853 ±(99.9%) 15.571 ops/ms [Average]
|
||||
(min, avg, max) = (12.981, 13.853, 14.687), stdev = 0.854
|
||||
CI (99.9%): [≈ 0, 29.425] (assumes normal distribution)
|
||||
|
||||
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 2 iterations, 1 s each
|
||||
# Measurement: 3 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Throughput, ops/time
|
||||
# Benchmark: com.ankurm.listbenchmarks.ListOpsBenchmark.iterateSum
|
||||
# Parameters: (impl = LinkedList, size = 1000)
|
||||
|
||||
# Run progress: 90.00% complete, ETA 00:00:10
|
||||
# Fork: 1 of 1
|
||||
# Warmup Iteration 1: 515.059 ops/ms
|
||||
# Warmup Iteration 2: 563.552 ops/ms
|
||||
Iteration 1: 535.605 ops/ms
|
||||
Iteration 2: 561.710 ops/ms
|
||||
Iteration 3: 561.907 ops/ms
|
||||
|
||||
|
||||
Result "com.ankurm.listbenchmarks.ListOpsBenchmark.iterateSum":
|
||||
553.074 ±(99.9%) 276.013 ops/ms [Average]
|
||||
(min, avg, max) = (535.605, 553.074, 561.907), stdev = 15.129
|
||||
CI (99.9%): [277.061, 829.088] (assumes normal distribution)
|
||||
|
||||
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 2 iterations, 1 s each
|
||||
# Measurement: 3 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Throughput, ops/time
|
||||
# Benchmark: com.ankurm.listbenchmarks.ListOpsBenchmark.iterateSum
|
||||
# Parameters: (impl = LinkedList, size = 100000)
|
||||
|
||||
# Run progress: 95.00% complete, ETA 00:00:05
|
||||
# Fork: 1 of 1
|
||||
# Warmup Iteration 1: 2.898 ops/ms
|
||||
# Warmup Iteration 2: 2.928 ops/ms
|
||||
Iteration 1: 2.959 ops/ms
|
||||
Iteration 2: 2.940 ops/ms
|
||||
Iteration 3: 2.979 ops/ms
|
||||
|
||||
|
||||
Result "com.ankurm.listbenchmarks.ListOpsBenchmark.iterateSum":
|
||||
2.959 ±(99.9%) 0.351 ops/ms [Average]
|
||||
(min, avg, max) = (2.940, 2.959, 2.979), stdev = 0.019
|
||||
CI (99.9%): [2.608, 3.310] (assumes normal distribution)
|
||||
|
||||
|
||||
# Run complete. Total time: 00:01:47
|
||||
|
||||
REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on
|
||||
why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial
|
||||
experiments, perform baseline and negative tests that provide experimental control, make sure
|
||||
the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts.
|
||||
Do not assume the numbers tell you what you want them to tell.
|
||||
|
||||
NOTE: Current JVM experimentally supports Compiler Blackholes, and they are in use. Please exercise
|
||||
extra caution when trusting the results, look into the generated code to check the benchmark still
|
||||
works, and factor in a small probability of new VM bugs. Additionally, while comparisons between
|
||||
different JVMs are already problematic, the performance difference caused by different Blackhole
|
||||
modes can be very significant. Please make sure you use the consistent Blackhole mode for comparisons.
|
||||
|
||||
Benchmark (impl) (size) Mode Cnt Score Error Units
|
||||
ListOpsBenchmark.addEnd ArrayList 1000 thrpt 3 177916.950 ± 64318.989 ops/ms
|
||||
ListOpsBenchmark.addEnd ArrayList 100000 thrpt 3 80128.728 ± 122132.789 ops/ms
|
||||
ListOpsBenchmark.addEnd LinkedList 1000 thrpt 3 46204.377 ± 21973.304 ops/ms
|
||||
ListOpsBenchmark.addEnd LinkedList 100000 thrpt 3 40902.411 ± 45819.499 ops/ms
|
||||
ListOpsBenchmark.addFront ArrayList 1000 thrpt 3 6585.099 ± 11228.597 ops/ms
|
||||
ListOpsBenchmark.addFront ArrayList 100000 thrpt 3 53.499 ± 6.659 ops/ms
|
||||
ListOpsBenchmark.addFront LinkedList 1000 thrpt 3 42897.540 ± 19001.947 ops/ms
|
||||
ListOpsBenchmark.addFront LinkedList 100000 thrpt 3 43314.310 ± 43475.313 ops/ms
|
||||
ListOpsBenchmark.addMiddle ArrayList 1000 thrpt 3 14353.710 ± 15740.742 ops/ms
|
||||
ListOpsBenchmark.addMiddle ArrayList 100000 thrpt 3 125.695 ± 29.247 ops/ms
|
||||
ListOpsBenchmark.addMiddle LinkedList 1000 thrpt 3 608.799 ± 98.048 ops/ms
|
||||
ListOpsBenchmark.addMiddle LinkedList 100000 thrpt 3 5.086 ± 1.014 ops/ms
|
||||
ListOpsBenchmark.getRandom ArrayList 1000 thrpt 3 75569.691 ± 16253.874 ops/ms
|
||||
ListOpsBenchmark.getRandom ArrayList 100000 thrpt 3 40457.598 ± 41624.817 ops/ms
|
||||
ListOpsBenchmark.getRandom LinkedList 1000 thrpt 3 2249.225 ± 7101.544 ops/ms
|
||||
ListOpsBenchmark.getRandom LinkedList 100000 thrpt 3 19.165 ± 14.662 ops/ms
|
||||
ListOpsBenchmark.iterateSum ArrayList 1000 thrpt 3 1861.466 ± 1077.953 ops/ms
|
||||
ListOpsBenchmark.iterateSum ArrayList 100000 thrpt 3 13.853 ± 15.571 ops/ms
|
||||
ListOpsBenchmark.iterateSum LinkedList 1000 thrpt 3 553.074 ± 276.013 ops/ms
|
||||
ListOpsBenchmark.iterateSum LinkedList 100000 thrpt 3 2.959 ± 0.351 ops/ms
|
||||
|
||||
@@ -0,0 +1,352 @@
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 2 iterations, 1 s each
|
||||
# Measurement: 3 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Throughput, ops/time
|
||||
# Benchmark: com.ankurm.listbenchmarks.DequeBenchmark.offerPollTail
|
||||
# Parameters: (impl = ArrayDeque, size = 1000)
|
||||
|
||||
# Run progress: 0.00% complete, ETA 00:01:00
|
||||
# Fork: 1 of 1
|
||||
# Warmup Iteration 1: 225049.942 ops/ms
|
||||
# Warmup Iteration 2: 216387.082 ops/ms
|
||||
Iteration 1: 260204.948 ops/ms
|
||||
Iteration 2: 152481.625 ops/ms
|
||||
Iteration 3: 199092.571 ops/ms
|
||||
|
||||
|
||||
Result "com.ankurm.listbenchmarks.DequeBenchmark.offerPollTail":
|
||||
203926.381 ±(99.9%) 985600.610 ops/ms [Average]
|
||||
(min, avg, max) = (152481.625, 203926.381, 260204.948), stdev = 54024.095
|
||||
CI (99.9%): [≈ 0, 1189526.991] (assumes normal distribution)
|
||||
|
||||
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 2 iterations, 1 s each
|
||||
# Measurement: 3 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Throughput, ops/time
|
||||
# Benchmark: com.ankurm.listbenchmarks.DequeBenchmark.offerPollTail
|
||||
# Parameters: (impl = ArrayDeque, size = 100000)
|
||||
|
||||
# Run progress: 8.33% complete, ETA 00:00:58
|
||||
# Fork: 1 of 1
|
||||
# Warmup Iteration 1: 73559.495 ops/ms
|
||||
# Warmup Iteration 2: 88126.623 ops/ms
|
||||
Iteration 1: 82011.079 ops/ms
|
||||
Iteration 2: 84538.326 ops/ms
|
||||
Iteration 3: 60837.052 ops/ms
|
||||
|
||||
|
||||
Result "com.ankurm.listbenchmarks.DequeBenchmark.offerPollTail":
|
||||
75795.485 ±(99.9%) 237457.872 ops/ms [Average]
|
||||
(min, avg, max) = (60837.052, 75795.485, 84538.326), stdev = 13015.867
|
||||
CI (99.9%): [≈ 0, 313253.357] (assumes normal distribution)
|
||||
|
||||
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 2 iterations, 1 s each
|
||||
# Measurement: 3 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Throughput, ops/time
|
||||
# Benchmark: com.ankurm.listbenchmarks.DequeBenchmark.offerPollTail
|
||||
# Parameters: (impl = LinkedList, size = 1000)
|
||||
|
||||
# Run progress: 16.67% complete, ETA 00:00:54
|
||||
# Fork: 1 of 1
|
||||
# Warmup Iteration 1: 82032.730 ops/ms
|
||||
# Warmup Iteration 2: 80584.591 ops/ms
|
||||
Iteration 1: 79301.950 ops/ms
|
||||
Iteration 2: 88128.376 ops/ms
|
||||
Iteration 3: 87233.185 ops/ms
|
||||
|
||||
|
||||
Result "com.ankurm.listbenchmarks.DequeBenchmark.offerPollTail":
|
||||
84887.837 ±(99.9%) 88631.345 ops/ms [Average]
|
||||
(min, avg, max) = (79301.950, 84887.837, 88128.376), stdev = 4858.183
|
||||
CI (99.9%): [≈ 0, 173519.182] (assumes normal distribution)
|
||||
|
||||
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 2 iterations, 1 s each
|
||||
# Measurement: 3 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Throughput, ops/time
|
||||
# Benchmark: com.ankurm.listbenchmarks.DequeBenchmark.offerPollTail
|
||||
# Parameters: (impl = LinkedList, size = 100000)
|
||||
|
||||
# Run progress: 25.00% complete, ETA 00:00:49
|
||||
# Fork: 1 of 1
|
||||
# Warmup Iteration 1: 102529.616 ops/ms
|
||||
# Warmup Iteration 2: 75609.806 ops/ms
|
||||
Iteration 1: 86058.866 ops/ms
|
||||
Iteration 2: 85350.743 ops/ms
|
||||
Iteration 3: 85011.734 ops/ms
|
||||
|
||||
|
||||
Result "com.ankurm.listbenchmarks.DequeBenchmark.offerPollTail":
|
||||
85473.781 ±(99.9%) 9747.596 ops/ms [Average]
|
||||
(min, avg, max) = (85011.734, 85473.781, 86058.866), stdev = 534.299
|
||||
CI (99.9%): [75726.185, 95221.376] (assumes normal distribution)
|
||||
|
||||
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 2 iterations, 1 s each
|
||||
# Measurement: 3 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Throughput, ops/time
|
||||
# Benchmark: com.ankurm.listbenchmarks.DequeBenchmark.offerPollTail
|
||||
# Parameters: (impl = ArrayList, size = 1000)
|
||||
|
||||
# Run progress: 33.33% complete, ETA 00:00:43
|
||||
# Fork: 1 of 1
|
||||
# Warmup Iteration 1: 212378.579 ops/ms
|
||||
# Warmup Iteration 2: 218438.486 ops/ms
|
||||
Iteration 1: 245793.910 ops/ms
|
||||
Iteration 2: 245870.397 ops/ms
|
||||
Iteration 3: 242681.893 ops/ms
|
||||
|
||||
|
||||
Result "com.ankurm.listbenchmarks.DequeBenchmark.offerPollTail":
|
||||
244782.067 ±(99.9%) 33189.080 ops/ms [Average]
|
||||
(min, avg, max) = (242681.893, 244782.067, 245870.397), stdev = 1819.205
|
||||
CI (99.9%): [211592.987, 277971.147] (assumes normal distribution)
|
||||
|
||||
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 2 iterations, 1 s each
|
||||
# Measurement: 3 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Throughput, ops/time
|
||||
# Benchmark: com.ankurm.listbenchmarks.DequeBenchmark.offerPollTail
|
||||
# Parameters: (impl = ArrayList, size = 100000)
|
||||
|
||||
# Run progress: 41.67% complete, ETA 00:00:37
|
||||
# Fork: 1 of 1
|
||||
# Warmup Iteration 1: 94135.159 ops/ms
|
||||
# Warmup Iteration 2: 95652.428 ops/ms
|
||||
Iteration 1: 98077.379 ops/ms
|
||||
Iteration 2: 100976.549 ops/ms
|
||||
Iteration 3: 98379.804 ops/ms
|
||||
|
||||
|
||||
Result "com.ankurm.listbenchmarks.DequeBenchmark.offerPollTail":
|
||||
99144.577 ±(99.9%) 29075.456 ops/ms [Average]
|
||||
(min, avg, max) = (98077.379, 99144.577, 100976.549), stdev = 1593.724
|
||||
CI (99.9%): [70069.122, 128220.033] (assumes normal distribution)
|
||||
|
||||
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 2 iterations, 1 s each
|
||||
# Measurement: 3 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Throughput, ops/time
|
||||
# Benchmark: com.ankurm.listbenchmarks.DequeBenchmark.pushPopHead
|
||||
# Parameters: (impl = ArrayDeque, size = 1000)
|
||||
|
||||
# Run progress: 50.00% complete, ETA 00:00:32
|
||||
# Fork: 1 of 1
|
||||
# Warmup Iteration 1: 298481.803 ops/ms
|
||||
# Warmup Iteration 2: 312401.613 ops/ms
|
||||
Iteration 1: 299087.963 ops/ms
|
||||
Iteration 2: 298466.157 ops/ms
|
||||
Iteration 3: 299175.622 ops/ms
|
||||
|
||||
|
||||
Result "com.ankurm.listbenchmarks.DequeBenchmark.pushPopHead":
|
||||
298909.914 ±(99.9%) 7056.600 ops/ms [Average]
|
||||
(min, avg, max) = (298466.157, 298909.914, 299175.622), stdev = 386.796
|
||||
CI (99.9%): [291853.313, 305966.514] (assumes normal distribution)
|
||||
|
||||
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 2 iterations, 1 s each
|
||||
# Measurement: 3 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Throughput, ops/time
|
||||
# Benchmark: com.ankurm.listbenchmarks.DequeBenchmark.pushPopHead
|
||||
# Parameters: (impl = ArrayDeque, size = 100000)
|
||||
|
||||
# Run progress: 58.33% complete, ETA 00:00:26
|
||||
# Fork: 1 of 1
|
||||
# Warmup Iteration 1: 106969.537 ops/ms
|
||||
# Warmup Iteration 2: 90907.103 ops/ms
|
||||
Iteration 1: 104740.942 ops/ms
|
||||
Iteration 2: 105037.254 ops/ms
|
||||
Iteration 3: 100438.523 ops/ms
|
||||
|
||||
|
||||
Result "com.ankurm.listbenchmarks.DequeBenchmark.pushPopHead":
|
||||
103405.573 ±(99.9%) 46955.844 ops/ms [Average]
|
||||
(min, avg, max) = (100438.523, 103405.573, 105037.254), stdev = 2573.808
|
||||
CI (99.9%): [56449.730, 150361.417] (assumes normal distribution)
|
||||
|
||||
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 2 iterations, 1 s each
|
||||
# Measurement: 3 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Throughput, ops/time
|
||||
# Benchmark: com.ankurm.listbenchmarks.DequeBenchmark.pushPopHead
|
||||
# Parameters: (impl = LinkedList, size = 1000)
|
||||
|
||||
# Run progress: 66.67% complete, ETA 00:00:21
|
||||
# Fork: 1 of 1
|
||||
# Warmup Iteration 1: 108757.170 ops/ms
|
||||
# Warmup Iteration 2: 76136.263 ops/ms
|
||||
Iteration 1: 79754.897 ops/ms
|
||||
Iteration 2: 87449.217 ops/ms
|
||||
Iteration 3: 83960.921 ops/ms
|
||||
|
||||
|
||||
Result "com.ankurm.listbenchmarks.DequeBenchmark.pushPopHead":
|
||||
83721.678 ±(99.9%) 70288.226 ops/ms [Average]
|
||||
(min, avg, max) = (79754.897, 83721.678, 87449.217), stdev = 3852.735
|
||||
CI (99.9%): [13433.452, 154009.905] (assumes normal distribution)
|
||||
|
||||
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 2 iterations, 1 s each
|
||||
# Measurement: 3 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Throughput, ops/time
|
||||
# Benchmark: com.ankurm.listbenchmarks.DequeBenchmark.pushPopHead
|
||||
# Parameters: (impl = LinkedList, size = 100000)
|
||||
|
||||
# Run progress: 75.00% complete, ETA 00:00:16
|
||||
# Fork: 1 of 1
|
||||
# Warmup Iteration 1: 110204.142 ops/ms
|
||||
# Warmup Iteration 2: 74155.001 ops/ms
|
||||
Iteration 1: 86320.175 ops/ms
|
||||
Iteration 2: 83052.660 ops/ms
|
||||
Iteration 3: 84954.624 ops/ms
|
||||
|
||||
|
||||
Result "com.ankurm.listbenchmarks.DequeBenchmark.pushPopHead":
|
||||
84775.820 ±(99.9%) 29939.400 ops/ms [Average]
|
||||
(min, avg, max) = (83052.660, 84775.820, 86320.175), stdev = 1641.080
|
||||
CI (99.9%): [54836.420, 114715.220] (assumes normal distribution)
|
||||
|
||||
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 2 iterations, 1 s each
|
||||
# Measurement: 3 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Throughput, ops/time
|
||||
# Benchmark: com.ankurm.listbenchmarks.DequeBenchmark.pushPopHead
|
||||
# Parameters: (impl = ArrayList, size = 1000)
|
||||
|
||||
# Run progress: 83.33% complete, ETA 00:00:10
|
||||
# Fork: 1 of 1
|
||||
# Warmup Iteration 1: 11212.051 ops/ms
|
||||
# Warmup Iteration 2: 11024.015 ops/ms
|
||||
Iteration 1: 11274.928 ops/ms
|
||||
Iteration 2: 11150.226 ops/ms
|
||||
Iteration 3: 10960.108 ops/ms
|
||||
|
||||
|
||||
Result "com.ankurm.listbenchmarks.DequeBenchmark.pushPopHead":
|
||||
11128.421 ±(99.9%) 2892.341 ops/ms [Average]
|
||||
(min, avg, max) = (10960.108, 11128.421, 11274.928), stdev = 158.539
|
||||
CI (99.9%): [8236.080, 14020.762] (assumes normal distribution)
|
||||
|
||||
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 2 iterations, 1 s each
|
||||
# Measurement: 3 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Throughput, ops/time
|
||||
# Benchmark: com.ankurm.listbenchmarks.DequeBenchmark.pushPopHead
|
||||
# Parameters: (impl = ArrayList, size = 100000)
|
||||
|
||||
# Run progress: 91.67% complete, ETA 00:00:05
|
||||
# Fork: 1 of 1
|
||||
# Warmup Iteration 1: 42.851 ops/ms
|
||||
# Warmup Iteration 2: 57.144 ops/ms
|
||||
Iteration 1: 54.588 ops/ms
|
||||
Iteration 2: 57.561 ops/ms
|
||||
Iteration 3: 55.350 ops/ms
|
||||
|
||||
|
||||
Result "com.ankurm.listbenchmarks.DequeBenchmark.pushPopHead":
|
||||
55.833 ±(99.9%) 28.174 ops/ms [Average]
|
||||
(min, avg, max) = (54.588, 55.833, 57.561), stdev = 1.544
|
||||
CI (99.9%): [27.658, 84.007] (assumes normal distribution)
|
||||
|
||||
|
||||
# Run complete. Total time: 00:01:04
|
||||
|
||||
REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on
|
||||
why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial
|
||||
experiments, perform baseline and negative tests that provide experimental control, make sure
|
||||
the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts.
|
||||
Do not assume the numbers tell you what you want them to tell.
|
||||
|
||||
NOTE: Current JVM experimentally supports Compiler Blackholes, and they are in use. Please exercise
|
||||
extra caution when trusting the results, look into the generated code to check the benchmark still
|
||||
works, and factor in a small probability of new VM bugs. Additionally, while comparisons between
|
||||
different JVMs are already problematic, the performance difference caused by different Blackhole
|
||||
modes can be very significant. Please make sure you use the consistent Blackhole mode for comparisons.
|
||||
|
||||
Benchmark (impl) (size) Mode Cnt Score Error Units
|
||||
DequeBenchmark.offerPollTail ArrayDeque 1000 thrpt 3 203926.381 ± 985600.610 ops/ms
|
||||
DequeBenchmark.offerPollTail ArrayDeque 100000 thrpt 3 75795.485 ± 237457.872 ops/ms
|
||||
DequeBenchmark.offerPollTail LinkedList 1000 thrpt 3 84887.837 ± 88631.345 ops/ms
|
||||
DequeBenchmark.offerPollTail LinkedList 100000 thrpt 3 85473.781 ± 9747.596 ops/ms
|
||||
DequeBenchmark.offerPollTail ArrayList 1000 thrpt 3 244782.067 ± 33189.080 ops/ms
|
||||
DequeBenchmark.offerPollTail ArrayList 100000 thrpt 3 99144.577 ± 29075.456 ops/ms
|
||||
DequeBenchmark.pushPopHead ArrayDeque 1000 thrpt 3 298909.914 ± 7056.600 ops/ms
|
||||
DequeBenchmark.pushPopHead ArrayDeque 100000 thrpt 3 103405.573 ± 46955.844 ops/ms
|
||||
DequeBenchmark.pushPopHead LinkedList 1000 thrpt 3 83721.678 ± 70288.226 ops/ms
|
||||
DequeBenchmark.pushPopHead LinkedList 100000 thrpt 3 84775.820 ± 29939.400 ops/ms
|
||||
DequeBenchmark.pushPopHead ArrayList 1000 thrpt 3 11128.421 ± 2892.341 ops/ms
|
||||
DequeBenchmark.pushPopHead ArrayList 100000 thrpt 3 55.833 ± 28.174 ops/ms
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
Removing every 3rd element of a 200000-element list via Iterator.remove()
|
||||
|
||||
ArrayList (iterator.remove, every 3rd): 1,314,187,777 ns (1314.19 ms)
|
||||
LinkedList (iterator.remove, every 3rd): 5,019,906 ns (5.02 ms)
|
||||
Ratio (ArrayList time / LinkedList time): 261.80x
|
||||
|
||||
ArrayList.remove(index) still shifts every element after the removed one,
|
||||
even though the iterator was already positioned on it. LinkedList's
|
||||
iterator.remove() unlinks the node the cursor holds - no shift, no search.
|
||||
This is the one removal pattern where LinkedList is the right call.
|
||||
@@ -0,0 +1,4 @@
|
||||
-------------------------------------------------------------------------------
|
||||
Test set: com.ankurm.listbenchmarks.CorrectnessTest
|
||||
-------------------------------------------------------------------------------
|
||||
Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.259 s -- in com.ankurm.listbenchmarks.CorrectnessTest
|
||||
@@ -0,0 +1,83 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>com.ankurm</groupId>
|
||||
<artifactId>java-core-examples</artifactId>
|
||||
<version>1.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>list-benchmarks</artifactId>
|
||||
<name>list-benchmarks</name>
|
||||
<description>ArrayList vs LinkedList vs ArrayDeque: JMH throughput for add/get/iterate/remove, and why LinkedList rarely wins.</description>
|
||||
|
||||
<properties>
|
||||
<jmh.version>1.37</jmh.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.openjdk.jmh</groupId>
|
||||
<artifactId>jmh-core</artifactId>
|
||||
<version>${jmh.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openjdk.jmh</groupId>
|
||||
<artifactId>jmh-generator-annprocess</artifactId>
|
||||
<version>${jmh.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<version>5.11.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>benchmarks</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.13.0</version>
|
||||
<configuration>
|
||||
<release>25</release>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>org.openjdk.jmh</groupId>
|
||||
<artifactId>jmh-generator-annprocess</artifactId>
|
||||
<version>${jmh.version}</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>3.2.5</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.5.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals><goal>shade</goal></goals>
|
||||
<configuration>
|
||||
<transformers>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<mainClass>org.openjdk.jmh.Main</mainClass>
|
||||
</transformer>
|
||||
</transformers>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
Executable
+23
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env bash
|
||||
# Regenerates every file in output/. Run from the list-benchmarks module directory.
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
mvn -q -B package
|
||||
|
||||
java -jar target/benchmarks.jar ListOpsBenchmark 2>&1 \
|
||||
| grep -vE 'JAVA_TOOL_OPTIONS|Djavax\.net\.ssl|^WARNING|^Benchmark result is saved to' \
|
||||
> output/01-list-ops-sweep.txt
|
||||
|
||||
java -jar target/benchmarks.jar DequeBenchmark 2>&1 \
|
||||
| grep -vE 'JAVA_TOOL_OPTIONS|Djavax\.net\.ssl|^WARNING|^Benchmark result is saved to' \
|
||||
> output/02-deque-ops-sweep.txt
|
||||
|
||||
java -cp target/classes com.ankurm.listbenchmarks.IteratorRemovalDemo 2>&1 \
|
||||
| grep -vE 'JAVA_TOOL_OPTIONS|Djavax\.net\.ssl|^WARNING' \
|
||||
> output/03-iterator-removal-demo.txt
|
||||
|
||||
mvn -q -B test
|
||||
cp target/surefire-reports/com.ankurm.listbenchmarks.CorrectnessTest.txt output/04-correctness-test.txt
|
||||
|
||||
echo "Regenerated output/01-04."
|
||||
Executable
+6
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
# Run one JMH benchmark class ad hoc, e.g.: ./scripts/run.sh ListOpsBenchmark -t 1
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
mvn -q -B package -DskipTests
|
||||
java -jar target/benchmarks.jar "$@"
|
||||
@@ -0,0 +1,84 @@
|
||||
package com.ankurm.listbenchmarks;
|
||||
|
||||
import org.openjdk.jmh.annotations.*;
|
||||
import org.openjdk.jmh.infra.Blackhole;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Deque;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Stack/queue-shaped workload: push a value in, pop it back out, repeatedly, on a deque that
|
||||
* already holds {@code size} elements. This is the workload the article's "when ArrayDeque fits
|
||||
* instead of either" section is about - it is neither "random access" (ArrayList's strength) nor
|
||||
* "insert given a list reference" (the only thing LinkedList is actually good at), it is the far
|
||||
* more common "push/pop/offer/poll from one or both ends" shape.
|
||||
*
|
||||
* <p>{@code ArrayDeque} is not a {@link List} - it deliberately does not implement
|
||||
* {@code get(index)} - which is itself part of the article's point: reach for it when you need a
|
||||
* stack or a queue, not a list, and the API stops you from doing index math you did not mean to
|
||||
* do.
|
||||
*
|
||||
* <p>See {@code output/02-deque-ops-sweep.txt} for the captured run.
|
||||
*/
|
||||
@BenchmarkMode(Mode.Throughput)
|
||||
@OutputTimeUnit(TimeUnit.MILLISECONDS)
|
||||
@State(Scope.Thread)
|
||||
@Warmup(iterations = 2, time = 1)
|
||||
@Measurement(iterations = 3, time = 1)
|
||||
@Fork(1)
|
||||
public class DequeBenchmark {
|
||||
|
||||
@Param({"ArrayDeque", "LinkedList", "ArrayList"})
|
||||
public String impl;
|
||||
|
||||
@Param({"1000", "100000"})
|
||||
public int size;
|
||||
|
||||
private Deque<Integer> deque; // used for ArrayDeque and LinkedList
|
||||
private List<Integer> arrayListAsStack; // used only for the ArrayList "anti-pattern" arm
|
||||
|
||||
@Setup(Level.Trial)
|
||||
public void setup() {
|
||||
switch (impl) {
|
||||
case "ArrayDeque" -> deque = new ArrayDeque<>(size);
|
||||
case "LinkedList" -> deque = new LinkedList<>();
|
||||
case "ArrayList" -> arrayListAsStack = new ArrayList<>(size);
|
||||
default -> throw new IllegalStateException(impl);
|
||||
}
|
||||
if ("ArrayList".equals(impl)) {
|
||||
for (int i = 0; i < size; i++) arrayListAsStack.add(i);
|
||||
} else {
|
||||
for (int i = 0; i < size; i++) deque.addLast(i);
|
||||
}
|
||||
}
|
||||
|
||||
/** push then pop at the head - the natural operation for a real Deque. */
|
||||
@Benchmark
|
||||
public void pushPopHead(Blackhole bh) {
|
||||
if (arrayListAsStack != null) {
|
||||
// The only way to get "push/pop at the front" semantics out of a List is
|
||||
// add(0, x) / remove(0) - an O(n) shift on every single call. That cost IS the point.
|
||||
arrayListAsStack.add(0, -1);
|
||||
bh.consume(arrayListAsStack.remove(0));
|
||||
} else {
|
||||
deque.addFirst(-1);
|
||||
bh.consume(deque.pollFirst());
|
||||
}
|
||||
}
|
||||
|
||||
/** offer then poll at the tail - the natural operation for a real Queue. */
|
||||
@Benchmark
|
||||
public void offerPollTail(Blackhole bh) {
|
||||
if (arrayListAsStack != null) {
|
||||
arrayListAsStack.add(-1);
|
||||
bh.consume(arrayListAsStack.remove(arrayListAsStack.size() - 1));
|
||||
} else {
|
||||
deque.addLast(-1);
|
||||
bh.consume(deque.pollLast());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.ankurm.listbenchmarks;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The one shape of removal where {@code LinkedList} can actually win: removing through an
|
||||
* {@link Iterator} cursor you are already standing on, instead of by index.
|
||||
*
|
||||
* <p>{@code ArrayList.remove(index)} is always an array shift, even when the index came from an
|
||||
* iterator that was already sitting on the element - the iterator does not save it from the
|
||||
* shift. {@code LinkedList}'s {@code Iterator.remove()} unlinks the current node directly, no
|
||||
* traversal needed, because the cursor already holds the node reference.
|
||||
*
|
||||
* <p>This is a plain wall-clock illustration with {@code System.nanoTime()}, not a JMH
|
||||
* benchmark - JIT warmup and GC are not controlled for the way {@link ListOpsBenchmark} controls
|
||||
* for them. Read it as "yes, this case is real", not as a throughput number to quote. The JMH
|
||||
* numbers for the general case are in {@code output/01-list-ops-sweep.txt} and
|
||||
* {@code output/02-deque-ops-sweep.txt}.
|
||||
*/
|
||||
public final class IteratorRemovalDemo {
|
||||
|
||||
private IteratorRemovalDemo() {}
|
||||
|
||||
public static void main(String[] args) {
|
||||
int size = 200_000;
|
||||
System.out.println("Removing every 3rd element of a " + size + "-element list via Iterator.remove()");
|
||||
System.out.println();
|
||||
|
||||
long arrayListNanos = removeEveryThird(buildArrayList(size));
|
||||
long linkedListNanos = removeEveryThird(buildLinkedList(size));
|
||||
|
||||
System.out.printf("ArrayList (iterator.remove, every 3rd): %,d ns (%.2f ms)%n",
|
||||
arrayListNanos, arrayListNanos / 1_000_000.0);
|
||||
System.out.printf("LinkedList (iterator.remove, every 3rd): %,d ns (%.2f ms)%n",
|
||||
linkedListNanos, linkedListNanos / 1_000_000.0);
|
||||
System.out.printf("Ratio (ArrayList time / LinkedList time): %.2fx%n",
|
||||
(double) arrayListNanos / linkedListNanos);
|
||||
System.out.println();
|
||||
System.out.println("ArrayList.remove(index) still shifts every element after the removed one,");
|
||||
System.out.println("even though the iterator was already positioned on it. LinkedList's");
|
||||
System.out.println("iterator.remove() unlinks the node the cursor holds - no shift, no search.");
|
||||
System.out.println("This is the one removal pattern where LinkedList is the right call.");
|
||||
}
|
||||
|
||||
private static long removeEveryThird(List<Integer> list) {
|
||||
long start = System.nanoTime();
|
||||
int i = 0;
|
||||
Iterator<Integer> it = list.iterator();
|
||||
while (it.hasNext()) {
|
||||
it.next();
|
||||
if (i % 3 == 0) {
|
||||
it.remove();
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return System.nanoTime() - start;
|
||||
}
|
||||
|
||||
private static List<Integer> buildArrayList(int size) {
|
||||
List<Integer> l = new ArrayList<>(size);
|
||||
for (int i = 0; i < size; i++) l.add(i);
|
||||
return l;
|
||||
}
|
||||
|
||||
private static List<Integer> buildLinkedList(int size) {
|
||||
List<Integer> l = new LinkedList<>();
|
||||
for (int i = 0; i < size; i++) l.add(i);
|
||||
return l;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package com.ankurm.listbenchmarks;
|
||||
|
||||
import org.openjdk.jmh.annotations.*;
|
||||
import org.openjdk.jmh.infra.Blackhole;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Throughput comparison of {@link ArrayList} and {@link LinkedList} on the five operations
|
||||
* that actually show up in real code: append, random-index read, full iteration, and
|
||||
* insert/remove at the front and in the middle.
|
||||
*
|
||||
* <p>Every mutating benchmark does an insert immediately followed by the matching removal so
|
||||
* the list's size (and therefore the cost profile) stays constant across the whole measurement
|
||||
* window instead of drifting as the iteration runs. That is what keeps {@code addFront} and
|
||||
* {@code addFront} comparable at iteration 1 and iteration 1,000,000 of the same run.
|
||||
*
|
||||
* <p>Referenced from the "ArrayList vs LinkedList in 2026" post - see
|
||||
* {@code output/01-list-ops-sweep.txt} for the captured run this repository ships.
|
||||
*/
|
||||
@BenchmarkMode(Mode.Throughput)
|
||||
@OutputTimeUnit(TimeUnit.MILLISECONDS)
|
||||
@State(Scope.Thread)
|
||||
@Warmup(iterations = 2, time = 1)
|
||||
@Measurement(iterations = 3, time = 1)
|
||||
@Fork(1)
|
||||
public class ListOpsBenchmark {
|
||||
|
||||
@Param({"ArrayList", "LinkedList"})
|
||||
public String impl;
|
||||
|
||||
@Param({"1000", "100000"})
|
||||
public int size;
|
||||
|
||||
private List<Integer> list;
|
||||
private final Random rnd = new Random(42);
|
||||
|
||||
@Setup(Level.Trial)
|
||||
public void setup() {
|
||||
list = "ArrayList".equals(impl) ? new ArrayList<>(size) : new LinkedList<>();
|
||||
for (int i = 0; i < size; i++) {
|
||||
list.add(i);
|
||||
}
|
||||
}
|
||||
|
||||
/** Append one element at the end, then remove it. Net size unchanged. */
|
||||
@Benchmark
|
||||
public void addEnd(Blackhole bh) {
|
||||
list.add(size, -1);
|
||||
bh.consume(list.remove(size));
|
||||
}
|
||||
|
||||
/** Insert at index 0, then remove index 0. The operation ArrayList is worst at. */
|
||||
@Benchmark
|
||||
public void addFront(Blackhole bh) {
|
||||
list.add(0, -1);
|
||||
bh.consume(list.remove(0));
|
||||
}
|
||||
|
||||
/** Insert at the midpoint, then remove it again. */
|
||||
@Benchmark
|
||||
public void addMiddle(Blackhole bh) {
|
||||
int mid = size / 2;
|
||||
list.add(mid, -1);
|
||||
bh.consume(list.remove(mid));
|
||||
}
|
||||
|
||||
/** Random-access read by index - no mutation, size never changes. */
|
||||
@Benchmark
|
||||
public int getRandom() {
|
||||
return list.get(rnd.nextInt(size));
|
||||
}
|
||||
|
||||
/** Full forward iteration summing every element, via the iterator (the idiomatic way). */
|
||||
@Benchmark
|
||||
public long iterateSum() {
|
||||
long sum = 0;
|
||||
for (int v : list) {
|
||||
sum += v;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.ankurm.listbenchmarks;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Deque;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
/**
|
||||
* Sanity checks only - these do not prove throughput, they prove the two data structures stay
|
||||
* behaviourally interchangeable through {@link List} so the benchmark's "same workload, two
|
||||
* implementations" framing is actually true, and pin the one behavioural difference the article
|
||||
* leans on: {@code ArrayDeque} is not a {@link List}.
|
||||
*/
|
||||
class CorrectnessTest {
|
||||
|
||||
@Test
|
||||
void arrayListAndLinkedListAgreeOnContents() {
|
||||
List<Integer> a = new ArrayList<>();
|
||||
List<Integer> l = new LinkedList<>();
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
a.add(i);
|
||||
l.add(i);
|
||||
}
|
||||
assertEquals(a, l, "List.equals is content-based, not implementation-based");
|
||||
assertEquals(a.get(500), l.get(500));
|
||||
}
|
||||
|
||||
@Test
|
||||
void addFrontShiftsArrayListButNotLinkedList() {
|
||||
List<Integer> a = new ArrayList<>(List.of(1, 2, 3));
|
||||
List<Integer> l = new LinkedList<>(List.of(1, 2, 3));
|
||||
a.add(0, 0);
|
||||
l.add(0, 0);
|
||||
assertEquals(List.of(0, 1, 2, 3), a);
|
||||
assertEquals(List.of(0, 1, 2, 3), l);
|
||||
// Both produce the same *result* - the benchmark exists because the *cost* differs.
|
||||
}
|
||||
|
||||
@Test
|
||||
void arrayDequeIsNotAList() {
|
||||
Deque<Integer> d = new ArrayDeque<>();
|
||||
assertThrows(ClassCastException.class, () -> {
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
List<Integer> asList = (List) d; // compiles only via raw type + unchecked cast
|
||||
asList.get(0);
|
||||
}, "ArrayDeque deliberately does not implement List - there is no get(index)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void arrayDequeAndLinkedListAgreeOnStackOrder() {
|
||||
Deque<Integer> ad = new ArrayDeque<>();
|
||||
Deque<Integer> ll = new LinkedList<>();
|
||||
for (int v : new int[] {1, 2, 3}) {
|
||||
ad.push(v);
|
||||
ll.push(v);
|
||||
}
|
||||
// Both are LIFO via push/pop - same observable order, different internal layout.
|
||||
assertEquals(3, ad.pop());
|
||||
assertEquals(3, ll.pop());
|
||||
assertEquals(2, ad.pop());
|
||||
assertEquals(2, ll.pop());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user