Skip to content

Commit 0a06214

Browse files
committed
docs(post): expand benchmark scenarios and localize citations
1 parent 96e098c commit 0a06214

1 file changed

Lines changed: 45 additions & 23 deletions

File tree

  • src/content/posts/2026-02-25-arraylist-vs-linkedlist-cache-locality

src/content/posts/2026-02-25-arraylist-vs-linkedlist-cache-locality/index.mdx

Lines changed: 45 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ Big-O는 연산 횟수의 증가율을 설명합니다. 하지만 실제 실행
4646
`ArrayList`는 내부적으로 `Object[]` 배열을 사용합니다.[^arraylist-javadoc]
4747
반면 `LinkedList`는 노드(prev/item/next)가 연결된 구조입니다.[^linkedlist-javadoc]
4848

49+
[^arraylist-javadoc]: ArrayList Javadoc, Oracle Java SE 25 — https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/ArrayList.html
50+
[^linkedlist-javadoc]: LinkedList Javadoc, Oracle Java SE 25 — https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/LinkedList.html
51+
4952
```
5053
ArrayList (contiguous references)
5154
[ref][ref][ref][ref][ref]...
@@ -78,6 +81,8 @@ CPU는 메모리를 바이트 단위가 아니라 **cache line 단위**로 가
7881

7982
Oracle dev.java의 공식 비교 자료도 이 점을 명확히 설명합니다.[^devjava-al-vs-ll]
8083

84+
[^devjava-al-vs-ll]: ArrayList vs LinkedList, dev.java (Oracle) — https://dev.java/learn/api/collections-framework/arraylist-vs-linkedlist/
85+
8186
---
8287

8388
## 4) "LinkedList 삽입 O(1)"의 실무 함정
@@ -96,6 +101,8 @@ Oracle dev.java의 공식 비교 자료도 이 점을 명확히 설명합니다.
96101

97102
`RandomAccess` 마커 인터페이스가 존재하는 이유도 바로 이 차이를 알고리즘이 구분하기 위함입니다.[^randomaccess-javadoc]
98103

104+
[^randomaccess-javadoc]: RandomAccess Javadoc, Oracle Java SE 17 — https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/RandomAccess.html
105+
99106
---
100107

101108
## 5) Java 2D 배열: "완전 flat"이 아니라 array-of-arrays
@@ -104,6 +111,8 @@ Oracle dev.java의 공식 비교 자료도 이 점을 명확히 설명합니다.
104111

105112
Java의 `int[][]`는 C 스타일의 단일 연속 2D 블록이 아니라, **배열의 배열**입니다.[^jls-arrays]
106113

114+
[^jls-arrays]: JLS Chapter 10 (Arrays), multidimensional arrays are arrays of arrays — https://docs.oracle.com/javase/specs/jls/se24/html/jls-10.html
115+
107116
그래도 아래 순회는 성능 차이가 납니다.
108117

109118
```java
@@ -139,6 +148,8 @@ Javadoc도 직접 이렇게 말합니다.
139148

140149
> "ArrayDeque는 queue로 사용할 때 LinkedList보다 faster일 가능성이 높다."[^arraydeque-javadoc]
141150
151+
[^arraydeque-javadoc]: ArrayDeque Javadoc, Oracle Java SE 25 — https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/ArrayDeque.html
152+
142153
물론 예외는 있습니다.
143154

144155
- `null` 원소를 꼭 저장해야 한다 (`ArrayDeque`는 null 금지)
@@ -152,6 +163,9 @@ Javadoc도 직접 이렇게 말합니다.
152163

153164
Project Valhalla(JEP 401)는 value class/value object를 통해 JVM이 더 공격적인 메모리 최적화를 할 여지를 넓히는 방향입니다.[^jep401][^valhalla]
154165

166+
[^jep401]: JEP 401: Value Classes and Objects (Preview) — https://openjdk.org/jeps/401
167+
[^valhalla]: OpenJDK Project Valhalla — https://openjdk.org/projects/valhalla/
168+
155169
핵심은 "가능성"입니다.
156170

157171
- value object는 identity가 없으므로
@@ -177,45 +191,53 @@ Project Valhalla(JEP 401)는 value class/value object를 통해 JVM이 더 공
177191
3. LinkedList를 선택할 때는 "왜 LinkedList여야 하는지"를 코드 코멘트/리뷰에 남김
178192
4. 성능 이슈는 `System.nanoTime()` 루프가 아니라 JMH로 검증[^jmh]
179193

194+
[^jmh]: OpenJDK JMH — https://github.com/openjdk/jmh
195+
180196
Big-O는 여전히 중요합니다. 다만 **현대 CPU에서 데이터가 어떻게 배치되고 이동하는지**까지 같이 봐야, 실제 서비스 성능을 예측할 수 있습니다.
181197

182198
---
183199

184-
## 9) 재현 가능한 JMH 예시 (로컬 + GitHub Actions)
200+
## 9) 재현 가능한 JMH 예시 (로컬 clone 기준)
185201

186202
말로만 "ArrayList가 보통 빠르다"고 끝내지 않기 위해, 이 저장소에 JMH 예시를 같이 두었습니다.
187203

188-
- 벤치마크 코드: `benchmarks/jmh-arraylist-vs-linkedlist/src/main/java/io/clickin/bench/ListAndDequeBenchmark.java`
189-
- 실행 워크플로: `.github/workflows/jmh-collections-benchmark.yml`
204+
- 예제 경로(canonical): `examples/blog/2026-02-25-arraylist-vs-linkedlist-cache-locality`
205+
- GitHub 링크 템플릿: `https://github.com/<owner>/<repo>/tree/main/examples/blog/2026-02-25-arraylist-vs-linkedlist-cache-locality`
206+
- 벤치마크 코드: `examples/blog/2026-02-25-arraylist-vs-linkedlist-cache-locality/src/main/java/io/clickin/bench/ListAndDequeBenchmark.java`
207+
- 실행 스크립트: `examples/blog/2026-02-25-arraylist-vs-linkedlist-cache-locality/run-local-jmh.sh`
190208

191209
로컬에서는 아래처럼 동일한 설정으로 바로 실행할 수 있습니다.
192210

193211
```bash
194-
mvn -B -ntp -f benchmarks/jmh-arraylist-vs-linkedlist/pom.xml clean package
195-
java -jar benchmarks/jmh-arraylist-vs-linkedlist/target/jmh-benchmarks.jar \
196-
io.clickin.bench.ListAndDequeBenchmark \
197-
-wi 5 -i 8 -w 300ms -r 300ms -f 1 -tu ns \
198-
-jvmArgs "-Xms1g -Xmx1g" \
199-
-rf json -rff benchmarks/jmh-arraylist-vs-linkedlist/results/jmh-result.json
212+
git clone https://github.com/<owner>/<repo>.git
213+
cd <repo>
214+
./examples/blog/2026-02-25-arraylist-vs-linkedlist-cache-locality/run-local-jmh.sh
200215
```
201216

202-
GitHub Actions 실행 시에는 결과(JSON/TXT)와 실행 환경 정보(Java version, CPU 정보)를 artifact로 올려서,
203-
"어떤 머신/JDK에서 나온 숫자인지"를 추적 가능하게 했습니다.
217+
아래는 이 글 작성 시점에 실행한 **quick run 결과**입니다.
218+
219+
- 설정: `./run-local-jmh.sh --quick` (quick preset)
220+
221+
| Scenario | size | Array/ArrayDeque (ns/op) | LinkedList (ns/op) | 관측 |
222+
|---|---:|---:|---:|---|
223+
| `getMiddle` | 65536 | `ArrayList` 1.627 | 114522.376 | LinkedList가 약 70,000배 느림 |
224+
| `iterate` | 65536 | `ArrayList` 167263.719 | 277359.265 | LinkedList가 약 1.66배 느림 |
225+
| `middleInsertThenRemove` | 65536 | `ArrayList` 3576.092 | 218811.664 | LinkedList가 약 61배 느림 |
226+
| `headInsertThenRemove` | 65536 | `ArrayList` 7530.176 | 33.369 | LinkedList가 약 225배 빠름 |
227+
| `tailAddThenRemove` | 65536 | `ArrayList` 15.028 | 31.444 | ArrayList가 약 2.09배 빠름 |
228+
| `iteratorRemoveAndRestore` | 65536 | `ArrayList` 7434.923 | 25.838 | LinkedList가 약 288배 빠름 |
229+
| `deque offerLast/pollFirst` | 65536 | `ArrayDeque` 15.159 | 35.446 | ArrayDeque가 약 2.34배 빠름 |
230+
| `deque offerFirst/pollLast` | 65536 | `ArrayDeque` 14.968 | 34.369 | ArrayDeque가 약 2.30배 빠름 |
231+
232+
관찰 포인트는 두 가지입니다.
233+
234+
1. `read/iterate/deque` 계열에서는 Array 구조가 일관되게 유리합니다.
235+
2. 반대로 `head insert/remove`, `iterator remove`처럼 LinkedList의 구조적 강점이 드러나는 시나리오도 분명히 존재합니다.
236+
237+
즉 "항상 ArrayList"가 아니라, **실제 접근 패턴을 기준으로 선택해야 한다**가 이 실험의 핵심 결론입니다.
204238

205239
결론은 단순합니다.
206240

207241
- 이론 복잡도만 보면 LinkedList가 좋아 보일 때가 있어도
208242
- 일반적인 Java 애플리케이션의 대부분 시나리오에서는
209243
- `ArrayList`(그리고 Deque라면 `ArrayDeque`)가 더 좋은 기본값입니다.
210-
211-
---
212-
213-
[^arraylist-javadoc]: ArrayList Javadoc, Oracle Java SE 25 — https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/ArrayList.html
214-
[^linkedlist-javadoc]: LinkedList Javadoc, Oracle Java SE 25 — https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/LinkedList.html
215-
[^randomaccess-javadoc]: RandomAccess Javadoc, Oracle Java SE 17 — https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/RandomAccess.html
216-
[^devjava-al-vs-ll]: ArrayList vs LinkedList, dev.java (Oracle) — https://dev.java/learn/api/collections-framework/arraylist-vs-linkedlist/
217-
[^arraydeque-javadoc]: ArrayDeque Javadoc, Oracle Java SE 25 — https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/ArrayDeque.html
218-
[^jls-arrays]: JLS Chapter 10 (Arrays), multidimensional arrays are arrays of arrays — https://docs.oracle.com/javase/specs/jls/se24/html/jls-10.html
219-
[^jep401]: JEP 401: Value Classes and Objects (Preview) — https://openjdk.org/jeps/401
220-
[^valhalla]: OpenJDK Project Valhalla — https://openjdk.org/projects/valhalla/
221-
[^jmh]: OpenJDK JMH — https://github.com/openjdk/jmh

0 commit comments

Comments
 (0)