import Metal // ══════════════════════════════════════════════════════════════════ // Batch Layer Processing - TRUE parallel layer forward pass // Process multiple tokens through entire layer simultaneously // ══════════════════════════════════════════════════════════════════ extension E4BLayer { /// Batch forward pass - process N tokens through entire layer in parallel /// Expected: 8-15x speedup for batch inference public func forwardBatchTrue( batchInput: MTLBuffer, // [batchSize, hiddenSize] positions: [Int], batchSize: Int, kvCache: KVCache, shouldStoreKV: Bool, temps: ForwardTemps, batchTemps: BatchTemps, // Batch-specific buffers engine: MarkBaseEngine, cmdBuf: MTLCommandBuffer ) throws { // Note: This is a simplified implementation focusing on FFN batch processing // Attention still needs sequential KV cache updates // ── Phase 1: Batch Input Norm ── guard let inputLN = inputLayernorm else { throw NSError(domain: "LayerBatch", code: -1, userInfo: [NSLocalizedDescriptionKey: "inputLayernorm required for batch processing"]) } try batchLayerRMSNorm( batchInput: batchInput, weights: inputLN, batchOutput: batchTemps.hBatch, hiddenSize: config.hiddenSize, batchSize: batchSize, cmdBuf: cmdBuf, engine: engine ) // ── Phase 2: Batch Attention (Sequential for KV cache) ── // Note: Attention needs per-token KV cache updates, so we process sequentially // But we can batch Q/K/V projections try batchQuantizedMatmul( batchInput: batchTemps.hBatch, weights: qProj, batchOutput: batchTemps.qBatch, batchSize: batchSize, cmdBuf: cmdBuf, engine: engine ) // Batch grouped norm for Q guard let qN = qNorm else { throw NSError(domain: "LayerBatch", code: -2, userInfo: [NSLocalizedDescriptionKey: "qNorm required for batch processing"]) } try batchGroupedRMSNorm( batchInput: batchTemps.qBatch, weights: qN, batchOutput: batchTemps.nsBatch, count: config.nHeads * config.headDim, groupSize: config.headDim, batchSize: batchSize, cmdBuf: cmdBuf, engine: engine ) // Sequential RoPE and attention (KV cache dependency) for i in 0..