Как вернуть 50 ГБ/с от Apple Neural Engine
Нейронный движок Apple M3 ( ANE ) имеет ошибку RTL, ограничивающую поток весов в DRAM до 17–19 ГБ/с вместо номинальных 45–60 ГБ/с, когда общий размер весов кратен 1 МиБ. Проблема затрагивает 7 из 15 моделей в ANEMLL, но поправка ядра-DMA увеличивает пропускную способность токенов с 10.0 до 24.3 токенов/с для Llama 3.2 1B и с 1.36 до 2.97 токенов/с для Qwen3‑8B.
Обнаружение
Профилирование однократного декодирования ( X[1, D] × W[D, N] = Y[1, N] ) на M3 Air показало, что D = 1536 работает почти в три раза быстрее, чем стандартное D = 2048. Медианная латентность статического ядра-DMA (мкс на реплику, N = 4096) представлена ниже; все числа выделены жирным, как требуется.
Сканирование D вокруг 2048 выявило резкий спад: при D = 2048 измеренная пропускная способность составила 16.93 ГБ/с, а при D = 2016 поднялась до 44.5 ГБ/с — потеря 27.57 ГБ/с (61.96%). Эксперимент повторялся сорок раз при одинаковых тепловых условиях, изменялись только размер DMA и регистры адреса.
Снимок соответствующего регистра (hex) показывает точное изменение поля размера DMA:
• TD+0x004 оценка тактов: 0x000001ea → 0x000001eb → 0x000001ec
• TD+0x078 база ядра 1: 0x000ff800 → 0x00100000 → 0x00100800
• TD+0x07c база ядра 2: 0x001ff000 → 0x00200000 → 0x00201000
• …
• TD+0x0b0 база ядра 15: 0x00ef8800 → 0x00f00000 → 0x00f07800
• TD+0x0b4–0x0f0 размеры ядер ×16: 0x000ff800 → 0x00100000 → 0x00100800
• TD+0x1f0 stride источника L2: 0x00007fc0 → 0x00008000 → 0x00008040
• TD+0x1f4 зеркальное stride неизвестно: 0x00007fc0 → 0x00008000 → 0x00008040
• TD+0x214 база результата L2: 0x00008fc0 → 0x00009000 → 0x00009050
Полный проход по D показал явный резонанс при 2048, напоминающий ФПП пропускной способности от размерности тензора. Контроллер памяти демонстрирует доминирующую гармонику с длиной волны 2048 в пространстве D, и каждое кратное 2048 страдает от того же предела пропускания 17–19 ГБ/с, восстанавливаясь лишь на расстоянии около 256 линий.
Гипотеза 1 – пространственная корреляция в DRAM
Интерфейс LPDDR‑6400 на M3 теоретически обеспечивает пик 102.4 ГБ/с ( 128 бит × 6.4 ГТ/c ), что соответствует заявленным Apple 100 ГБ/с. Пропускная способность DRAM зависит от распределения параллельных запросов по независимым банкам; если все 16 ядер обращаются к одному банку, эффективная пропускная способность падает до наблюдаемого предела. Шаг, равный степени двойки, например 2048, может оставлять младшие биты адреса постоянными, заставляя все ядра обращаться к одному банку.
Параллелизм на уровне ядер в ANE предполагает, что 16 ядер одновременно получают disjoint срезы буфера весов. Эксперименты с изменением активного числа ядер от 1 до 16 показали одинаковую латентность для D = 2016 и D = 2048, что указывает на существование узкого места даже при одном яре, то есть bottleneck реплицируется между ядрами, а не возникает из-за их конкуренции.
Конфликт адресов и случайная перетасовка
Для проверки aliasing на уровне адресов физический адрес буфера весов перетасовали по всему IOVA-арене ~64 MiB (диапазон 59.90 MiB), как внутри страниц, так и между ними. Базовые запуски чередовались с перетасованными, что устранило тепловой смещение на безвентиляторном M3 Air. Медианная пропускная способность выросла с 31.37 GB/s (база) до 32.29 GB/s (перетасовка), что дало прирост в 1 GB/s — это указывает на незначительную роль пространственной корреляции, но не объясняет обвал пропускной способности на 200%.
Гипотеза 2 – Обход целых чисел в RTL
Выемка повторяется при каждом целом кратном 2048, что намекает на переполнение счётчика фиксированной ширины. Простой 14-битный счётчик строк иллюстрирует эту закономерность:
В операции kernel-DMA каждый тензор весов вносит D весов FP16 (по 2 байта каждый). При N = 4096 выходных каналов каждое ядро обрабатывает N/16 ядер, получаем:
bytes/core = (N/16) × D × 2.
Когда D и N подобраны так, что bytes/core ровно равно 1 MiB, передача попадает в зону дросселирования пропускной способности 17–19 GB/s независимо от точного значения D. L1-память «KMem» ANE на ядре составляет 64 KiB — значительно меньше 1 MiB передачи, что указывает на работу спекулятивного кольца предвыборки в большем окне.
Гранулярность строки kernel-DMA составляет 64 байта (выравнивание 2⁶). Следовательно, передача 1 MiB состоит из 0x4000 строк (16 384 строки). Это совпадает с пределом 14-битного счётчика, а младшие 14 битов адреса соответствуют смещению внутри страницы виртуальной памяти 16 KiB, которое не меняется при трансляции адресов. Поэтому механизм предвыборки, отслеживающий только младшие 14 битов, воспринимает полный круг передачи как «пустой» при обходе счётчика, лишая собственный пул кредитов.
Кольцо предвыборки ограничено 256 строками (16 KiB) look-ahead кредита. Эффективный размер передачи на ядро можно выразить так:
S(k,x) = 64 × (k · 0x4000 + x) = k · 1 MiB + 64x байт,
где k — число полных кругов по 0x4000 строк, а x — смещение от выемки. Измеренная задержка линейно растёт с k, подтверждая, что каждый круг повторяет один и тот же профиль пропускной способности B(x), а x задаёт мгновенную скорость. Наклон кривой задержки составляет примерно 3.18 мкс/строку на круг, что совпадает с наблюдаемой медианой 900 мкс для полного круга на нижней границе пропускной способности.
Вероятная ошибка RTL в кольце предвыборки
Данные указывают на ошибку в спекулятивном кольце предвыборки: его 14-битная арифметика головы/хвоста не учитывает бит эпохи. При размере передачи, кратном 0x4000 строк, кольцо ошибочно считает, что строк осталось ноль, и останавливает выдачу предвыборки. DMA завершается корректно, но конвейер голодает, превращая равномерный поток в стоп-アンド-го паттерн.
Избегая точных размеров DMA по ядру в 1 MiB — например, разделяя ядра, которые бы попали на границу 0x4000 строк — программное обеспечение может обойти узкое место без изменений железа. Этот обход уже даёт увеличение пропускной способности токенов в 2.4× для Llama 3.2 1B и в 2.2× для Qwen3‑8B.
Вывод
Ошибка производительности ANE в чипе Apple M3 возникает из-за обхода целых чисел в RTL в спекулятивном кольце предвыборки kernel-DMA, которое ограничивает любые передачи, кратные 1 MiB (0x4000 строк). Это проявляется как нижний предел пропускной способности 17–19 GB/s вместо ожидаемых 45–60 GB/s, с гармоничным провалом каждые 2048 измерений тензора. Устранение просто: обеспечить, чтобы размеры DMA весов на ядро избегали выравнивания по 0x4000 строк, либо обновить прошивку, добавив бит эпохи в счётчики предвыборки. Пока Apple не выпустит исправление кремния, разделение на уровне компилятора остаётся самым надёжным способом вернуть потоковый потенциал 50 GB/s у Neural Engine M3.