HN Show: Memo Go Plan9
(pehringer.info)Memanfaatkan konkurensi dan paralelisme di Go
- Pengantar tentang proyek yang bertujuan meningkatkan kemampuan komputasi numerik dengan memanfaatkan konkurensi dan paralelisme di Go.
- Dengan menggunakan instruksi SIMD (Same Instruction Multiple Data), komputasi paralel dapat dijalankan pada level perangkat keras.
- Compiler Go tidak memanfaatkan SIMD, dan karena tidak menemukan paket SIMD serbaguna yang sesuai, penulis memutuskan untuk mengembangkan paketnya sendiri.
Bahasa assembly Plan9
- Go menggunakan bahasa assembly miliknya sendiri bernama Plan9, yang memakai instruksi dan register platform tertentu dengan sedikit modifikasi.
- Plan9 untuk x86 dan Plan9 untuk ARM berbeda satu sama lain.
- Melalui contoh sederhana Plan9, artikel ini menjelaskan cara penggunaan dasarnya.
Contoh Plan9
- Melalui file
AddInts_amd64.sdanmain.go, artikel ini menjelaskan deklarasi fungsi dasar di Plan9 dan cara menggunakannya. - Dijelaskan juga cara menyimpan argumen fungsi dan nilai kembalian ke stack sesuai calling convention Go.
Rencana desain paket
- Dirancang sebuah paket yang menyediakan lapisan abstraksi tipis untuk operasi SIMD aritmetika dan bit.
- Dibuat paket internal yang mencakup implementasi Plan9 per arsitektur, lalu dikonfigurasi melalui fungsi inisialisasi.
Contoh SIMD
- Cara menggunakan SIMD dijelaskan melalui contoh fungsi Plan9 SIMD untuk x86.
- Melalui file
Supported_amd64.sdanAddFloat32_amd64.s, ditunjukkan cara memeriksa dukungan SSE dan menjalankan operasi penjumlahanfloat32.
Performa dan masa depan
- Melalui grafik yang menunjukkan perbedaan performa antara implementasi perangkat lunak Go dan implementasi Plan9 SIMD, terlihat peningkatan kecepatan sekitar 200–450%.
- Penulis berharap memo ini dapat menginspirasi proyek yang menggunakan Plan9 dan SIMD.
# Ringkasan GN⁺
- Artikel ini memperkenalkan cara memaksimalkan performa dengan memanfaatkan konkurensi dan paralelisme di Go.
- Dijelaskan bagaimana menggunakan bahasa assembly Plan9 dan instruksi SIMD untuk menjalankan komputasi paralel pada level perangkat keras.
- Artikel ini menunjukkan kemungkinan pemanfaatan Plan9 dan SIMD bagi para pengembang Go, dan dapat berguna untuk mengeksplorasi pendekatan baru dalam peningkatan performa.
- Untuk proyek dengan fungsi serupa, direkomendasikan pustaka dukungan SIMD di Rust atau pustaka terkait SIMD di C++.
1 komentar
Komentar Hacker News
Beberapa hal tentang assembly Go: pada amd64,
inttersebut sebenarnya 64-bitJika memakai
int32, daftar parameter akan disejajarkan pada word, tetapi ada jebakannya. Pada sistem 64-bit, nilai kembalian selalu dimulai pada offset yang disejajarkan doublewordNOSPLITdidefinisikan ditextflag.hyang disediakan otomatis oleh compiler Go. Namun dari yang saya baca,NOSPLITtampaknya hanya dihormati pada fungsiruntime.XX, jadi di sini tidak melakukan apa-apa, dan juga tidak diperlukanNOSPLITberarti jangan biarkan compiler menyisipkan kode untuk memeriksa apakah stack mungkin meluap sehingga perlu di-split. Untuk fungsi yang tidak membutuhkan ruang stack, ini secara teknis tidak diperlukan, dan pada dasarnya ada untuk mencegah kode pemeriksaan itu disuntikkan ke dalam fungsi yang melakukan pemeriksaan stack split itu sendiriBagi yang penasaran mengapa
4disebut mewakili “NOSPLIT” dan katanya diperlukan: biasanya setelah ukuran frame (parameter setelahNOSPLIT) ada ukuran argumen, dan keduanya dipisahkan dengan tanda minusItu bukan operasi pengurangan, melainkan sintaks yang khas saja. Ukuran frame
$24-8berarti fungsi memiliki frame 24 byte dan dipanggil dengan argumen 8 byte di frame pemanggilJika
NOSPLITtidak ditentukan padaTEXT, ukuran argumen wajib diberikan. Untuk fungsi assembly yang memiliki prototipe Go,go vetmemeriksa apakah ukuran argumen sudah benarSumber: https://go.dev/doc/asm
Katanya “Go memakai bahasa assembly internalnya sendiri bernama Plan9”; apakah bahasa itu memang benar-benar disebut begitu?
Sintaksnya memang berasal dari Plan 9, tetapi kami menyebutnya assembly Go
Lihat https://go.dev/doc/asm
Namun semakin saya mencari, semakin saya merasa ini seperti halusinasi LLM
Dokumen format assembly tidak memberinya nama resmi dan hanya menyebutnya
go assemblerSumber halusinasi ini mungkin paragraf pertama: “Assembler didasarkan pada gaya input assembler Plan 9 dan… dokumen ini menjelaskan ringkasan sintaks tersebut, perbedaannya, serta kekhasan saat menulis kode assembly yang berinteraksi dengan Go”
Ini mirip seperti mengatakan “GNU Compiler Collection memakai bahasa assembly internalnya sendiri bernama Unix”
Jika penasaran mengapa tim Go memilih format assembly khusus seperti ini, Rob Pike pernah membahas desain assembler Go dalam presentasi tahun 2016 [1][2]
Intinya tampaknya berangkat dari pengamatan bahwa sebagian besar bahasa assembly secara umum mirip, jadi idenya adalah membuat bahasa assembly umum yang “memungkinkan berbicara dengan level terendah mesin tanpa perlu mempelajari sintaks baru”
Ini juga memungkinkan pembuatan assembler secara otomatis yang bekerja dengan menerima PDF manual instruksi arsitektur baru sebagai input
[1]: https://www.youtube.com/watch?v=KINIAgRpkDA
[2]: https://go.dev/talks/2016/asm.slide#1
Disebutkan “membutuhkan fungsi-fungsi untuk melakukan operasi SIMD pada slice”, tetapi saya penasaran bagaimana sebenarnya itu digunakan
Setelah mencari di seluruh tulisan, saya tidak menemukan operasi apa pun yang dilakukan pada slice
Sunting: menemukannya di dokumen tertaut: https://pkg.go.dev/github.com/pehringer/simd#pkg-index
Pada dasarnya, jika ada 2 slice yang ingin dijumlahkan, alih-alih memakai loop
for, pemrosesannya diparalelkan dengan SIMD dan bisa ditulis sepertisimd.AddInt32(slice1, slice2, result)https://github.com/pehringer/simd/blob/main/simd_test.go
Terkait prosesor yang didukung Go, bagian ini relevan (1). Dukungan x64 dasar mencakup SSE dan SSE2
Namun saya tidak tahu apakah compiler Go benar-benar menghasilkannya. Berbeda dari compiler yang sangat kompleks seperti gcc yang sangat mengutamakan performa, compiler Go lebih memilih compiler yang sederhana dan cepat ala Wirth (2)
(1) https://go.dev/wiki/MinimumRequirements#amd64
(2) https://irreal.org/blog/?p=7075
https://smartgo.blog/2024/01/06/niklaus-wirth/
Sepertinya penulis keliru memahami bagian ini, jadi saya tinggalkan tautan referensi: https://en.wikipedia.org/wiki/Plan_9_from_Bell_Labs
Saya juga sebenarnya hendak mengatakan bahwa ini tampaknya hasil LLM yang salah menafsirkan kode
Sulit dibayangkan dengan cara lain bahwa seseorang tahu istilah Plan 9 dan menggali sampai ke assembly, tetapi tidak menyadari “perairan” seperti apa yang sedang ia masuki. Saya baru tahu setelah melihat orang lain juga berpikir sama
Jika ini benar, saya berharap penulisnya jujur tanpa merasa malu atau merasa “ketahuan”. Dengan begitu kita juga bisa belajar. Saya ingin mendapatkan keyakinan terhadap tipe “paparan LLM” seperti ini, tetapi meski terlihat sangat jelas sekalipun, tampaknya jarang sekali orang mengakuinya
Tentu saja dalam kasus ini belum tentu jelas; ini hanya dugaan yang sangat terburu-buru dan menghakimi
Hal seperti ini benar-benar membuat saya kesal
Isinya sedikit di atas level saya, tetapi saya menikmati cara tulisannya membawa pembaca ikut mengikuti prosesnya
Rasanya ini bukan percobaan pertama yang seperti ini, kan? Sepertinya ada puluhan Gopher yang benar-benar haus akan SIMD. Apakah pola yang lebih umum adalah memakai CGO?
Memanggil fungsi assembly dari Go jauh lebih murah
Di https://pkg.go.dev/github.com/grailbio/base/simd ada beberapa pekerjaan saya ke arah ini
Kalau ingatan saya benar, waktu itu beberapa instruksi AVX bahkan belum bisa di-encode oleh assembler Plan 9 milik Go, jadi harus di-encode langsung sebagai byte [0]
Library paling matang yang pernah saya lihat—meski saya belum pernah memakainya—menggunakan sebagian CGO sambil memakai hack yang rapi untuk menghindari overhead itu [1]
[0]: https://github.com/slimsag/rand/blob/f1e8d464c0021a391d5cd64...
[1]: https://github.com/alivanz/go-simd/
Jika ingin menulis program assembly Go, saya sarankan melihat Avo(https://github.com/mmcloughlin/avo)
Ini menyediakan type safety dan melakukan beberapa pemeriksaan untuk memastikan assembly yang dihasilkan valid. Ia bisa mengalokasikan register secara dinamis, dan membuat Anda tidak perlu menghitung sendiri hal-hal seperti ukuran stack dan frame
Ia juga bisa menangani detail calling convention, sehingga Anda dapat dengan mudah memuat argumen ke register atau lokasi yang diinginkan
Baru-baru ini saya mem-porting seluruh assembly amd64 di library crypto Go ke Avo, dan library ini sangat berguna untuk pekerjaan semacam itu