HN Açıklandı: Go Plan9 Notları
(pehringer.info)Go'da eşzamanlılık ve paralelliğin kullanımı
- Sayı hesaplama yeteneğini artırmak için Go'nun eşzamanlılık ve paralellik özelliklerinden yararlanmayı amaçlayan bir projenin tanıtımı.
- SIMD (Same Instruction Multiple Data) komutları kullanılarak donanım düzeyinde paralel hesaplama yapılabilir.
- Go derleyicisi SIMD'den yararlanmadığı için ve uygun genel amaçlı bir SIMD paketi bulunamadığından, paketi doğrudan geliştirmeye karar verildi.
Plan9 assembly dili
- Go, Plan9 adlı kendi assembly dilini kullanır; bu dil, belirli platformların komutlarını ve register'larını biraz değiştirerek kullanır.
- x86 Plan9 ile ARM Plan9 birbirinden farklıdır.
- Plan9'ın basit bir örneği üzerinden temel kullanım açıklanır.
Plan9 örneği
AddInts_amd64.sdosyası vemain.godosyası üzerinden Plan9'da temel fonksiyon bildirimi ve kullanım biçimi açıklanır.- Go'nun çağrı kurallarına göre fonksiyon argümanlarının ve dönüş değerlerinin stack'te nasıl saklandığı anlatılır.
Paket tasarım planı
- Aritmetik ve bit işlemleri için SIMD işlerine yönelik ince bir soyutlama katmanı sağlayan bir paket tasarlanır.
- Mimariye özel Plan9 implementasyonlarını içeren bir iç paket oluşturulur ve bu yapı başlatma fonksiyonu üzerinden ayarlanır.
SIMD örneği
- x86 SIMD Plan9 fonksiyon örnekleriyle SIMD'in kullanım biçimi açıklanır.
Supported_amd64.sveAddFloat32_amd64.sdosyaları üzerinden SSE desteğinin nasıl kontrol edildiği vefloat32toplama işleminin nasıl yapıldığı gösterilir.
Performans ve gelecek
- Go'nun yazılım implementasyonu ile Plan9 SIMD implementasyonu arasındaki performans farkını gösteren grafik üzerinden yaklaşık %200-450 hız artışı görüldüğü belirtilir.
- Bu notların, Plan9 ve SIMD kullanan projeler için ilham vermesi umulur.
# GN⁺ özeti
- Bu yazı, Go'nun eşzamanlılık ve paralellik özelliklerini kullanarak performansı en üst düzeye çıkarmanın yollarını tanıtır.
- Plan9 assembly dili ve SIMD komutlarıyla donanım düzeyinde paralel hesaplamanın nasıl yapılabileceğini açıklar.
- Go geliştiricileri için Plan9 ve SIMD'in kullanım potansiyelini gösterir ve performans artışı için yeni yaklaşımları keşfetmede faydalı olabilir.
- Benzer işlevlere sahip projeler olarak Rust'un SIMD destek kütüphaneleri veya C++'taki SIMD ile ilgili kütüphaneler önerilir.
1 yorum
Hacker News yorumları
Go assembly hakkında birkaç noktaya değinmek gerekirse, amd64’te şu
int’ler aslında 64 bittir.int32kullanırsanız parametre listesinde word hizalaması olur, ama bir tuzak var: 64 bit sistemlerde dönüş değerleri her zaman doubleword hizalı bir offset’ten başlar.NOSPLIT, Go derleyicisinin otomatik olarak sağladığıtextflag.hiçinde tanımlıdır. Ancak okuduğum kadarıylaNOSPLITyalnızcaruntime.XXfonksiyonlarında dikkate alınıyor gibi; bu yüzden burada hiçbir şey yapmıyor ve gerekli de değil.NOSPLIT, derleyicinin, stack’in taşma ihtimaline karşı bölme gerekip gerekmediğini kontrol eden kodu eklememesi anlamına gelir. Stack alanına ihtiyaç duymayan bir fonksiyon için teknik olarak gereksizdir; temelde, stack bölmeyi kontrol eden fonksiyonun kendisinin içine bu kontrol kodunun enjekte edilmesini engellemek için vardır.4değeri “NOSPLIT”i temsil ediyor ve bir nedenden gerekli denmiş; merak edenler için ekleyeyim: Normalde frame boyutunun (NOSPLIT’ten sonraki parametre) ardından argüman boyutu gelir ve ikisi eksi işaretiyle ayrılır.Bu bir çıkarma işlemi değil, sadece kendine özgü bir söz dizimidir.
$24-8frame boyutu, fonksiyonun 24 baytlık bir frame’e sahip olduğu ve çağıranın frame’indeki 8 baytlık argümanlarla çağrıldığı anlamına gelir.TEXTiçindeNOSPLITbelirtmezseniz argüman boyutunu mutlaka vermeniz gerekir. Go prototipi olan assembly fonksiyonları içingo vet, argüman boyutunun doğru olup olmadığını denetler.Kaynak: https://go.dev/doc/asm
“Go, Plan9 adlı kendi dahili assembly dilini kullanıyor” denmiş; bu dil gerçekten böyle mi adlandırılıyor?
Sözdizimi Plan 9’dan türemiş olsa da biz ona Go assembly diyoruz.
Bkz. https://go.dev/doc/asm
Ama daha fazla baktıkça bunun bir LLM halüsinasyonu olduğu hissine kapılıyorum.
Assembly biçimi dokümanı buna düzgün bir ad vermiyor, sadece
go assemblerdiyor.Bu halüsinasyonun kaynağı muhtemelen ilk paragraf: “Assembler, Plan 9 assembler’ının girdi stilini temel alır ve… bu belge söz diziminin bir özetini, farkları ve Go ile etkileşen assembly kodu yazarken dikkat edilmesi gereken özel noktaları açıklar.”
“GNU Compiler Collection, Unix adlı kendi dahili assembly dilini kullanıyor” demeye benziyor.
Go ekibinin neden böyle özel bir assembly biçimini seçtiğini merak ediyorsanız, Rob Pike 2016’daki bir sunumunda Go assembler tasarımını ele almıştı [1][2].
Ana fikir, çoğu assembly dilinin genel olarak birbirine benzemesinden hareketle, “makinenin en alt seviyesiyle konuşurken yeni bir söz dizimi öğrenme gereğini ortadan kaldıran” ortak bir assembly dili oluşturmak gibi görünüyor.
Ayrıca yeni bir mimarinin instruction manual PDF’ini girdi olarak alıp çalışan assembler’ı otomatik üretmeyi de mümkün kılıyor.
[1]: https://www.youtube.com/watch?v=KINIAgRpkDA
[2]: https://go.dev/talks/2016/asm.slide#1
“Slice’lar üzerinde SIMD işlemleri gerçekleştirecek fonksiyonlara ihtiyaç vardı” denmiş; bunun pratikte nasıl kullanıldığını merak etmiştim.
Yazının tamamında arama yaptım ama slice’lar üzerinde yapılan herhangi bir işlem bulamadım.
Düzenleme: Bağlantı verilen dokümanda buldum: https://pkg.go.dev/github.com/pehringer/simd#pkg-index
Temel olarak toplamak istediğiniz 2 slice varsa,
fordöngüsü yerine SIMD ile paralel işleyipsimd.AddInt32(slice1, slice2, result)gibi kullanabiliyorsunuz.https://github.com/pehringer/simd/blob/main/simd_test.go
Go’nun desteklediği işlemcilerle ilgili olarak şu bölüm alakalı (1). Varsayılan x64 desteğine SSE ve SSE2 dahil.
Ancak Go derleyicisinin bunları gerçekten üretip üretmediğini bilmiyorum. Performansı en yüksek öncelik yapan gcc gibi çok karmaşık derleyicilerin aksine, Go derleyicisi Wirth tarzı (2), basit ve hızlı bir derleyiciyi tercih ediyor.
(1) https://go.dev/wiki/MinimumRequirements#amd64
(2) https://irreal.org/blog/?p=7075
https://smartgo.blog/2024/01/06/niklaus-wirth/
Yazarın bu kısmı karıştırmış gibi göründüğü için referans bağlantısı bırakıyorum: https://en.wikipedia.org/wiki/Plan_9_from_Bell_Labs
Ben de bunun kodun LLM tarafından yanlış yorumlanmasının sonucu gibi göründüğünü söylemek üzereydim.
Plan 9 terimini bilip assembly’ye kadar inmesine rağmen nasıl bir suya girdiğinin farkında olmamasını başka türlü pek hayal edemiyorum. Başkalarının da aynı şeyi düşündüğünü görünce anladım.
Eğer durum buysa, yazarın utanmış ya da “yakalandım” diye hissetmek yerine dürüst olmasını isterim. Ancak böyle biz de bir şeyler öğrenebiliriz. Bu tür “LLM’e maruz kalma” örüntülerinden emin olmak istiyorum ama ne kadar bariz görünse de insanların bunu kabul ettiği neredeyse hiç olmuyor gibi.
Elbette burada bariz olan bir şey yok; bu yalnızca çok aceleci ve yargılayıcı bir tahmin.
Böyle şeyler gerçekten sinirimi bozuyor.
Benim seviyemin biraz üzerindeydi ama yazının okuru yanında götürme biçimi keyifliydi.
Bu tür bir deneme ilk kez yapılıyor olamaz, değil mi? SIMD’ye susamış kelimenin tam anlamıyla onlarca Gopher vardır herhalde. Daha yaygın örüntü CGO kullanmak mı?
Go’dan assembly fonksiyonu çağırmak çok daha ucuz.
https://pkg.go.dev/github.com/grailbio/base/simd adresinde bu yönde yaptığım çalışmalar var.
Yanlış hatırlamıyorsam o dönemde birçok AVX komutu Go’nun Plan 9 assembler’ında encode bile edilemiyordu; bayt olarak elle encode etmek gerekiyordu [0].
Gördüğüm en olgun kütüphane, hiç kullanmamış olsam da, CGO’yu kısmen kullanırken o ek yükten kaçınan temiz bir hack uyguluyor [1].
[0]: https://github.com/slimsag/rand/blob/f1e8d464c0021a391d5cd64...
[1]: https://github.com/alivanz/go-simd/
Go assembly programları yazacaksanız Avo’ya (https://github.com/mmcloughlin/avo) bakmanızı öneririm.
Tip güvenliği sağlar ve geçerli assembly çıktısı üretip üretmediğine dair bazı kontroller yapar. Register’ları dinamik olarak tahsis edebilir; stack ve frame boyutu gibi şeyleri sizin hesaplamanıza gerek bırakmaz.
Çağrı kuralı ayrıntılarını da ele alabilir; böylece argümanları istediğiniz register’a ya da konuma kolayca yükleyebilirsiniz.
Yakın zamanda Go’nun crypto kütüphanesindeki amd64 assembly’nin tamamını Avo’ya port ettim; bu tür işler için çok kullanışlı bir kütüphane.