Section 12/201 menit

12. Optimasi Performa: Compute Units & Batching

12. Optimasi Performa: Compute Units & Batching

Memilih Compute Units yang Tepat

swift
// Core ML: benchmark compute units untuk model specific

import CoreML

final class ComputeUnitBenchmark {
    static func benchmark(modelURL: URL, input: MLFeatureProvider) async {
        let configurations: [(String, MLComputeUnits)] = [
            ("CPU Only", .cpuOnly),
            ("CPU + GPU", .cpuAndGPU),
            ("CPU + Neural Engine", .cpuAndNeuralEngine),
            ("All (Auto)", .all)
        ]

        for (name, units) in configurations {
            let config = MLModelConfiguration()
            config.computeUnits = units

            do {
                let model = try MLModel(contentsOf: modelURL, configuration: config)

                let iterations = 50
                let start = CFAbsoluteTimeGetCurrent()

                for _ in 0..<iterations {
                    _ = try model.prediction(from: input)
                }

                let elapsed = (CFAbsoluteTimeGetCurrent() - start) / Double(iterations) * 1000
                print("\(name): \(String(format: "%.2f", elapsed))ms per inference")
            } catch {
                print("\(name): Error — \(error)")
            }
        }
    }
}

// Gunakan MLComputePlan untuk analisis lebih detail (iOS 17+)
func analyzeComputePlan(modelURL: URL) async throws {
    let config = MLModelConfiguration()
    config.computeUnits = .all

    let plan = try await MLComputePlan.load(contentsOf: modelURL, configuration: config)

    for (layer, deviceUsage) in plan.deviceUsage {
        print("Layer: \(layer), Device: \(deviceUsage)")
    }
}

Batch Processing untuk Throughput Tinggi

swift
// Core ML: batch prediction untuk throughput optimal

import CoreML

final class BatchImageClassifier {
    private let model: MLModel

    init(modelURL: URL) throws {
        let config = MLModelConfiguration()
        config.computeUnits = .all
        self.model = try MLModel(contentsOf: modelURL, configuration: config)
    }

    func classifyBatch(images: [UIImage]) async throws -> [String] {
        let BATCH_SIZE = 32  // Sesuaikan dengan memory budget

        var allResults: [String] = []

        // Proses dalam chunks untuk kontrol memory
        for chunk in images.chunked(into: BATCH_SIZE) {
            let providers: [MLFeatureProvider] = chunk.compactMap { image in
                guard let cgImage = image.cgImage,
                      let pixelBuffer = cgImage.toPixelBuffer(size: CGSize(width: 224, height: 224)) else {
                    return nil
                }
                return try? MLDictionaryFeatureProvider(dictionary: [
                    "image": MLFeatureValue(pixelBuffer: pixelBuffer)
                ])
            }

            let batchProvider = MLArrayBatchProvider(array: providers)
            let options = MLPredictionOptions()
            options.usesCPUOnly = false

            let results = try model.predictions(from: batchProvider, options: options)

            for i in 0..<results.count {
                let output = results.features(at: i)
                if let label = output.featureValue(for: "classLabel")?.stringValue {
                    allResults.append(label)
                }
            }
        }

        return allResults
    }
}

extension Array {
    func chunked(into size: Int) -> [[Element]] {
        stride(from: 0, to: count, by: size).map {
            Array(self[$0..<Swift.min($0 + size, count)])
        }
    }
}