Inside the M4 Apple Neural Engine · Part 4

The Complete Machine

What the M4 Neural Engine is, how it is built, what it can do, and where it stops

manjeet singh · August 2026 · Substack version coming soon

Parts 1, 2, and 3 treated the ANE as a tool: compiling models, measuring throughput, and training a transformer. This part describes the M4 ANE itself, from the host interface and compiled program format to SRAM, DMA, compute, and measured limits.

MIL was the right level for those experiments. It let us describe a graph, ask Apple's compiler to place it on the ANE, and measure the result. It did not expose the decisions made after the graph crossed the compiler boundary. Fusion, tiling, SRAM placement, DMA scheduling, tensor layout, and numeric mode were chosen inside ANECompiler.framework. A MIL benchmark could show that two graphs behaved differently; it could not show which hardware fields made them different.

For this part we moved one layer down and treated the compiled HWX program as the object of study. We compiled matched MIL graphs that differed in one property, compared their HWX binaries, changed candidate fields, and loaded the modified programs directly. A field was assigned a hardware role only after the edited program loaded and its output or timing changed in the expected way. Programs emitted from scratch supplied a second check for the formats we had mapped. HWX still passes through Apple's loader and firmware; this method removes the graph compiler from the experiment, not the runtime.

PathWhat it revealsWhat remains hidden
MIL → compiler → ANEOperation support, graph-level performance, numerical output, compiler policyThe descriptor fields and scheduling choices selected by the compiler
HWX → loader → ANETask Descriptors, DMA records, tiling, modes, synchronization, field-level A/B testsFirmware internals and physical events without performance counters

This account comes mainly from reverse engineering an M4. We traced the private API below CoreML, recovered the in-memory compile and evaluation paths, decoded the 0xBEEFFACE Mach-O program format, mapped Task Descriptor fields, derived the tiling formulas, and recovered the activation lookup-table format. Programs emitted from scratch match compiler output byte for byte and run at 98.8% of measured peak throughput. Separate experiments cover int8 execution, tensor geometry, dispatch latency, Task Descriptor counts, and compute/DMA overlap. The compiler's 2,360-byte hardware parameter table was extracted from a live process with LLDB.

Bryngelson's reference for A11 through M5 supplies names for the compiler passes and fields in that parameter table. The Orion paper documents MIL-level constraints relevant to LLM workloads. Measurements and claims taken from those sources are identified where they appear.


The Pipeline, End to End

The diagram follows one program from MIL text through compilation, loading, dispatch, and execution on the M4 ANE.

1 / 9
COMPILE · ANECompiler.framework model.mil program(1.3) func main<ios18> x: fp16[512×256] y1 = conv(x, W) y2 = add(y1, b) y = relu(y2) -> (y) typed SSA · 3 ops op graph conv 1×1 add bias relu 3/3 validators pass conv + bias + relu ZinNEBypassLayer 1 engine layer 3 ops → 1 layer legalize · tile operand 4 MiB tile 0 · 2 MiB tile 1 · 2 MiB HAT[0x1b8] = 2 MiB schedule · TDs TD0 load A TD1 conv TD2 conv TD3 store 2 MiB L2 cut at 2 MiB peak bank layout ⌊addr/16⌋ mod 64 no bank conflicts model.hwx 0xBEEFFACE __TEXTTD stream __KERN_0weights / LUT __FVMLIB ×2in / out desc LOAD · aned + IOKit aned cache com.apple.aned/…/model.hwx IOKit AppleH16ANEInterface DART device VA · 16 KiB pages dispatch floor ≈ 90 µs XPC round-trip, paid per evaluation HARDWARE · H16G Unified DRAM · 120 GB/s · input / output IOSurfaces · operands > 2 MiB streamed tile DMA 0 tile DMA 1 tile DMA 2 kernel DMA · 16 chunks SRAM scratchpad · 64 banks · 16 B interleave · no eviction weight store 64 KiB resident · 16 MiB streamed MAC array · 8 compute sets × 8 accumulators · fp16 planar engine ALU · 33-pt LUT · reduce relu via ALU (no LUT) IOSurface output zero-copy to CPU / GPU 18.77 TFLOPS fp16 · 4.57 W · 0 mW idle

What the ANE Is

The Apple Neural Engine is a fixed-function matrix accelerator included in Apple systems-on-chip since the A11 in 2017. It shares unified DRAM and the SoC power budget with the CPU and GPU. Apple exposes it through CoreML as one target behind the framework's scheduler. The instruction format, driver interface, and execution counters are private.

Apple M4 · one die (T8132) · one memory CPU · 10 cores + SME peak3.9 TOPS int8 power5–8 W dispatch0 (inline) runsany code GPU · 10 cores (Metal) peak~3.6 TFLOPS fp16 power8–12 W dispatch~0 (in-flight) runsshaders ANE · 16 cores (H16G) peak19 TFLOPS fp16 power4.6 W dispatch≈90 µs / call runscompiled graphs writereadread / write IOSurface one buffer, three mappings, zero copies Unified DRAM · up to 32 GB 120 GB/s, shared by all three · the ANE streams anything over 2 MiB through this
The CPU, GPU, and ANE use different programming models but share unified memory. A GPU-produced tensor can reach the ANE through another mapping of the same IOSurface. Each ANE submission adds approximately 90 µs of host-side dispatch time.

The ANE executes complete compiled graphs as atomic operations. Its program model exposes no branch instructions, loops, or dynamic control flow, and execution cannot be paused to inspect intermediate values. Inputs and outputs use IOSurfaces, shared-memory regions that the CPU, GPU, and ANE can map without copying.

The M4 ANE has the internal codename H16G. IORegistry identifies it as AppleH16ANEInterface with architecture h16g, version 192, and 16 registered cores. The meaning of the "G" suffix has not been confirmed.

Inside the Engine

Most of the internal model came from byte-level comparisons of compiled programs, followed by runs of binaries emitted from scratch. Those comparisons exposed Task Descriptor fields, tiling rules, the weight-streaming stride, and the lookup-table format. We also called ZinIrHalH16g::GetParams() inside the live M4 compiler process and extracted its 2,360-byte Hardware Abstraction Table (HAT). Bryngelson's compiler analysis supplies names for several internal passes and HAT fields. The values below were read from the M4.

The 2 MiB rule · HAT[0x1b8] operand ≤ 2 MiB → resident DMAload whole operand MACcompute time → fill first, then run · the array idles during the load operand > 2 MiB → tiled and double-buffered DMA tile 0tile 1tile 2tile 3 MAC tile 0tile 1tile 2tile 3 time → tile n+1 loads while tile n computes · only the first tile is exposed inside SRAM: 64 banks · 16 B granule · bank = ⌊addr / 16⌋ mod 64the compiler picks a row stride so consecutive rows land on different banks 8 rows, stride coprime with 64 → 8 different banks, no conflict
The compiler reads the 2 MiB threshold from the HAT. Smaller operands are loaded before compute begins. Larger operands are tiled, allowing the next tile's DMA transfer to overlap computation on the current tile.

SRAM

The on-chip memory is a 64-bank scratchpad with a 16-byte interleave granule. An address maps to bank floor(addr / 16) mod 64. The compiler manages it as scratch space with no runtime cache replacement policy. When a tensor's data fits in SRAM, it stays there for the duration of the computation. When it does not fit, the compiler tiles the work and streams data through in chunks.

The streaming threshold is 2 MiB per operand (not per operation, and not a sum of all operands). This value comes from the HAT at offset 0x1b8. Below 2 MiB, an operand can be fully resident before compute starts. Above it, the compiler splits the operand into tiles and double-buffers them: while tile N computes, tile N+1 loads.

The compiler switches each operand from resident to streamed mode at 2 MiB. The throughput change previously observed around 24–32 MB reflects the behavior of tiled, streamed operands rather than a single total-SRAM capacity boundary. Performance beyond the threshold depends on overlap between tile loads and compute.

Weights have their own budget. Up to 64 KiB of weights stay resident on-chip (the "kernel-coefficient store"). Weights up to 16 MiB can be streamed through in 16 DMA chunks. The compiler divides the weight matrix into 16 pieces regardless of total size and loads them sequentially.

FINDINGThe compiler compares each operand with HAT[0x1b8], a 2 MiB threshold, when choosing resident or streamed storage.

Compute

The M4 compute array contains 8 compute sets, each with 8 accumulators. FP16 is the default path: products use fp16 inputs, accumulation is wider than fp16 (the exact width has not been measured), and the output is rounded to fp16. W8A8 chains use a packed compute mode in their middle blocks and reach a 38 TOPS ceiling. A compute-configuration word in the Task Descriptor stream selects the mode.

The HAT reports 8 compute sets at offset 0x238, while IORegistry reports 16 cores for the device. The mapping between those two units is not exposed in the interfaces we examined. The ANE clock frequency has also not been measured directly.

The compiler controls the output-channel-group (OCG) size, meaning how many output channels are computed in one pass through the accumulator. The OCG is the smaller of a power-of-two accumulator budget and a format-specific byte cap (32, 16, or 8 bytes depending on weight format). The formula and its constants come straight from the HAT (byte caps at 0x3880x398, per-accumulator tiers at 0x3a80x3c0).

FINDING8 compute sets × 8 accumulators. Default path is fp16 (19 TFLOPS). With W8A8, the MACs dual-pack two int8 ops per cycle (38 TOPS). The per-cycle lane width is not in the HAT.

DMA

Data moves between DRAM and SRAM through DMA engines. Compiled programs reference three tile-DMA engines and one kernel-DMA engine. The physical engine count and queue depth behind those references have not been measured directly. In our tests, sweeping 2 through 8 independent input planes produced a linear slowdown of ~995 µs per additional plane. No concurrency knee appeared, suggesting the DMA path serializes tile loads in the configurations we tested.

Fused operations keep intermediate values in SRAM. When one operation feeds the next, the compiler emits DMA_INTER instead of DMA_STORE followed by DMA_LOAD. A fused matmul + bias + relu runs 5.7× faster than three separate dispatches because it avoids the intermediate DRAM transfers and two additional dispatches.

FINDINGDMA_INTER keeps fused intermediates in SRAM. A fused matmul + bias + relu runs 5.7× faster than the same operations submitted separately.

What the ANE Computes

The ANECompiler.framework validator exports (_ANECValidate*Layer) identify approximately 50 hardware layer types. The operations that compile and execute fall into five groups.

GroupOperations
Matrix opsmatmul, conv (1x1/3x3/5x5), depthwise conv, SDPA (fused attention)
Elementwise256 ALU opcodes: add, mul, sub, max, min, plus post-ops (square, sign). Controlled by a single opcode byte in the binary.
Activationsrelu (direct ALU), sigmoid, tanh, gelu, silu, exp, softplus, elu (via 33-point piecewise-linear LUT), rsqrt/sqrt/reciprocal (dedicated hardware)
Reductionsreduce_sum, reduce_mean, reduce_max (native hardware, not conv trick), softmax and layer_norm (decomposed into 6-7 fused primitives)
Layoutreshape, transpose, concat, space_to_depth, depth_to_space, slice

Operations not supported on the hardware: acos, asin, tan, log, cumsum, mod, and boolean logical ops. These fall back to the CPU (via BNNS) or GPU (via Metal). CoreML handles the segmentation automatically.

Activations like sigmoid and gelu do not have dedicated hardware. Instead, the compiler stores 33 fp16 samples in a segment called __KERN_0 and the hardware linearly interpolates between them at evaluation time. For sigmoid with samples from -8 to +8, the max error is 2.4e-4. We verified that replacing the samples with an arbitrary function (x³, not a MIL op) produces correct output on hardware. Any pointwise function approximable by 33 piecewise-linear segments over a fixed domain can run on the ANE.

How Programs Reach the Hardware

The path from a neural network description to hardware execution has five layers:

MIL text (typed SSA graph)
  → ANECCompile() via XPC to ANECompilerService
    → 0xBEEFFACE Mach-O binary (Task Descriptor stream)
      → aned daemon cache → IOKit DMA → ANE SRAM
        → atomic graph execution → IOSurface output

MIL (Machine Learning Intermediate Language) is the input format. It is a text-based SSA language with typed tensors, named operations, and explicit shapes. The ANE compiler is a C function (ANECCompile) in the private ANECompiler.framework. It can also be reached through the ObjC API (_ANEInMemoryModel.compileWithQoS:).

The compiler produces a custom Mach-O binary with magic number 0xBEEFFACE, CPU type 0x80 (128, for ANE), and CPU subtype 0x07 (H16G). The binary contains a stream of Task Descriptors (TDs) that configure DMA engines and the compute array. The program is not a sequence of instructions in the CPU sense. It is a register-write stream that configures the hardware for one graph execution.

Dispatch from the host goes through the aned daemon via XPC for compile and load, but evaluation takes a direct path through ANEServicesProgramProcessRequestDirect without the daemon. The host posts a mailbox command, the engine runs the entire graph, and the output appears in the designated IOSurface.

Each evaluation adds approximately 90 µs of host time for the XPC round trip and IOKit setup. The floor is CPU-bound and does not change with SoC power state. For operations that complete in under ~1 ms, dispatch occupies a large fraction of wall-clock time. Fusion reduces that cost by placing more work in each submitted program.

FINDINGEvery evaluation costs ≈90 µs of host time before the engine does anything. Under ~1 ms of work, dispatch dominates; the fix is fewer, larger programs.

Performance

Measurement note: The M4 reaches 4.57 W at fp16 peak, or 4.1 TFLOPS/W. Earlier measurements taken with persistent pmset lowpowermode enabled reported 2.79 W and 6.6 TFLOPS/W. All results below use the August 2026 measurements without low-power mode.

Throughput

Peak throughput requires deep operation chains of 32–64 layers and a channel/spatial balance around 512–1024 channels at 64²–128² spatial dimensions. A single matmul reaches roughly 30% of peak. A 64-layer conv1x1 chain keeps the compute pipeline occupied and reaches 98.8%.

ConfigurationFP16 TFLOPSW8A8 TOPS% of ceiling
Single matmul 1024×10245.227%
Single matmul 2048×20485.730%
32L conv1x1 512ch 128²18.6734.5498.3%
64L conv1x1 512ch 128²18.7735.5898.8%
48L conv1x1 1024ch 64²18.4536.0194.8% (W8A8)
Measured fp16 throughput, M4, unthrottled 05101520 single matmul 1024²5.2single matmul 2048²5.732L conv1x1 512ch 128²18.6764L conv1x1 512ch 128²18.7748L conv1x1 1024ch 64²18.45 19 TFLOPS ceiling W8A8 dual-pack: 38 TOPS TFLOPS
Single matmuls reach 5.2–5.7 TFLOPS. Conv1x1 chains of 32 layers or more at 512–1024 channels reach 18.45–18.77 TFLOPS, within 1.2% of the measured fp16 ceiling.

FP16 throughput saturates near 19 TFLOPS. Two packed int8 products per lane give a corresponding rate of 38 TOPS; the highest measured W8A8 result is 36.01 TOPS.

INT8: the W8A8 path

The M4 ANE runs W8A8 convolution chains in a packed mode. Both weights and the intermediate activations must be int8. Int8 weights with fp16 activations stay on the fp16 compute path; keeping the edges between convolutions in int8 selects the packed path.

At MIL level, the compiler is given int8 weights through constexpr_affine_dequantize and an int8 quantize/dequantize pair between consecutive convolutions. The pair describes the precision of the connecting edge. It does not add an intermediate DRAM store: the compiled program carries the quantized value directly into the next block.

WeightsActivationsMeasuredMechanism
fp16fp161.00× baselinefp16 compute, 2-byte inter-layer transfers
int8fp161.00×weights expanded before compute; chain remains fp16
int8int8up to 1.95×packed int8 compute, 1-byte inter-layer transfers

The benchmark counts the same convolution operations in both modes: 2 × channels² × kernel² × spatial² × depth. Deep fp16 chains reach 18.77 TFLOPS. Deep W8A8 chains reach 36.01 TOPS. The measured rates correspond to one fp16 product or two int8 products per compute lane per cycle.

FP16 and W8A8 throughput by chain depth Measured conv1x1 throughput using the same operation-count formula 10 20 30 40 38 TOPS · packed W8A8 rate throughput available in packed mode 19 TFLOPS · fp16 rate 0 5.4 5.9 L=1 9.8 12.4 L=4 12.3 18.6 L=8 11.7 22.4 L=16 16.1 27.4 L=32 18.5 36.0 L=48* chain depth (layers) fp16 W8A8 packed-mode range conv1x1 chains · L=1–32 at 512ch 32² · L=48* at 1024ch 64² · median of 100–400 evals
A one-layer program has no quantized internal edge, so its fp16 and W8A8 timings are nearly equal. Packed middle blocks appear in deeper W8A8 chains. Throughput rises to 36.01 TOPS at 48 layers; the corresponding fp16 chain reaches 18.45 TFLOPS.

The compiled chain has an fp16 boundary at each end and int8 edges inside it. The entry block accepts fp16 input, performs the first convolution, and writes an int8 intermediate. Middle blocks consume and produce int8. The final block consumes int8 and writes the requested fp16 output.

BlockMode wordDMA configRole
Entry (block 0)0x934180050x80041240fp16 input to quantized intermediate
Middle (1 … n-2)0xb14180050x80049240packed int8 compute with one-byte transfers
Output (n-1)0x91418005conversion to the output format

Three parts of the HWX record change together. The compute mode becomes 0xb1418005 in a middle block. The inter-layer DMA word becomes 0x80049240, selecting a one-byte edge instead of the fp16 form 0x80041240. The block grows by four configuration words, including quantization control and scale. Changing only the mode or only the DMA word does not produce a valid W8A8 block.

Direct HWX controlHardware resultField role
Compiler W8A8 stream0 mismatchesReference packed path
Middle mode 0xb1418005 → 0x93418005Program runs; 1,976,199 of 2,097,152 outputs differArithmetic and scheduling mode
Either edge DMA 0x80049240 → 0x80041240Load succeeds; evaluation returns 0x15Quantized inter-block transfer

The mode-only edit also changes the three-layer run from 371.4 µs to 703.9 µs. The program still loads and executes, so the word is active hardware configuration rather than descriptive metadata. Reverting either quantized DMA edge stops evaluation before an output is produced.

A direct HWX emitter must update the complete block: compute mode, both edge-DMA records, quantization words, scales, and the depth-dependent command metadata in the container. The field mutations above establish direct access to the packed path. A geometry-independent W8A8 constructor still requires the remaining container metadata to be mapped.

How a W8A8 convolution chain stays quantized block 0 mode 0x93 fp16 input int8 weights+ scale entryconvolution quantizeint8 edge blocks 1…n-2 mode 0xb1 int8 input int8 wt+ int8 act packed MAC2 int8 products int8 out inter-tile DMA: 1 B/elem (0x80049240) packed compute · 1-byte inter-layer values · consecutive quantized blocks remain int8 block n-1 mode 0x91 int8 input finalconvolution fp16 out internal edges: 1 byte per element · entry and output remain fp16-facing measured deep-chain rate: 36.01 TOPS W8A8 · 18.77 TFLOPS fp16
The fp16 input is quantized after the entry convolution. Middle blocks keep activations on one-byte edges and use packed int8 compute. The final block writes fp16 output. Int8 weights alone do not create this internal path.

A single convolution has no quantized internal edge. At 1024 channels and 32² spatial dimensions, the int8-weight version takes 260.4 µs and the fp16 version takes 260.5 µs. Packed execution starts inside a chain, after one convolution produces an int8 activation for the next. W8-only programs therefore measure 1.00× while deep W8A8 chains approach 2×.

FINDINGW8A8 is a chain property. Middle blocks use mode 0xb1418005, one-byte edges use DMA word 0x80049240, and both records are required. Int8 weights without int8 activation edges stay on the fp16 path.

Power and efficiency

StateThroughputANE PowerEfficiency
Idle00 mWHard power gate
FP16 peak18.77 TFLOPS4.57 W4.1 TFLOPS/W
W8A8 peak36.01 TOPS12.25 W2.9 TOPS/W

The ANE draws exactly 0 mW at idle and powers on for each submission. W8A8 provides 1.95× the throughput while drawing 2.7× the power. FP16 is more efficient per counted operation; W8A8 completes more operations per second and uses narrower intermediate tensors. These measurements do not isolate how much of the additional power belongs to the compute array or the surrounding memory system.

Geometry matters

A shallow-channel tensor leaves much of the compute array idle. SpaceToDepth can move the computation into the 512–1024 channel range; DepthToSpace restores the original layout afterward:

Pipeline (32 layers)FP16 TFLOPSW8A8 TOPS
Native 64ch 128²3.926.62
S2D(4) → 1024ch 32² → D2S16.3731.97
Speedup4.18x4.83x
Channels are what the array eats 64 ch × 128 × 128 wide, shallow compute sets busy 1 of 8 3.92 TFLOPS SpaceToDepth(4) DMA reshuffle, ~free block 2 lowers to a conv: avoid it 1024 ch × 32 × 32 narrow, deep compute sets busy 8 of 8 16.37 same FLOPs, same 32 layers, same weights 4.18× faster end to end, S2D and D2S included · W8A8: 6.62 → 31.97 TOPS, 4.83× the compiler splits output channels across compute sets, so a 64-channel layer can never occupy more than a fraction of the array
The array parallelizes over output channels. A shallow tensor leaves most compute sets idle even when its spatial extent is large. SpaceToDepth trades spatial dimensions for channels and places more of the array in use.

S2D/D2S overhead is 0.3-1.4% of the pipeline. Avoid block_size=2: the compiler lowers it as a convolution with one-hot weights (2.8-3.1% overhead) rather than a DMA reshuffle.

FINDINGThe array parallelizes over channels. SpaceToDepth(4) on a 64-channel tensor lifted a 32-layer chain from 3.92 to 16.37 TFLOPS, 4.18× end to end.

ANE vs GPU vs CPU

The CPU, GPU, and ANE occupy different operating ranges on the M4.

ANEGPU (Metal)CPU (SME)
Peak throughput19 TFLOPS fp16~3.6 TFLOPS fp163.9 TOPS int8
Power at peak4.6 W~8-12 W~5-8 W
Dispatch overhead~90 µs (XPC)~0 (in-flight)0 (inline)
ProgrammingMIL graph → binaryMetal shadersInline assembly
FlexibilityFixed op setArbitrary computeArbitrary code
I/OIOSurfaceMTLBufferStack/heap

For supported operations large enough to amortize the 90 µs dispatch, the ANE measures 5× faster than the GPU and 8–13× more power-efficient than the CPU. The GPU supports a broader operation set and can add work to an active command stream. The CPU runs arbitrary code without the ANE submission cost.

Where the 90 µs dispatch floor stops mattering 0100200300400 0100200300 work in the op, measured as ANE compute time (µs) wall time (µs) crossover ≈ 23 µs of ANE work (≈ 113 µs on the CPU) ANE: 90 µs + t CPU / SME: ≈ 4.9 t CPU wins ANE wins
The crossover model uses two measured inputs: an approximately 90 µs ANE dispatch floor and a 4.9× peak-throughput ratio between the ANE and the CPU matrix unit. Under these assumptions, the crossover occurs near 23 µs of ANE compute. Fusion moves workloads toward the range where the ANE amortizes dispatch.

For LLM inference, prefill provides the large matrix operations that suit the ANE. Token-by-token decode contains smaller operations for which the 90 µs submission cost can dominate, making CPU/SME execution more suitable. In our on-device training path, the ANE runs forward and backward matmuls at 2.8–4.6 W; the CPU performs weight updates, masked attention, and the Adam optimizer.

FINDINGANE for prefill and anything over ~25 µs of matrix work; CPU/SME for single-token decode where the 90 µs floor would dominate.

The M6: Dual ANE

Apple announced the 2 nm M6 on August 25, 2026. It contains two 16-core Neural Engines, and Apple states that system frameworks can use both simultaneously. The published claim is twice the peak compute of the preceding configuration.

Apple did not publish a TOPS figure for the M6 ANE. If each engine matches the M4's 19 TFLOPS fp16 rate, the pair would provide 38 TFLOPS fp16 or 76 TOPS in packed W8A8 mode. This is an extrapolation, not an M6 measurement. Apple has not disclosed whether the engines share SRAM, coordinate DMA, or operate independently. DRAM bandwidth rises from 120 GB/s on M4 to 170 GB/s on M6, an increase of 42%.

M4 · 3 nm M6 · 2 nm · announced Aug 25 2026 ANE16 cores · H16G19 TFLOPS · 4.6 W DRAM · 120 GB/smeasured: everything in this article one dispatch path · ≈90 µs ANE 016 cores19 TFLOPS? ANE 116 cores19 TFLOPS? ? DRAM · 170 GB/s (+42%)Apple: "2× peak compute" · no TOPS figure published open: shared SRAM? one dispatch or two? can one compiled graph span both engines? expected if independent: 38 TFLOPS fp16 · 76 TOPS W8A8
Apple specifies two 16-core Neural Engines, twice the peak compute, and simultaneous use through system frameworks. The connection between the engines, their SRAM organization, and their dispatch paths remain undocumented.

The unresolved architectural questions concern graph placement, memory, and dispatch. A graph may span both engines or be partitioned into independent subgraphs. Each engine may have its own 2 MiB operand threshold and 64 KiB kernel store, or those resources may be shared. The dispatch path may submit both engines together or pay the host-side cost separately.

Apple also added "Neural Accelerators" to the M6 GPU cores, claiming a 30% improvement over M5 for GPU-side AI work. These are separate from the ANE and appear to be small fixed-function units within the GPU pipeline, similar to NVIDIA's tensor cores. Whether they share the same ISA as the ANE or use their own encoding is unknown.

No M6 Pro or Max variant was announced with the M6. A dual-engine base chip leaves open whether later variants will scale ANE count with die area. Counts beyond the announced pair are speculative.

We do not have an M6 to test. The numerical estimates above extrapolate from M4 measurements and Apple's published specifications. Hardware inspection would begin with the HAT, followed by single- and dual-engine dispatch measurements and an overlap test.

FINDINGM6 ships two 16-core engines and a 42% wider memory bus. Apple has said "2× peak" and nothing about how they coordinate.

Limits and Open Questions

Measured boundaries

BoundaryM4 result
Compiled graph depthConvolution chains through 383 blocks compile, load, and evaluate with 128 KiB tensors. At 384 blocks, ANECompilerService disconnects before producing HWX.
Numeric formatsThe mapped compute paths are fp16 and packed W8A8. The H16G E4M3 capability gate is zero, and the current compiler exposes no FP8 path.
Control flowHWX contains DMA, compute, synchronization, and termination records. It has no decoded branch or predicate record. Procedure selection happens before dispatch.
Host submissionEach evaluation carries an approximately 90 µs host-side floor. Two concurrent requests save about 16% end to end, from overlapping host/runtime work rather than doubling engine throughput.

Still unknown

Maximum hardware program size. The compiler stops first at 384 convolution blocks, leaving the physical HWX limit above 383 unmeasured.

Internal overlap. A combined matmul and independent ReLU saved 19.5 µs after subtracting one dispatch, 1.1% of the shorter operation. Eight independent adds were 1.0083× faster than eight chained adds. These are graph-level timings; the overlap between MAC, planar, and DMA engines inside a Task Descriptor is still unresolved.

DMA topology. Tests expose three tile-DMA references and one kernel-DMA reference in compiled programs. The number of physical engines and their queue depth are unknown. A sweep from two to eight input planes serialized into additional Task Descriptors and therefore measured the compiler's schedule rather than the hardware's DMA width.

Lane count and clock. The aggregate rates are 19 TFLOPS for fp16 and 38 TOPS for packed W8A8. The HAT's 8/4/2/1 values describe accumulator tiling. The active clock and the number of output channels produced per cycle have not been isolated.

Synchronization scope. WAIT and WAIT_EXT are required by dependent programs. Their exact targets, scope, and latency are not yet mapped.


The M4 ANE is a fixed-function matrix accelerator with 8 compute sets, 64 banks of on-chip SRAM, and a compiler-managed streaming model built around a 2 MiB per-operand threshold. It reaches 19 TFLOPS in fp16 and 38 TOPS in packed W8A8 mode. Peak fp16 power is 4.57 W, and the engine draws 0 mW when idle. Programs use a fixed operation set with no branching, and every submission includes an approximately 90 µs host-side dispatch cost. Apple keeps the compiler, program format, and driver interface private.

The M6 doubles the engine count. Whether that is two independent accelerators or a coordinated pair remains to be measured.


References

  1. Spencer H. Bryngelson. Apple Neural Engine: Architecture, Programming, and Performance. arXiv:2606.22283, June 2026. arxiv.org/abs/2606.22283
  2. Ramchand Kumaresan. Orion: Characterizing and Programming Apple's Neural Engine for LLM Training and Inference. arXiv:2603.06728, March 2026. arxiv.org/abs/2603.06728
  3. Spencer H. Bryngelson. ANEForge: Python for direct computation on the Apple Neural Engine. arXiv:2606.17090, June 2026. arxiv.org/abs/2606.17090
  4. Manjeet Singh. Inside the M4 Apple Neural Engine, Parts 1-3. maderix.substack.com, 2026. Part 1, Part 2, Part 3
  5. Dougall Johnson. Apple G13 GPU Architecture. github.com/dougallj/applegpu, 2021-present.
  6. Apple Inc. Apple introduces M6 and M5 Ultra for a big leap in performance and AI compute. August 25, 2026. apple.com/newsroom