Memindahkan Ring ke p4

Tujuh perubahan kontrak antara sebuah mesin dan pemanggilnya. Masing-masing gagal dengan cara berbeda, dan hanya satu yang tampak seperti error.

Kami menggabungkan rilis baru mesin p4 dan ring berhenti melayani. Bukan dengan crash — pemuat melaporkan sukses, para agen melaporkan ready, dan tak terjadi apa-apa. Menelusuri balik dari kesunyian itu memakan satu hari dan menemukan tujuh tempat di mana pemanggil kami dan mesinnya sudah saling menyimpang. Yang membuatnya layak dicatat bukan jumlahnya. Melainkan bahwa enam dari tujuh tak menghasilkan error apa pun.

Kegagalan satu

Peristiwa untuk node yang tak ada diteruskan, bukan ditolak

Pemuat kami mengalamatkan perintah LOAD ke node yang ingin diciptakannya. Tetapi sebuah node belum ada sampai LOAD menciptakannya, dan aturan broker untuk peristiwa yang menyebut node tak dikenal adalah meneruskannya keluar alih-alih menolaknya. Perintah itu meninggalkan agen sambil mencari tempat lain untuk dituju, tak menemukan apa pun, lalu dibuang. Tak ada satu baris log pun, karena dari sudut pandang broker tak ada yang salah.

Perbaikannya adalah mengalamatkan LOAD ke *agen*, dibungkus dalam amplop siklus-hidup mesin yang netral-backend, dengan perintah adapter sendiri sebagai isi yang buram. Jelas setelah tahu; tak terlihat dari luar.

Kegagalan dua

Sebuah angka yang harus sama dengan angka lain

Sebuah model dimuat di bawah load generation, dan tiap node didaftarkan dengan node generation. Kami memperlakukan keduanya sebagai independen — timestamp untuk yang satu, 1 untuk yang lain — dan semuanya berjalan. Ring termuat. Ia menjawab satu permintaan dengan benar. Lalu node kepala mati.

Pemeriksaannya, di dalam akuntansi pelepasan milik adapter.

let Endpoint::Node { generation, .. } = &event.envelope.source;
if *generation != receipt.load_generation {
    return Err("release owner census generation differs from source");
}

Tanda terima yang menutup sebuah permintaan yang selesai membawa load generation, dan node yang mengirimnya membawa miliknya sendiri. Saat keduanya berbeda, node dihentikan. Jadi bentuk bug-nya adalah: pemuatan sukses, permintaan pertama sukses, kepala mati, dan setiap sesi sesudah itu menggantung setengah-termuat. Itu terbaca persis seperti crash di bawah beban dan sama sekali tak seperti ketidakcocokan. Pemuat kami kini menolak rencana yang kedua angkanya tak sepakat, sebelum apa pun dimuat.

Kegagalan tiga

Sebuah batas yang tak mungkin terpenuhi

Adapter membandingkan hasil terbesar yang boleh dikembalikan sebuah stage terhadap angka yang dilaporkan stage server saat ia menyala, untuk kesetaraan persis. Kami tak bisa tahu angka itu tanpa memuat modelnya — jadi kami memuat dengan tebakan, dan kegagalannya memberi tahu kami angka nyata keempat stage sekaligus:

Satu run, empat jawaban.

step37-s0: profile=33554432, READY=34419218444
step37-s1: profile=33554432, READY=34419218444
step37-s2: profile=33554432, READY=34419218444
step37-s3: profile=33554432, READY=59136012

34 GB. Retained store milik agen adalah 256 MiB, jadi ring itu tak akan pernah bisa diterima. Membaca penurunan rumusnya dari stage server menunjukkan sebabnya: batas itu menskala dengan n_batch × n_ubatch, dan kami mewarisi lebar batch 2048 dari sebuah konfigurasi yang mendahului pemeriksaan ini. Pada 128 baris — lebar yang dipakai tata letak produksi — batasnya 138 MB dan muat. Kini kami menurunkannya di dalam rencana dari rumus yang sama alih-alih membawa konstanta yang diingat, dan angka prediksi untuk stage ekor keluar persis sama dengan yang pernah dicatat deployment lain — jenis kesepakatan yang layak dimiliki sebelum menghabiskan dua puluh menit untuk satu pemuatan.

Empat yang lain

Ringkas saja

  • Alamat loopback di ring dua-host. Sebuah stage menelepon agen stage berikutnya di alamat yang diiklankan agen itu. Iklankan 127.0.0.1 dan host A menelepon dirinya sendiri. Konfigurasi ring yang tercatat ternyata loopback sejak awal — ia tak pernah bisa bekerja antar-host.
  • Jurnalnya wajib. Sebuah model tak akan dimuat tanpa jurnal operasional agen. Kami mematikannya saat mengejar error lain dan memperburuk gejalanya dengan cara yang tampak seperti kemajuan.
  • Nama perangkat bersifat khusus-backend. Pembangun rencana acuan menargetkan CUDA dan memancarkan --device CUDA0. Build HIP menamai perangkatnya ROCm0. Rencana itu memuat seluruh model *lalu* gagal menemukan perangkatnya.
  • Server native adalah bagian dari rilis. Agen yang dibangun dari pohon sumber lebih baru menginginkan kapabilitas yang tak dilaporkan stage server yang terpasang. Juga ditemukan setelah pemuatan model penuh.
Apa yang kami ambil darinya

Kesunyian adalah mode kegagalan yang mahal

Setiap satu dari semua ini murah untuk diperbaiki dan mahal untuk ditemukan, dan polanya konsisten: kegagalan yang mahal adalah yang membuat sistem tampak benar tetapi tak melakukan apa-apa, atau melakukan sesuatu sekali saja. Penjaga yang kami tambahkan semuanya berbentuk sama — menolak lebih awal, di tempat kesalahannya masih terbaca. Pemuat menuliskan load generation ke disk *sebelum* perintah pertama berangkat, karena tanpa itu ia tak terpulihkan. Ia menolak ketidakcocokan generation alih-alih menemukannya setelah permintaan pertama. Ia menurunkan batas hasil alih-alih mengingatnya.

Ring sedang melayani. Empat stage di dua mesin, 113 GiB bobot residen, token pertama dalam 1.4 dtk saat dingin dan ~0.3 dtk saat hangat, dan kontribusi per-node mengalir sampai ke buku besar penyelesaian untuk pertama kalinya.