Initial commit: E4B-MarkBase model integration with passing tests
CI / build-and-test (push) Has been cancelled
CI / build-and-test (push) Has been cancelled
- E4B-MarkBase model (42 layers, 4.4GB) loaded successfully - All Phase 1-6 tests passed (model loading, forward pass, vision/audio towers, token generation, performance) - All stress tests passed (5/5 in 127.6s) - Concurrent inference - Memory stress (67.5 tok/s, 0 NaN) - Continuous generation - Batch processing - Long-running stability - Swift Metal inference engine with multimodal support
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
import XCTest
|
||||
@testable import MarkBase
|
||||
|
||||
final class BatchGenerationTest: XCTestCase {
|
||||
|
||||
func testBatchGenerationPerformance() throws {
|
||||
print("\n═══════════════════════════════════════════════════════════════════")
|
||||
print(" Batch Generation Performance Test")
|
||||
print("═══════════════════════════════════════════════════════════════════\n")
|
||||
|
||||
let modelDir = "/Users/accusys/MarkBaseEngine/models/E4B-MarkBase"
|
||||
let engine = try MarkBaseEngine(autoCompile: true)
|
||||
let textModel = try E4BModel(modelDir: modelDir, engine: engine, maxContextLength: 256)
|
||||
|
||||
print("Model: \(textModel.numHiddenLayers) layers, hidden=\(textModel.hiddenSize)\n")
|
||||
|
||||
// Warm up
|
||||
print("Warm up...")
|
||||
_ = try textModel.forwardOptimized(tokenId: 2, position: 0)
|
||||
print(" ✓ Warm up complete\n")
|
||||
|
||||
// Test 1: Sequential generation (baseline)
|
||||
print("Test 1: Sequential generation (10 tokens)")
|
||||
let seqStart = Date()
|
||||
var seqTokens: [Int] = [2]
|
||||
for _ in 0..<10 {
|
||||
let logits = try textModel.forwardOptimized(
|
||||
tokenId: seqTokens.last!,
|
||||
position: seqTokens.count - 1
|
||||
)
|
||||
var maxIdx = 0
|
||||
var maxLogit = logits[0]
|
||||
for i in 1..<logits.count {
|
||||
if logits[i] > maxLogit {
|
||||
maxLogit = logits[i]
|
||||
maxIdx = i
|
||||
}
|
||||
}
|
||||
seqTokens.append(maxIdx)
|
||||
}
|
||||
let seqTime = Date().timeIntervalSince(seqStart) * 1000
|
||||
print(" ✓ Generated 10 tokens in \(seqTime) ms")
|
||||
print(" Average: \(seqTime / 10) ms/token")
|
||||
print(" Tokens: \(seqTokens)")
|
||||
|
||||
// Test 2: Batch generation
|
||||
print("\nTest 2: Batch generation (10 tokens in batches of 8)")
|
||||
let batchStart = Date()
|
||||
let batchTokens = try textModel.generateBatch(startToken: 2, numTokens: 10)
|
||||
let batchTime = Date().timeIntervalSince(batchStart) * 1000
|
||||
print(" ✓ Generated 10 tokens in \(batchTime) ms")
|
||||
print(" Average: \(batchTime / 10) ms/token")
|
||||
print(" Tokens: \(batchTokens)")
|
||||
|
||||
// Comparison
|
||||
print("\n═══════════════════════════════════════════════════════════════════")
|
||||
let speedup = seqTime / batchTime
|
||||
let improvement = (seqTime - batchTime) / seqTime * 100
|
||||
print("Comparison:")
|
||||
print(" Sequential: \(seqTime) ms (\(seqTime/10) ms/token)")
|
||||
print(" Batch: \(batchTime) ms (\(batchTime/10) ms/token)")
|
||||
print(" Speedup: \(speedup)x")
|
||||
print(" Improvement: \(improvement)%")
|
||||
|
||||
if speedup > 1.0 {
|
||||
print("\n✓ Batch generation is faster!")
|
||||
} else {
|
||||
print("\n⚠ Batch generation needs optimization")
|
||||
}
|
||||
print("═══════════════════════════════════════════════════════════════════\n")
|
||||
}
|
||||
|
||||
func testSingleVsBatchComparison() throws {
|
||||
print("\n═══════════════════════════════════════════════════════════════════")
|
||||
print(" Single vs Batch Token Generation")
|
||||
print("═══════════════════════════════════════════════════════════════════\n")
|
||||
|
||||
let modelDir = "/Users/accusys/MarkBaseEngine/models/E4B-MarkBase"
|
||||
let engine = try MarkBaseEngine(autoCompile: true)
|
||||
let textModel = try E4BModel(modelDir: modelDir, engine: engine, maxContextLength: 256)
|
||||
|
||||
// Warm up
|
||||
print("Warm up...")
|
||||
_ = try textModel.forwardOptimized(tokenId: 2, position: 0)
|
||||
_ = try textModel.forwardBatch(tokenIds: [2], positions: [0])
|
||||
print(" ✓ Warm up complete\n")
|
||||
|
||||
// Test single token
|
||||
print("Test 1: Single token generation")
|
||||
var singleTimes: [Double] = []
|
||||
for _ in 0..<5 {
|
||||
let start = Date()
|
||||
let logits = try textModel.forwardOptimized(tokenId: 2, position: 0)
|
||||
let elapsed = Date().timeIntervalSince(start) * 1000
|
||||
singleTimes.append(elapsed)
|
||||
XCTAssertFalse(logits.contains { $0.isNaN }, "Single logits should not have NaN")
|
||||
}
|
||||
let singleAvg = singleTimes.reduce(0, +) / Double(singleTimes.count)
|
||||
print(" Average: \(singleAvg) ms")
|
||||
|
||||
// Test batch of 1
|
||||
print("\nTest 2: Batch of 1 token")
|
||||
var batch1Times: [Double] = []
|
||||
for _ in 0..<5 {
|
||||
let start = Date()
|
||||
let logits = try textModel.forwardBatch(tokenIds: [2], positions: [0])
|
||||
let elapsed = Date().timeIntervalSince(start) * 1000
|
||||
batch1Times.append(elapsed)
|
||||
XCTAssertFalse(logits[0].contains { $0.isNaN }, "Batch logits should not have NaN")
|
||||
}
|
||||
let batch1Avg = batch1Times.reduce(0, +) / Double(batch1Times.count)
|
||||
print(" Average: \(batch1Avg) ms")
|
||||
|
||||
// Test batch of 4
|
||||
print("\nTest 3: Batch of 4 tokens")
|
||||
var batch4Times: [Double] = []
|
||||
for _ in 0..<5 {
|
||||
let start = Date()
|
||||
let logits = try textModel.forwardBatch(tokenIds: [2, 2, 2, 2], positions: [0, 1, 2, 3])
|
||||
let elapsed = Date().timeIntervalSince(start) * 1000
|
||||
batch4Times.append(elapsed)
|
||||
for l in logits {
|
||||
XCTAssertFalse(l.contains { $0.isNaN }, "Batch logits should not have NaN")
|
||||
}
|
||||
}
|
||||
let batch4Avg = batch4Times.reduce(0, +) / Double(batch4Times.count)
|
||||
print(" Average: \(batch4Avg) ms")
|
||||
print(" Per-token: \(batch4Avg / 4) ms")
|
||||
|
||||
print("\n═══════════════════════════════════════════════════════════════════")
|
||||
print("Results:")
|
||||
print(" Single: \(singleAvg) ms")
|
||||
print(" Batch(1): \(batch1Avg) ms")
|
||||
print(" Batch(4): \(batch4Avg) ms (\(batch4Avg/4) ms/token)")
|
||||
|
||||
let batch4Speedup = (singleAvg * 4) / batch4Avg
|
||||
print(" Batch(4) speedup vs 4x single: \(batch4Speedup)x")
|
||||
print("═══════════════════════════════════════════════════════════════════\n")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user