GPU'm, Yerine Geçtiği CPU'dan Daha Yavaştı

Bir keresinde ağır bir hesaplamayı CPU’dan GPU’ya taşıdım, uçacağını umuyordum. Onun yerine süründü — yerine geçmesi gereken sıradan koddan çarpıcı biçimde daha yavaştı. Hız satın almıştım ve bir şekilde bir yavaşlama kurmuştum.

Donanım darboğaz değildi. Modern bir GPU, bir bilgisayara koyabileceğiniz en hızlı şeylerden biridir. Sorun, ona neredeyse benzersiz biçimde kötü olduğu bir biçimde şekillenmiş bir iş vermiş olmam ve sonra çalıştıramayınca şaşırmamdı.

Düşürme olan yükseltme

Beklenti basitti ve sandığım kadarıyla güvenliydi: GPU’lar hızlıdır, işim yavaştı, dolayısıyla işi GPU’ya taşımak onu hızlı yapmalıydı. Hızlandırmanın nasıl işlediğine dair halk arasındaki sürüm budur ve belirli, öğretici bir biçimde yanlıştır.

“Hızlandırılmış” sürümü ölçtüğümde, biraz daha yavaş değildi. Büyük bir katsayı kadar daha yavaştı — size sorunun eksik bir optimizasyon değil, temel bir uyumsuzluk olduğunu söyleyen türden bir sonuç. Yaptığım şeyin biçmiyle ilgili bir şey, donanıma etkin biçimde karşı koyuyordu.

”Onu GPU’ya koymak” aslında ne demekti

İşte gerçekten ne yapmıştım. Mevcut bir döngüm vardı: bir birim işi al, hesapla, sonrakine geç, binlerce kez. “GPU’yu kullanmak” için, basitçe her bir bireysel birimi GPU’da çalıştırmıştım — ve döngüyü tam olarak olduğu yerde bırakmıştım.

Yani art arda binlerce kez, programım GPU’ya dönüyor, ona bir küçük görev veriyor, yanıtı bekliyor ve sonraki için geri dönüyordu. Döngü hâlâ kontroldeydi. GPU, çakmak istediğim her bir çivi için bireysel olarak telefon ettiğim çok hızlı bir taşeron sadece.

Gizli maliyet: sadece sormak bile bir şeye mal olur

Bir GPU’ya her bir iş parçası verdiğinizde, onu kurmanın ve göndermenin sabit bir maliyeti vardır — tek başına küçük, bir kez ödediğinizde görünmez. Ben bunu binlerce kez ödüyordum. Yakından baktığımda, çalışma süresinin çoğu hesaplama bile değildi. Genel giderdi: GPU boşta oturuyor, ne yapacağının söylenmesini bekliyordu, programım ise zamanını art arda bir minik soru sormakla geçiriyordu.

Neredeyse tamamen sorma eyleminden oluşan, gerçek işin bir yuvarlama hatası olduğu bir süreç inşa etmiştim. CPU sürümü yavaştı, ama en azından meşgul kalıyordu. GPU sürümü yavaştı çünkü ömrünün çoğunu beklemekle geçiriyordu.

GPU bir taksi değil, bir otobüstür

Sonunda bunu düzelten zihinsel model: bir GPU, tek bir şeyi yapmanın daha hızlı bir yolu değildir. Aynı şeyi aynı anda muazzam sayıda yapmanın bir yoludur. Bir spor arabası değil, elli koltuklu bir otobüstür.

Onu öğe başına bir kez çağırmak, o otobüsü her seferinde tek bir yolcuyla şehrin bir ucundan diğerine gidip getirmek gibidir. Otobüs gerçekten hızlıdır. Verimliliğiniz iç karartıcıdır — ve tam olarak iç karartıcıdır çünkü kalabalıklar için yapılmış bir aracı insanları teker teker taşımak için kullanıyorsunuz. Kusur otobüste değildir. Sevkiyattadır.

Çözüm: döngüyü bırak, gruplamaya başla

Onarım daha hızlı bir çekirdek ya da daha iyi bir kart değildi. Döngüyü yeniden yapılandırmaktı. “Her öğe için, onu GPU’ya gönder” yerine, işi GPU’nun birçok öğeyi bir kerede alıp birlikte işleyebileceği biçimde yeniden şekillendirdim — binlerce minik sevkiyatı bir avuç büyük sevkiyata dönüştürdüm. Defalarca ödediğim genel gider, binlerce kez yerine birkaç kez ödediğim bir şeye çöktü.

Bu, insanların “vektörleştirme” ya da gruplamayla kastettiği şeyin yaygın bir biçimidir: birçok benzer işlemi, küçük adımlardan oluşan uzun bir dizi yerine birlikte ifade etmek. Çoğu zaman bu, CPU’nuzu utandıran bir GPU ile CPU’su tarafından utandırılan bir GPU arasındaki farktır ve ikisi arasındaki uçurum muazzam olabilir.

Bu, donanımın tüm amacı neden budur

Yapıyı bir ayar ayrıntısı gibi ele almak yerine neden bu kadar önemli olduğunu içselleştirmem biraz zaman aldı. Bir GPU’nun buradaki avantajı, herhangi bir tek işlemin hızlı olmasından gelmiyordu. Çok sayıda benzer işlemi aynı anda yapmaktan geliyordu. O paralellik, tüm değer önerisidir. Kodunuz işini “bunların hepsini aynı anda yap” olarak ifade edemiyorsa, paralelliğin tutunacağı hiçbir şey yoktur — ve seri, teker teker iş yapan bir GPU, sadece pahalı, hantal, kötü şekillenmiş bir CPU’dur.

Yani “onu GPU’ya koy” hiçbir zaman gerçek talimat değildi. Gerçek talimat, “işi hepsi bir kerede yapılabilecek biçimde yeniden şekillendir” idi. Donanım, ancak sorunun biçiminin izin verdiği kadar hızlı olacaktı.

Araç, yanlış biçimi kurtaramaz

Ders, GPU’ların çok ötesine iyi genelleniyor. Hesaplamanızın yapısı o donanıma karşı koyuyorsa, daha hızlı donanıma uzanmak pek bir işe yaramaz. Bunun gibi işler için, performans, üzerinde çalışan şeyin nominal hızından çok hesaplamanın biçiminde — nasıl organize edildiği, gruplandığı ve ifade edildiğinde — yaşıyordu. Yavaş bir algoritmayı hızlı bir makineye koyabilir ve hızlı bir hayal kırıklığı elde edebilirsiniz.

O yavaşlamayı, “onu hızlandırdım” ile “onu daha hızlı donanıma taşıdım”ın aynı cümle olmadığının bir hatırlatıcısı olarak aklımda tutuyorum. İlki işi anlamakla ilgilidir. İkincisi, silikonun bunu sizin için anlayacağını ummakla ilgilidir. Anlamayacak.

— Sinyal yok, getiri yok, yatırım tavsiyesi değildir.