v2: use MPSMatrixMultiplication for matmul in EmbeddingGemma
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import Metal
|
import Metal
|
||||||
|
import MetalPerformanceShaders
|
||||||
import Accelerate
|
import Accelerate
|
||||||
|
|
||||||
/// EmbeddingGemma configuration
|
/// EmbeddingGemma configuration
|
||||||
@@ -80,15 +81,15 @@ public final class EmbeddingGemmaModel: @unchecked Sendable {
|
|||||||
try loadBuffer("\(p).post_attention_layernorm.weight"),
|
try loadBuffer("\(p).post_attention_layernorm.weight"),
|
||||||
try loadBuffer("\(p).post_feedforward_layernorm.weight"),
|
try loadBuffer("\(p).post_feedforward_layernorm.weight"),
|
||||||
])
|
])
|
||||||
qProjs.append(try loadBuffer("\(p).self_attn.q_proj.weight"))
|
qProjs.append(try loadAndTranspose("\(p).self_attn.q_proj.weight", rows: config.hiddenSize, cols: config.hiddenSize))
|
||||||
kProjs.append(try loadBuffer("\(p).self_attn.k_proj.weight"))
|
kProjs.append(try loadAndTranspose("\(p).self_attn.k_proj.weight", rows: config.numKeyValueHeads * config.headDim, cols: config.hiddenSize))
|
||||||
vProjs.append(try loadBuffer("\(p).self_attn.v_proj.weight"))
|
vProjs.append(try loadAndTranspose("\(p).self_attn.v_proj.weight", rows: config.numKeyValueHeads * config.headDim, cols: config.hiddenSize))
|
||||||
oProjs.append(try loadBuffer("\(p).self_attn.o_proj.weight"))
|
oProjs.append(try loadAndTranspose("\(p).self_attn.o_proj.weight", rows: config.numAttentionHeads * config.headDim, cols: config.hiddenSize))
|
||||||
qNorms.append(try loadBuffer("\(p).self_attn.q_norm.weight"))
|
qNorms.append(try loadBuffer("\(p).self_attn.q_norm.weight"))
|
||||||
kNorms.append(try loadBuffer("\(p).self_attn.k_norm.weight"))
|
kNorms.append(try loadBuffer("\(p).self_attn.k_norm.weight"))
|
||||||
gateProjs.append(try loadBuffer("\(p).mlp.gate_proj.weight"))
|
gateProjs.append(try loadAndTranspose("\(p).mlp.gate_proj.weight", rows: config.intermediateSize, cols: config.hiddenSize))
|
||||||
upProjs.append(try loadBuffer("\(p).mlp.up_proj.weight"))
|
upProjs.append(try loadAndTranspose("\(p).mlp.up_proj.weight", rows: config.intermediateSize, cols: config.hiddenSize))
|
||||||
downProjs.append(try loadBuffer("\(p).mlp.down_proj.weight"))
|
downProjs.append(try loadAndTranspose("\(p).mlp.down_proj.weight", rows: config.hiddenSize, cols: config.intermediateSize))
|
||||||
}
|
}
|
||||||
let fnData = try readTensor("norm.weight")
|
let fnData = try readTensor("norm.weight")
|
||||||
finalNorm = engine.device.makeBuffer(bytes: fnData, length: fnData.count * 4)!
|
finalNorm = engine.device.makeBuffer(bytes: fnData, length: fnData.count * 4)!
|
||||||
@@ -161,6 +162,18 @@ public final class EmbeddingGemmaModel: @unchecked Sendable {
|
|||||||
return engine.device.makeBuffer(bytes: data, length: data.count * 4)!
|
return engine.device.makeBuffer(bytes: data, length: data.count * 4)!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func loadAndTranspose(_ name: String, rows: Int, cols: Int) throws -> MTLBuffer {
|
||||||
|
// Load [rows, cols] and transpose to [cols, rows] for MPS matmul C = A × B
|
||||||
|
let data = try readTensor(name)
|
||||||
|
var transposed = [Float](repeating: 0, count: data.count)
|
||||||
|
for r in 0..<rows {
|
||||||
|
for c in 0..<cols {
|
||||||
|
transposed[c * rows + r] = data[r * cols + c]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return engine.device.makeBuffer(bytes: transposed, length: transposed.count * 4)!
|
||||||
|
}
|
||||||
|
|
||||||
private func lookupEmbeddings(tokens: [Int], cmdBuf: MTLCommandBuffer) throws -> MTLBuffer {
|
private func lookupEmbeddings(tokens: [Int], cmdBuf: MTLCommandBuffer) throws -> MTLBuffer {
|
||||||
let seqLen = tokens.count, hs = config.hiddenSize
|
let seqLen = tokens.count, hs = config.hiddenSize
|
||||||
let buf = engine.device.makeBuffer(length: seqLen * hs * 4)!
|
let buf = engine.device.makeBuffer(length: seqLen * hs * 4)!
|
||||||
@@ -196,22 +209,24 @@ public final class EmbeddingGemmaModel: @unchecked Sendable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func matmulSeq(input: MTLBuffer, weight: MTLBuffer, output: MTLBuffer, m: Int, k: Int, n: Int, cmdBuf: MTLCommandBuffer) throws {
|
private func matmulSeq(input: MTLBuffer, weight: MTLBuffer, output: MTLBuffer, m: Int, k: Int, n: Int, cmdBuf: MTLCommandBuffer) throws {
|
||||||
let enc = cmdBuf.makeComputeCommandEncoder()!
|
// Use MPS for optimized matrix multiplication on Apple Silicon
|
||||||
defer { enc.endEncoding() }
|
// Weight is stored transposed [k, n] for C = A × B
|
||||||
let pso = try engine.pipeline(named: "matmul_f32")
|
let descA = MPSMatrixDescriptor(rows: m, columns: k, rowBytes: k * 4, dataType: .float32)
|
||||||
enc.setComputePipelineState(pso)
|
let descB = MPSMatrixDescriptor(rows: k, columns: n, rowBytes: n * 4, dataType: .float32)
|
||||||
for i in 0..<m {
|
let descC = MPSMatrixDescriptor(rows: m, columns: n, rowBytes: n * 4, dataType: .float32)
|
||||||
let inputOffset = i * k * 4
|
let matA = MPSMatrix(buffer: input, descriptor: descA)
|
||||||
let outputOffset = i * n * 4
|
let matB = MPSMatrix(buffer: weight, descriptor: descB)
|
||||||
enc.setBuffer(input, offset: inputOffset, index: 0)
|
let matC = MPSMatrix(buffer: output, descriptor: descC)
|
||||||
enc.setBuffer(weight, offset: 0, index: 1)
|
|
||||||
enc.setBuffer(output, offset: outputOffset, index: 2)
|
let matMul = MPSMatrixMultiplication(device: engine.device,
|
||||||
var mm: UInt32 = 1, kk = UInt32(k), nn = UInt32(n)
|
transposeLeft: false,
|
||||||
enc.setBytes(&mm, length: 4, index: 3)
|
transposeRight: false,
|
||||||
enc.setBytes(&kk, length: 4, index: 4)
|
resultRows: m,
|
||||||
enc.setBytes(&nn, length: 4, index: 5)
|
resultColumns: n,
|
||||||
enc.dispatchThreads(MTLSize(width: n, height: 1, depth: 1), threadsPerThreadgroup: MTLSize(width: min(256, n), height: 1, depth: 1))
|
interiorColumns: k,
|
||||||
}
|
alpha: 1.0,
|
||||||
|
beta: 0.0)
|
||||||
|
matMul.encode(commandBuffer: cmdBuf, leftMatrix: matA, rightMatrix: matB, resultMatrix: matC)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func eltwiseAdd(a: MTLBuffer, b: MTLBuffer, output: MTLBuffer, count: Int, cmdBuf: MTLCommandBuffer) throws {
|
private func eltwiseAdd(a: MTLBuffer, b: MTLBuffer, output: MTLBuffer, count: Int, cmdBuf: MTLCommandBuffer) throws {
|
||||||
|
|||||||
Reference in New Issue
Block a user