İki DGX Spark sınıfı kutu, tek model: her birinde birer kopya, bölünmeye karşı
Operasyonlar
Yedi hafta boyunca yerel dil modelimiz, tensör paralelliği ile iki NVIDIA GB10 makinesi arasında bölünmüş olarak çalıştı. 22 Ekim'de bunu her makinede modelin tam bir kopyası ve önünde en az bağlantılı bir vekil sunucu ile değiştirdik. Verim saniyede yaklaşık 180'den 210 token'a yükseldi ve artık herhangi bir makineyi, modeli ortadan kaldırmadan yeniden başlatabiliyorsunuz.
Neden bölmüştük
Her makinede CPU ve GPU'nun paylaştığı 128 GB bellek var. Eylül ayında model tek bir makineye sığıyordu, ancak önbelleği zar zor sığıyordu: kullandığımız vLLM derlemesi bu modelin KV önbelleğini FP8'de tutamıyordu, bu nedenle önbellek BF16'daydı ve bir makine bunun için 499.110 token'a yer bırakıyordu. Bu, modelin 262.144 token'lık tam bağlamında iki istekten daha az. Doğrudan ağ bağlantısı üzerinden tensör paralelliği ile her iki makineye bölündüğünde, önbellek üç milyondan fazla token'a büyüdü. Modeli hız için değil, yer için bölmüştük.
Neler değişti
Aynı modelin daha yeni bir 4-bit derlemesi (NVFP4, kuantum farkındalıklı damıtma ile oluşturuldu) yaklaşık 71 GiB bellek kullanıyor ve GB10 için topluluk vLLM derlemesi (eugr/spark-vllm-b12x) önbelleğini FP8'de çalıştırıyor. Artık bir makine tüm modeli ve 0,76 ile 1,08 milyon token'lık bir önbelleği, makinenin belleğinin ne kadarını ayırdığına bağlı olarak tam bağlamda 2,9 ile 4,1 isteğe yetecek kadar yer tutuyor. Her iki kopya da modelin kendi spekülatif kod çözümlemesini (MTP, dört taslak token) kullanıyor. Geçiş yapmadan önce yeni derleme üzerinde araç çağırma testimizi çalıştırdık: 86 puan aldı, yerini aldığı derleme 75 puan almıştı.
Neyi ölçtük
| İki makineye bölünmüş | Her makinede bir kopya | |
|---|---|---|
| KV önbelleği | 3 milyondan fazla token | 0,76 ve 1,08 milyon token |
| Tam bağlamda istekler | yaklaşık 12 | 2,9 ve 4,1 |
| Yükümüz altındaki verim | yaklaşık 180 token/s | 210 token/s (111 + 99) |
| Başlatma | yaklaşık 20 dakika | kopya başına 5 ila 13 dakika |
| Bir makine yeniden başlatıldığında | model çöker | diğer kopya hizmet verir |
Tek kopya, aynı anda işlediği istek sayısına göre kendi başına: kısa istemlerle 1 istek saniyede 36 token, 4 istek 82, 8 istek 109 ve 16 istek 150 üretti. E-posta ajanımızın daha uzun istemleriyle 4 istek 81, 8 istek 113 ve 16 istek yalnızca 87 üretti: sekiz isteği aştıktan sonra daha fazlası işlemi yavaşlattı.
Bir öğleden sonra bir kopya aynı anda en fazla 12, diğeri 8 istek aldı. İlki saniyede 91 ile 93 token üretti ve dakikada 26 ile 27 isteği tamamladı; ikincisi saniyede 81 ile 83 token ve dakikada 22 istek üretti. Şimdi ikisi de 12 alıyor.
Nelerden vazgeçtik
Alan. Bölünmüş modelin üç milyondan fazla tokenlık tek bir önbelleği vardı; kopyaların iki daha küçük önbelleği var ve bir istek yalnızca kendisine hizmet eden kopyanın önbelleğini kullanabilir. Ajanlarımız çok sayıda kısa ve orta uzunlukta istek gönderir ve onlar için verim daha önemliydi. Aynı anda birkaç çok uzun bağlam sunuyorsanız, bölme hâlâ daha iyi bir tercih olabilir. Bölünmüş kurulumu sakladık ve geri geçiş tek bir betikle yapılır.
Geçiş sırasında neler ters gitti
- Model olmadan on bir dakika, ardından 30 saniyelik 502 hataları. Bölünmüş model, aynı makinelerde bir kopya yüklenmeden önce durmak zorunda kaldı, bu yüzden ajanlarımız yaklaşık on bir dakika bekledi. İlk kopya ayağa kalktığında, proxy yaklaşık 30 saniye daha "502 no live upstreams" yanıtı vermeye devam etti: backend'leri çalışmadıkları sırada başarısız olarak işaretlemişti (
max_fails=3,fail_timeout=30s) ve süre dolana kadar onları tekrar denemedi. - Vekil sunucuyu atlayan bir istemci. Toplu işlerimizden birinin yapılandırmasında ilk makinenin adresi vardı, bu yüzden tüm yükü bir kopyaya gitti ve diğer kopyada bolca kapasite boşta kaldı. Artık tüm istemciler vekil sunucu üzerinden geçiyor; makinelerin kendi adresleri yalnızca sağlık kontrolleri için kullanılıyor.
- Sıçrayan metrikler. Verim monitörümüz, proxy üzerinden vLLM'in
/metricsuç noktasını okuyordu. İki backend ile ardışık okumalar farklı kopyalardan geldi, sayaçlar ileri geri sıçradı ve monitör modeli aç (starved) olarak raporladı. Artık her kopyayı doğrudan okuyup topluyor. - Yeni kopyayı durduracak eski bir gözetim mekanizması. Bölünmüş kurulum, model yanıt vermeyi bıraktığında hizmetini yeniden başlatan bir gözetim mekanizmasına sahipti. Yeni hizmet, eskisiyle
Conflicts=bildirir, bu yüzden bu yeniden başlatma yeni kopyayı durduracaktı. Sadece durdurulmakla kalmayıpsystemctl disable --nowile kapatılması gerekiyordu, çünkü operasyon katmanımız etkin ancak çalışmayan birimleri yeniden başlatıyor. - Başka işler yapan makinede bellek. Bir makine aynı zamanda veritabanlarımızı ve site derlemelerini çalıştırıyor. vLLM'in
gpu-memory-utilizationdeğeri 0.78 iken, diğer her şey için yalnızca 5 GiB kalıyordu, bu yüzden bunu 0.74'e düşürdük ve diğer kopya yükü taşırken yalnızca o kopyayı yeniden başlattık. Bu makinelerde, vLLM'e verdiğiniz pay, diğer her şeyin kullandığı bellekten çıkar.
Bu makinelerden iki tanesine sahipseniz
- Model ve ihtiyaç duyduğunuz önbellek tek bir makineye sığıyorsa, her birinin arkasında en az bağlantı (least-connection) proxy'si ile birer kopya çalıştırın. Modeli yalnızca tek bir makine ihtiyaç duyduğunuz önbelleği barındıramıyorsa bölün.
- Toplu işler ve eski yapılandırmalar dahil tüm istemcileri proxy'ye yönlendirin.
- Ölçümleri her kopyadan doğrudan okuyun.
- Her makineye, üzerinde çalışan diğer şeylere göre kendi bellek payını verin.
- Bir kopyanın aynı anda kaç istek işleyeceğini kendi trafiğinize göre ayarlayın: bizim karışımımızda 12, 8'i geçti.
- Geçişin, bir kopyanın başlaması için gereken süre boyunca modeli erişimden kaldıracağını bekleyin ve buna göre plan yapın.
Factory Floor, bir veya iki makine için proxy ve başlatma yapılandırmalarını, modelleri geçirmeden önce çalıştırdığımız sağlık kontrolü ve araç kullanımı testini, ayrıca makinenin geri kalanı için gözetim mekanizmalarını ve bellek güvenlik kısıtlarını içerir.