Section 12/171 menit

12. Profiling dengan Instruments dan Core ML Profiler

12. Profiling dengan Instruments dan Core ML Profiler

Instruments: Core ML Profiler

Xcode 14+ menyertakan template Core ML Profiler di Instruments:

swift
Xcode → Product → Profile (Command+I)
→ Instruments template: Core ML

Instrument ini menampilkan:

  • Model loading time: berapa lama MLModel.load() membutuhkan waktu
  • Prediction latency: per-prediksi, breakdown per-layer
  • Compute unit utilization: berapa % ANE, GPU, CPU
  • Memory footprint: peak dan average selama inferensi

Profiling dengan os_signpost

Untuk profiling kustom yang terintegrasi dengan Instruments:

swift
import os

// MARK: - Instrumented Inference

final class InstrumentedModel {
    private let model: MLModel
    private let log = OSLog(subsystem: "com.yourapp", category: .pointsOfInterest)
    private let inferenceSignpost = OSSignpostID(log: OSLog(subsystem: "com.yourapp", category: .pointsOfInterest))

    init(model: MLModel) {
        self.model = model
    }

    func predict(input: MLFeatureProvider) throws -> MLFeatureProvider {
        let spid = OSSignpostID(log: log)

        // Mark mulai inferensi
        os_signpost(.begin, log: log, name: "CoreML Inference", signpostID: spid,
                    "model=%{public}s", "MyModel")

        defer {
            os_signpost(.end, log: log, name: "CoreML Inference", signpostID: spid)
        }

        // Mark preprocessing
        os_signpost(.event, log: log, name: "Preprocessing", signpostID: spid)

        let result = try model.prediction(from: input)

        // Mark postprocessing
        os_signpost(.event, log: log, name: "Postprocessing", signpostID: spid)

        return result
    }
}

Latency Benchmarking

swift
// Benchmark tool untuk mengukur latency dan throughput
final class CoreMLBenchmark {
    struct BenchmarkResult {
        let warmupLatencies: [TimeInterval]
        let steadyStateLatencies: [TimeInterval]
        let averageLatency: TimeInterval
        let p95Latency: TimeInterval
        let p99Latency: TimeInterval
        let throughputPerSecond: Double
    }

    func benchmark(
        model: MLModel,
        sampleInput: MLFeatureProvider,
        warmupRuns: Int = 5,
        benchmarkRuns: Int = 100
    ) throws -> BenchmarkResult {
        var warmupLatencies: [TimeInterval] = []
        var steadyLatencies: [TimeInterval] = []

        // Warmup — biarkan JIT dan cache warming terjadi
        for _ in 0..<warmupRuns {
            let start = CFAbsoluteTimeGetCurrent()
            _ = try model.prediction(from: sampleInput)
            warmupLatencies.append(CFAbsoluteTimeGetCurrent() - start)
        }

        // Actual benchmark
        for _ in 0..<benchmarkRuns {
            let start = CFAbsoluteTimeGetCurrent()
            _ = try model.prediction(from: sampleInput)
            steadyLatencies.append(CFAbsoluteTimeGetCurrent() - start)
        }

        let sorted = steadyLatencies.sorted()
        let avg = sorted.reduce(0, +) / Double(sorted.count)
        let p95 = sorted[Int(Double(sorted.count) * 0.95)]
        let p99 = sorted[Int(Double(sorted.count) * 0.99)]
        let totalTime = steadyLatencies.reduce(0, +)
        let throughput = Double(benchmarkRuns) / totalTime

        return BenchmarkResult(
            warmupLatencies: warmupLatencies,
            steadyStateLatencies: steadyLatencies,
            averageLatency: avg,
            p95Latency: p95,
            p99Latency: p99,
            throughputPerSecond: throughput
        )
    }

    func printReport(_ result: BenchmarkResult) {
        print("=== Core ML Benchmark Report ===")
        print("Average latency:   \(String(format: "%.2f", result.averageLatency * 1000))ms")
        print("P95 latency:       \(String(format: "%.2f", result.p95Latency * 1000))ms")
        print("P99 latency:       \(String(format: "%.2f", result.p99Latency * 1000))ms")
        print("Throughput:        \(String(format: "%.1f", result.throughputPerSecond)) predictions/sec")
        print("Warmup overhead:   \(String(format: "%.2f", result.warmupLatencies.first ?? 0 * 1000))ms")
    }
}