You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
CPU는 메모리를 바이트 단위가 아니라 **cache line 단위**로 가져옵니다(대부분 64B).
75
+
CPU는 메모리를 바이트 단위가 아니라 **cache line 단위**로 가져옵니다(많은 x86_64 환경에서 64B, 아키텍처별 차이 있음).[^cacheline-caveat]
76
+
77
+
[^cacheline-caveat]: Cache line 크기는 CPU 아키텍처/구현에 따라 다를 수 있습니다. 예: Intel Optimization Reference Manual(2024)과 Arm AArch64 CTR_EL0 문서 참고. 또한 Intel 매뉴얼은 데이터 의존적 주소 추적(pointer chasing) 패턴에서 prefetch 효율이 떨어질 수 있음을 설명합니다. 이 글에서는 서버 환경에서 흔한 x86_64 기준 설명을 사용합니다. https://cdrdv2-public.intel.com/814198/248966-Optimization-Reference-Manual-V1-050.pdf, https://developer.arm.com/documentation/ddi0601/latest/AArch64-Registers/CTR-EL0--Cache-Type-Register
72
78
73
79
배열은 연속 메모리라서, `arr[i]`를 읽을 때 주변 원소 참조들도 같이 캐시에 올라옵니다.
74
80
그래서 다음 접근이 캐시 히트가 될 가능성이 큽니다(공간 지역성).
@@ -79,7 +85,7 @@ CPU는 메모리를 바이트 단위가 아니라 **cache line 단위**로 가
79
85
- pointer chasing으로 캐시/TLB 미스가 누적되기 쉽고
80
86
- 하드웨어 prefetcher가 잘 먹히지 않는 경우가 많습니다
81
87
82
-
Oracle dev.java의 공식 비교 자료도 이 점을 명확히 설명합니다.[^devjava-al-vs-ll]
88
+
Oracle dev.java의 공식 비교 자료도 ArrayList/LinkedList 선택 시 성능 관점을 함께 설명합니다.[^devjava-al-vs-ll]
83
89
84
90
[^devjava-al-vs-ll]: ArrayList vs LinkedList, dev.java (Oracle) — https://dev.java/learn/api/collections-framework/arraylist-vs-linkedlist/
85
91
@@ -99,17 +105,14 @@ Oracle dev.java의 공식 비교 자료도 이 점을 명확히 설명합니다.
99
105
100
106
그래서 일반적인 List 사용에서는 `ArrayList`가 더 빠른 경우가 훨씬 많습니다.
101
107
102
-
`RandomAccess` 마커 인터페이스가 존재하는 이유도 바로 이 차이를 알고리즘이 구분하기 위함입니다.[^randomaccess-javadoc]
103
-
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
108
106
109
---
107
110
108
111
## 5) Java 2D 배열: "완전 flat"이 아니라 array-of-arrays
109
112
110
113
여기서 자주 헷갈리는 포인트가 있습니다.
111
114
112
-
Java의 `int[][]`는 C 스타일의 단일 연속 2D 블록이 아니라, **배열의 배열**입니다.[^jls-arrays]
115
+
Java의 `int[][]`는 C 스타일의 단일 연속 2D 블록이 아니라, **배열 변수의 배열**입니다.[^jls-arrays]
113
116
114
117
[^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
118
@@ -146,9 +149,9 @@ Deque/Queue/Stack 용도로는 `LinkedList`보다 `ArrayDeque`가 기본 선택
146
149
147
150
Javadoc도 직접 이렇게 말합니다.
148
151
149
-
> "ArrayDeque는 queue로 사용할 때 LinkedList보다 faster일 가능성이 높다."[^arraydeque-javadoc]
152
+
> "ArrayDeque는 queue로 사용할 때 LinkedList보다 더 빠를 가능성이 높다."[^arraydeque-javadoc]
150
153
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
154
+
[^arraydeque-javadoc]: "This class is likely to be faster than Stack when used as a stack, and faster than LinkedList when used as a queue." ArrayDeque Javadoc, Oracle Java SE 25 — https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/ArrayDeque.html
152
155
153
156
물론 예외는 있습니다.
154
157
@@ -159,26 +162,25 @@ Javadoc도 직접 이렇게 말합니다.
159
162
160
163
---
161
164
162
-
## 7) 앞으로의 변수: Project Valhalla
165
+
## 7) 왜 지금 더 차이가 커졌나: memory wall
163
166
164
-
Project Valhalla(JEP 401)는 value class/value object를 통해 JVM이 더 공격적인 메모리 최적화를 할 여지를 넓히는 방향입니다.[^jep401][^valhalla]
167
+
제가 이 주제를 "요즘 CPU에서 더 중요해졌다"고 보는 이유는, 알고리즘 교과서가 틀려서가 아니라 **하드웨어 밸런스가 바뀌었기 때문**입니다.
165
168
166
-
[^jep401]: JEP 401: Value Classes and Objects (Preview) — https://openjdk.org/jeps/401
Wulf/McKee가 정리한 고전적인 memory wall 논점은 단순합니다. CPU 성능 향상 속도가 DRAM 지연 개선 속도보다 더 빨라서, 메모리 접근의 상대 비용이 시간이 갈수록 커진다는 점입니다.[^memory-wall]
168
170
169
-
핵심은 "가능성"입니다.
171
+
실무적으로는 "캐시 미스 한 번이 몇 ns인가"보다, **"코어가 그동안 몇 cycle을 놀게 되느냐"**가 더 중요합니다.
172
+
McCalpin은 이미 1990년대 중반에 "cache miss 한 번 처리하는 동안 최신 프로세서는 100개 이상의 부동소수점 연산을 수행할 수 있다"고 정리했고, 지연(latency) 지배가 강해지는 방향을 지적했습니다.[^mccalpin-balance]
170
173
171
-
- value object는 identity가 없으므로
172
-
- JVM이 flattening/scalarization 같은 표현 최적화를 적용할 수 있고
173
-
- 특정 경우 locality/footprint에 유리해질 수 있습니다
174
+
그래서 제 해석은 이렇습니다.
174
175
175
-
하지만 여기서 과장하면 안 됩니다.
176
+
- pointer chasing이 과거에 없던 문제가 된 게 아니라
177
+
-**예전보다 miss penalty(특히 cycle 기준)가 더 커져서**
178
+
- locality 차이가 실제 벽시계 시간에 더 크게 번역되기 쉬워졌습니다.
176
179
177
-
- 어떤 레이아웃이 항상 보장되는 것은 아님
178
-
- 모든 타입/모든 상황에서 flattening이 일어나는 것도 아님
179
-
- 현재는 preview 성격이므로 버전/구현별 차이를 전제로 봐야 함
180
+
즉 "옛날엔 LinkedList도 충분히 빨랐다"는 단정은 조심해야 하지만, "현대 CPU에서는 locality를 놓치면 비용이 더 빠르게 커진다"는 방향은 역사적으로도 일관된 설명입니다.
180
181
181
-
즉 Valhalla는 "Linked 구조가 자동으로 사라진다"가 아니라, **"JVM 최적화 가능 공간이 커진다"**로 이해하는 것이 정확합니다.
182
+
[^memory-wall]: Wulf, McKee, "Hitting the Memory Wall: Implications of the Obvious" (1995) — https://libraopen.lib.virginia.edu/downloads/4b29b598d
183
+
[^mccalpin-balance]: John D. McCalpin, "Memory Bandwidth and Machine Balance in Current High Performance Computers" (1995) — https://www.cs.virginia.edu/~mccalpin/papers/balance/
182
184
183
185
---
184
186
@@ -189,55 +191,68 @@ Project Valhalla(JEP 401)는 value class/value object를 통해 JVM이 더 공
189
191
1. 특별한 이유가 없으면 `List`는 `ArrayList`부터 시작
190
192
2. Deque가 필요하면 `ArrayDeque`부터 시작
191
193
3. LinkedList를 선택할 때는 "왜 LinkedList여야 하는지"를 코드 코멘트/리뷰에 남김
192
-
4. 성능 이슈는 `System.nanoTime()` 루프가 아니라 JMH로 검증[^jmh]
0 commit comments