애플 M3 뉴럴 엔진에서 50 GB/s 되찾기
애플 M3 뉴럴 엔진(ANE)의 RTL 성능 에라타로 인해 가중치 크기가 1 MiB의 정수 배일 때 DRAM 처리량이 공칭 45-60 GB/s에서 17-19 GB/s로 급감한다. 이는 저수준 하드웨어 분석을 통해 발견되었으며, 커널 DMA 엔진의 문제 경로를 우회함으로써 Llama 3.2 1B의 토큰 처리량이 10.0에서 24.3 tokens/s로, Qwen3-8B가 1.36에서 2.97 tokens/s로 각각 2배 이상 향상되었다.
ANE에서 50 GB/s 되찾기 (2026년 8월 10일, 3,162 단어)
소개
애플 M3 뉴럴 엔진(ANE)의 RTL 성능 에라타(erratum)는 총 가중치 크기가 1 MiB의 정수 배일 때마다 DRAM 가중치 스트리밍 처리량을 공칭 45-60 GB/s에서 17-19 GB/s로 제한합니다. 현재 이 문제는 ANEMLL의 15개 모델 중 7개에 영향을 미칩니다. 커널 DMA 엔진의 투기적 프리페치 링(speculative prefetch ring)에서 문제가 되는 경로를 우회함으로써 Llama 3.2 1B의 토큰 처리량은 10.0에서 24.3 tokens/s로(DRAM 사용량은 24.7에서 60.0 GB/s로), Qwen3-8B는 1.36에서 2.97 tokens/s로(DRAM 사용량은 22.4에서 48.7 GB/s로) 향상되었습니다.
발견
저는 단일 토큰 디코딩을 위해 뉴럴 엔진의 DRAM 가중치 스트리밍 처리량(GB/s)을 프로파일링하고 있었습니다:
X[1,D] × W[D,N] = Y[1,N]
N=4096일 때, D=1536이 Llama 3.2의 기본값인 D=2048보다 거의 3배 빠르게 실행되는 것을 발견했습니다.
STATIC(순수 KernelDMA) 복제본당 중앙값 µs, N=4096:
D: 576 / 768 / 1024 / 1280 / 1536 / 2048 rep a: 150.4 / 196.8 / 238.1 / 293.8 / 310.9 / 997.6 rep b: 157.8 / 190.2 / 250.1 / 288.3 / 326.8 / 995.0 rep c: 148.7 / 189.6 / 249.4 / 275.2 / 316.5 / 995.4
D=2048 근처의 값을 스윕(sweep)해 보니:
이상하군요? D=2048에서 처리량은 16.93 GB/s였습니다. D=2016에서는 44.5 GB/s였습니다. 즉, 44.505062 − 16.930761 = 27.574301 GB/s (61.96% 감소) 차이가 납니다. 44.5 GB/s에서 16.93 GB/s로, 27.57 GB/s의 급락이 발생한 것입니다.
참고로 이 스윕 데이터는 M3 Air에서 수집되었으며, 단일 실행 내에서 동일한 열/부하 조건에서 40회 반복되었습니다. 또한 ANE 레지스터 파일의 DMA 크기와 주소만 유일하게 변경되는 변수임을 확인했습니다:
D=2044 / D=2048 / D=2052일 때, TD+0x004의 예상 사이클은 모두 동일하고, TD+0x078의 core 1 base는 0x000ff800 / 0x00100000 / 0x00100800, core 2 base는 0x001ff000 / 0x00200000 / 0x00201000, ... core 15 base는 0x00ef8800 / 0x00f00000 / 0x00f07800, TD+0x0b4–0x0f0의 core sizes(×16)는 core base와 동일, TD+0x134의 Common.Cin은 0x000007fc / 0x00000800 / 0x00000804, TD+0x1f0의 L2 소스 스트라이드는 0x00007fc0 / 0x00008000 / 0x00008040, TD+0x1f4의 알 수 없는 스트라이드 미러도 동일한 값, TD+0x214의 L2 결과 base는 0x0008fc0 / 0x0009000 / 0x0009050입니다.
그래서 D의 전체 범위를 스윕해 보았습니다:
KernelDMA DRAM D-크기 스윕(대화형): 2,747개의 원시 관측치 · 67개 D 값 × 41회 무작위 라운드 · Apple M3, 모든 시간 측정 관측치의 중앙값 추세
이건 좋은 발상이었습니다. D=2048에서 공명(resonance)이 보였기 때문입니다. 처리량(GB/s)을 텐서 차원(D)에 대해 FFT를 수행할 줄은 몰랐았는데, 여기 그 결과가 있습니다:
분명히 메모리 컨트롤러의 처리량은 텐서 차원 공간에서 파장 2048의 지배적 고조파(dominant harmonic)를 가집니다. 그리고 안타깝게도 그것은 딥(하락 구간)입니다 :(
D=2048의 모든 배수는 마찬가지로 17-19 GB/s의 고정된 대역폭 하한에 제한됩니다. D=2048의 배수에서 처리량은 공칭 45-60 GB/s에서 17-19 GB/s로 급격히 떨어지며, 단 ~256 라인 떨어진 곳에서 공칭값으로 회복합니다.
이것은 RTL 정확성 버그가 아닙니다. 커널 DMA는 여전히 전송을 올바르게 완료하기 때문입니다. 하지만 2048 근처의 요청들은 크레딧이 부족한 별도의 이슈(issue) 체제로 강제되어, 불행히도 매우 흔한 전송 크기에서 28-43 GB/s(최악의 경우 60→17)만큼 처리량을 질식시키고 있습니다.
가설 1 - DRAM 공간 상관관계
16개 코어가 2의 거듭제곱 스트라이드에서 동일한 DRAM 뱅크에 앨리어싱(aliasing)되고 있는 걸까요?
DRAM은 병렬 데이터 인터페이스입니다. DRAM 대역폭은 DQ(데이터) 핀 수 × 핀당 데이터 전송률입니다:
DRAM BW = N × R = 128 bit × 6.4 GT/s = 102.4 GB/s
M3의 LPDDR-6400이 102.4 GB/s라는 것은 광고된 100 GB/s와 일치합니다. 지속 가능한 DRAM 대역폭은 엄밀히 그 DQ 활용률이며, 102.4 GB/s DRAM 상한에 미달하는 매 1 GB/s는 DQ가 대기하는 추가 사이클을 의미합니다.