Strategi verifikasi CI saat dependensi GPU sulit diandalkan berangkat dari satu kenyataan operasional: tidak semua perubahan kode layak menunggu runner GPU, dan tidak semua kegagalan di GPU harus memblokir seluruh tim. Jika workload Anda melibatkan training kecil, inferensi, preprocessing tensor, atau komponen CUDA/cuDNN, pendekatan CI yang efektif adalah memisahkan sinyal cepat dari validasi mahal.
Dalam konteks tren efisiensi komputasi yang sering dibahas sebagai “GPU bubble”, pertanyaan praktisnya bukan sekadar bagaimana memakai GPU lebih sedikit, tetapi bagaimana merancang workflow verifikasi yang tetap dipercaya ketika GPU mahal, lambat, atau tidak selalu tersedia. Jawabannya biasanya bukan satu test suite tunggal, melainkan piramida pengujian, matriks CI bertingkat, dataset emas kecil, dan aturan gagal/lolos yang sadar akan nondeterminisme.
Mengapa CI berbasis GPU sering rapuh
Pipeline yang menyentuh GPU cenderung lebih rapuh dibanding test CPU biasa karena beberapa faktor:
- Ketersediaan runner terbatas: antrean panjang, quota habis, atau runner preempted.
- Biaya per run lebih tinggi: tidak realistis menjalankan seluruh suite untuk setiap commit kecil.
- Nondeterminisme numerik: hasil inferensi atau training bisa sedikit berbeda antar device, driver, kernel, atau tingkat paralelisme.
- Ketergantungan lingkungan: image container, driver host, library native, dan model artifact harus cocok.
- Sinyal kegagalan ambigu: error bisa berasal dari kode aplikasi, model corrupt, OOM, timeout jaringan, atau mismatch environment.
Karena itu, target utama CI bukan “semua test berjalan di GPU”, melainkan mendapatkan sinyal yang cepat, relevan, dan proporsional terhadap risiko perubahan.
Gunakan test pyramid untuk workload GPU
Kesalahan umum adalah memindahkan seluruh validasi ke integration test GPU. Akibatnya, feedback lambat dan flaky. Pendekatan yang lebih sehat adalah menempatkan verifikasi dalam beberapa lapisan.
1. Unit test CPU sebagai fondasi
Bagian terbesar test suite sebaiknya tetap berjalan tanpa GPU. Fokuskan pada:
- logika bisnis di sekitar pipeline ML/inferensi,
- preprocessing dan postprocessing data,
- kontrak input-output fungsi,
- serialisasi konfigurasi,
- pemilihan device, fallback, dan handling error.
Jika kode Anda sulit diuji tanpa GPU, biasanya itu tanda bahwa abstraksi belum rapi. Pisahkan antarmuka runtime inferensi dari orchestration aplikasi.
2. Smoke test CPU untuk jalur end-to-end minimal
Smoke test CPU berguna untuk memastikan seluruh pipeline masih dapat berjalan secara dasar, meskipun lebih lambat atau memakai model kecil. Tujuannya bukan akurasi final, tetapi memverifikasi bahwa:
- model dapat dimuat,
- preprocessing tidak rusak,
- inferensi menghasilkan bentuk output yang benar,
- artifacts dan konfigurasi ditemukan di lokasi yang tepat.
Ini penting karena banyak regresi tidak benar-benar membutuhkan GPU untuk terdeteksi.
3. Integration test GPU yang sempit dan terarah
Jangan menjalankan semua skenario di GPU. Pilih beberapa test yang benar-benar menjawab risiko spesifik, misalnya:
- kernel/path eksekusi khusus GPU,
- penggunaan precision tertentu,
- batching dan memory management,
- kompatibilitas model dengan runtime target.
Set integration GPU yang baik biasanya kecil, cepat, dan stabil. Jika test GPU butuh 20 menit hanya untuk memastikan model bisa melakukan satu inferensi, sinyalnya terlalu mahal.
4. Nightly atau scheduled validation untuk cakupan berat
Test yang mahal seperti benchmark performa, evaluasi pada dataset lebih besar, atau validasi beberapa kombinasi hardware sebaiknya dipindahkan ke workflow terjadwal. Perubahan harian tetap mendapat sinyal cepat, sementara regresi mendalam tetap dipantau secara rutin.
Pemisahan smoke test CPU vs validasi GPU
Pemisahan ini adalah inti strategi verifikasi CI saat dependensi GPU sulit diandalkan. Secara praktis:
- Pull request: jalankan lint, unit test CPU, smoke test end-to-end CPU, dan mungkin satu GPU check opsional jika runner tersedia.
- Merge ke branch utama: jalankan subset GPU yang lebih ketat.
- Nightly: jalankan matriks GPU lengkap, evaluasi dataset kecil-menengah, serta cek performa.
- Release candidate: jalankan validasi penuh yang mendekati produksi.
Dengan pemisahan ini, kegagalan pada runner GPU tidak selalu menghentikan produktivitas tim untuk perubahan berisiko rendah, tetapi perubahan tetap harus melewati gerbang yang sesuai sebelum rilis.
Contoh aturan sederhana
- Perubahan dokumentasi atau UI non-ML: tidak perlu validasi GPU.
- Perubahan preprocessing, tokenization, atau routing inferensi: wajib smoke test CPU dan subset validasi GPU.
- Perubahan kernel, dependency native, image base, atau code path device-specific: wajib validasi GPU sebelum merge.
Aturan seperti ini membantu tim kecil mengelola biaya tanpa kehilangan disiplin engineering.
Pakai golden dataset kecil agar sinyal tetap konsisten
Untuk verifikasi inferensi atau evaluasi model, gunakan golden dataset kecil: sekumpulan input representatif yang disimpan stabil dan dipakai berulang untuk membandingkan output antar commit.
Ciri golden dataset yang baik
- Ukurannya kecil sehingga cepat dijalankan di CI.
- Mewakili kasus umum dan beberapa edge case penting.
- Memiliki output referensi atau metrik referensi yang dapat diperiksa.
- Disimpan versinya dengan jelas, terpisah dari data eksperimen besar.
Golden dataset tidak harus mengejar coverage statistik besar. Fungsinya adalah deteksi regresi, bukan evaluasi riset penuh.
Apa yang dibandingkan?
Tergantung jenis workload, Anda bisa membandingkan:
- bentuk tensor atau schema output,
- label/top-k yang diharapkan,
- rentang skor confidence,
- checksum artifact intermediate tertentu,
- metrik agregat sederhana pada sejumlah sampel kecil.
Untuk pipeline yang sensitif terhadap floating point, hindari membandingkan angka mentah secara bit-perfect jika memang runtime tidak deterministik.
Toleransi output nondeterministik: jangan pakai assert yang terlalu kaku
GPU sering memunculkan variasi kecil pada output. Penyebabnya bisa berupa urutan operasi paralel, library backend, precision, atau optimisasi kernel. Test yang menuntut kesamaan absolut sering menjadi sumber flaky test.
Pola verifikasi yang lebih aman
- Toleransi absolut/relatif untuk tensor atau skor numerik.
- Perbandingan top-k alih-alih probabilitas mentah.
- Validasi rentang untuk latency, penggunaan memori, atau confidence.
- Metode agregat seperti rata-rata error pada sampel kecil, bukan satu nilai tunggal.
Contoh sederhana dalam Python:
from math import isclose
def assert_scores_close(actual, expected, rel_tol=1e-4, abs_tol=1e-6):
if len(actual) != len(expected):
raise AssertionError(f"length mismatch: {len(actual)} != {len(expected)}")
for i, (a, e) in enumerate(zip(actual, expected)):
if not isclose(a, e, rel_tol=rel_tol, abs_tol=abs_tol):
raise AssertionError(
f"score mismatch at index {i}: actual={a}, expected={e}"
)
Namun toleransi tidak boleh menjadi alasan untuk membiarkan regresi besar lolos. Tetapkan ambang berdasarkan karakteristik model dan backend Anda, lalu dokumentasikan alasannya.
Kapan perlu deterministic mode?
Jika framework dan workload Anda mendukung mode lebih deterministik, gunakan untuk sebagian test verifikasi numerik. Trade-off-nya biasanya adalah performa lebih lambat atau keterbatasan pada operator tertentu. Cocok untuk test validasi sempit, kurang cocok untuk benchmark performa.
Mitigasi flaky test pada pipeline GPU
Flaky test merusak kepercayaan terhadap CI. Pada workload GPU, sumbernya sering berasal dari timeout, OOM, race condition, model artifact tidak sinkron, atau asumsi performa yang terlalu optimistis.
Langkah mitigasi yang efektif
- Batasi concurrency untuk job yang memakai GPU yang sama.
- Gunakan timeout yang realistis, bukan terlalu ketat.
- Warm up runtime jika inferensi pertama memang lebih lambat karena inisialisasi.
- Log metadata lingkungan: nama device, versi driver, hash model, image digest, dan commit SHA.
- Bedakan retriable failure vs real regression. Misalnya, kegagalan saat mengunduh artifact bisa di-retry, sedangkan mismatch output tidak boleh otomatis diulang tanpa investigasi.
- Jalankan test dengan input kecil tetap agar beban memori konsisten.
Kesalahan umum: menambahkan retry ke semua test GPU. Ini hanya menyembunyikan bug. Retry masuk akal untuk kegagalan infrastruktur, bukan untuk validasi hasil.
Caching artifact dan model untuk memangkas waktu CI
Sering kali bottleneck pipeline GPU bukan komputasinya saja, melainkan pengunduhan model, build image, atau ekstraksi artifact besar. Karena itu, strategi cache sangat berpengaruh.
Apa yang layak di-cache
- file model yang versioned dan immutable,
- dependency environment yang jarang berubah,
- artifact build intermediate,
- hasil preprocessing tetap untuk golden dataset kecil,
- container image base yang sudah berisi dependency native.
Prinsip cache yang aman
- Kunci cache harus eksplisit: berdasarkan hash lockfile, manifest model, atau digest image.
- Jangan cache output yang seharusnya diverifikasi: cache untuk input/dependency, bukan untuk menyamarkan hasil test.
- Pastikan artifact immutable: satu versi model harus mengarah ke isi yang sama.
Contoh manifest sederhana untuk menjaga integritas artifact:
{
"model_name": "vision-inference",
"model_version": "2026-08-15",
"sha256": "...",
"source": "object-storage://ml-artifacts/vision-inference/2026-08-15/model.bin"
}
Dengan manifest seperti ini, CI dapat memverifikasi bahwa file yang diambil benar-benar sesuai sebelum test dijalankan.
Gate regresi performa tanpa membuat PR selalu mahal
Performa adalah alasan utama memakai GPU, tetapi benchmark di CI mudah menipu jika dijalankan sembarangan. Variasi runner, kontensi resource, dan warm-up bisa menghasilkan noise tinggi.
Praktik yang lebih realistis
- Jangan jadikan benchmark lengkap sebagai syarat setiap PR kecil.
- Gunakan performance gate bertingkat: baseline ringan di merge, benchmark lebih stabil di nightly.
- Bandingkan terhadap baseline pada lingkungan yang relatif konsisten.
- Ukur metrik yang benar-benar penting: latency p50/p95 sederhana, throughput mini-batch, peak memory, atau waktu startup model.
Jika variasi runner tinggi, lebih aman memakai ambang regresi yang konservatif dan menjadikan hasil benchmark sebagai sinyal review, bukan auto-fail mutlak, kecuali Anda memiliki runner yang sangat stabil.
Matriks CI bertingkat: cepat di PR, lebih lengkap di merge dan nightly
Matriks CI tidak harus penuh di setiap event. Susun bertingkat berdasarkan risiko dan biaya.
Contoh desain matriks
- PR cepat
- lint
- unit test CPU
- smoke test CPU end-to-end
- mocked integration test untuk layanan eksternal
- PR berlabel khusus atau file tertentu berubah
- tambahan subset test GPU
- validasi loading model aktual
- Merge ke main
- smoke test GPU
- golden dataset kecil di GPU
- cek memori/latency sederhana
- Nightly
- matriks beberapa model/backend
- dataset lebih besar
- benchmark performa
- validasi kompatibilitas image/runtime
Keuntungan pendekatan ini adalah mayoritas commit mendapat feedback cepat, sementara area paling berisiko tetap diuji dengan kedalaman yang cukup.
Contoh pseudo-config workflow
jobs:
test-cpu:
runs-on: cpu-runner
steps:
- checkout
- restore-cache
- run: make test-unit
- run: make test-smoke-cpu
test-gpu-smoke:
if: branch == "main" or label == "needs-gpu"
runs-on: gpu-runner
steps:
- checkout
- restore-model-cache
- run: make test-smoke-gpu
- run: make test-golden-gpu
test-gpu-nightly:
if: schedule == "nightly"
runs-on: gpu-runner
steps:
- checkout
- restore-model-cache
- run: make test-gpu-full
- run: make benchmark-gpu
Ini bukan sintaks runner tertentu, tetapi menunjukkan pemisahan tanggung jawab yang sebaiknya ada pada workflow Anda.
Kapan memakai mock, emulator, atau nightly run
Mock: untuk logika orchestration dan error path
Mock cocok ketika Anda ingin menguji:
- pemilihan device CPU/GPU,
- retry logic,
- fallback saat model gagal dimuat,
- integrasi dengan service eksternal seperti storage atau queue.
Mock tidak cukup untuk membuktikan bahwa kernel GPU, runtime inferensi, atau kompatibilitas artifact benar-benar berfungsi.
Emulator atau backend pengganti: untuk kontrak, bukan performa
Jika ekosistem Anda menyediakan backend non-GPU yang kompatibel secara API, itu berguna untuk memeriksa kontrak pemanggilan, schema output, dan alur aplikasi. Namun jangan menganggapnya representatif untuk latency, memory footprint, atau numerik yang benar-benar identik.
Nightly run: untuk test mahal dan mudah noisy
Pindahkan ke nightly jika test:
- memakan waktu lama,
- membutuhkan beberapa kombinasi hardware,
- rentan noise performa,
- lebih cocok sebagai sinyal tren daripada syarat merge langsung.
Nightly tetap harus menghasilkan tindakan jelas: membuat issue otomatis, notifikasi ke channel tim, atau penandaan commit terakhir yang lolos.
Arsitektur kode yang memudahkan verifikasi
Strategi CI akan jauh lebih mudah jika kode Anda dirancang untuk dipisah menurut tanggung jawab.
Pola yang membantu
- Interface runtime: pisahkan kelas/komponen yang mengurus inferensi dari logika aplikasi.
- Adapter device: satu antarmuka, beberapa implementasi untuk CPU, GPU, atau mock.
- Pre/post-processing murni: usahakan fungsi deterministik yang bisa diuji di CPU.
- Artifact manifest: semua model dan asset penting memiliki versi dan checksum.
- Feature flag: memudahkan mematikan optimisasi tertentu saat investigasi flaky test.
Jika seluruh pipeline terikat langsung ke satu library GPU pada setiap lapisan, hampir pasti test akan mahal dan sulit diisolasi.
Debugging saat test GPU gagal tetapi CPU lolos
Kasus ini umum dan sering membingungkan. Urutan diagnosis yang praktis:
- Pastikan artifact model yang dimuat benar: versi, checksum, path.
- Bandingkan metadata environment antara run yang lolos dan gagal.
- Cek apakah failure terjadi saat init runtime, alokasi memori, atau saat inferensi aktual.
- Jalankan satu sampel dari golden dataset secara lokal atau di runner yang sama.
- Nonaktifkan optimisasi yang mencurigakan untuk mempersempit area masalah.
- Periksa apakah assert terlalu ketat terhadap variasi numerik.
Yang sering terlewat adalah membedakan bug fungsional dari instabilitas infrastruktur. Log yang kaya metadata sangat membantu di sini.
Checklist implementasi CI untuk tim kecil
Berikut checklist yang bisa langsung diterapkan tanpa perlu infrastruktur besar:
- Buat unit test CPU untuk preprocessing, postprocessing, konfigurasi, dan fallback logic.
- Tambahkan smoke test CPU end-to-end dengan model kecil atau input mini.
- Definisikan golden dataset kecil yang disimpan versioned dan stabil.
- Pisahkan workflow menjadi PR cepat, merge ke main, dan nightly GPU.
- Terapkan subset GPU test yang benar-benar memverifikasi path khusus GPU.
- Gunakan toleransi numerik yang terdokumentasi, bukan equality absolut.
- Cache model artifact, dependency native, dan image base dengan key yang jelas.
- Simpan manifest model + checksum untuk mencegah artifact drift.
- Tambahkan logging metadata: device, driver, commit SHA, hash model, dan parameter test.
- Bedakan kegagalan infrastruktur retriable dari regresi hasil.
- Buat aturan kapan perubahan harus memicu validasi GPU wajib.
- Jangan jadikan benchmark berat sebagai syarat semua PR; pindahkan ke nightly atau merge gate terbatas.
- Review test flaky secara berkala; hapus assert yang terlalu sensitif dan perbaiki sumber nondeterminisme.
- Pastikan ada jalur fallback CPU atau mode verifikasi minimal jika GPU runner sedang tidak tersedia.
Penutup
CI yang sehat untuk workload berbasis GPU bukan tentang memaksa semua verifikasi berjalan di hardware mahal setiap saat. Yang lebih penting adalah menempatkan jenis verifikasi yang tepat pada tahap yang tepat: CPU untuk sinyal cepat, GPU untuk risiko yang benar-benar spesifik, dan nightly untuk validasi mahal yang tidak perlu menghambat semua perubahan.
Jika Anda menerapkan test pyramid, golden dataset kecil, toleransi terhadap nondeterminisme, cache artifact yang disiplin, dan matriks CI bertingkat, tim kecil pun bisa menjaga kualitas sistem ML atau inferensi tanpa bergantung penuh pada ketersediaan GPU setiap saat.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!