4 poin oleh GN⁺ 2024-10-19 | 1 komentar | Bagikan ke WhatsApp

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.s dan main.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.s dan AddFloat32_amd64.s, ditunjukkan cara memeriksa dukungan SSE dan menjalankan operasi penjumlahan float32.

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

 
GN⁺ 2024-10-19
Komentar Hacker News
  • Beberapa hal tentang assembly Go: pada amd64, int tersebut sebenarnya 64-bit
    Jika memakai int32, daftar parameter akan disejajarkan pada word, tetapi ada jebakannya. Pada sistem 64-bit, nilai kembalian selalu dimulai pada offset yang disejajarkan doubleword
    NOSPLIT didefinisikan di textflag.h yang disediakan otomatis oleh compiler Go. Namun dari yang saya baca, NOSPLIT tampaknya hanya dihormati pada fungsi runtime.XX, jadi di sini tidak melakukan apa-apa, dan juga tidak diperlukan
    NOSPLIT berarti 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 sendiri

  • Bagi yang penasaran mengapa 4 disebut mewakili “NOSPLIT” dan katanya diperlukan: biasanya setelah ukuran frame (parameter setelah NOSPLIT) ada ukuran argumen, dan keduanya dipisahkan dengan tanda minus
    Itu bukan operasi pengurangan, melainkan sintaks yang khas saja. Ukuran frame $24-8 berarti fungsi memiliki frame 24 byte dan dipanggil dengan argumen 8 byte di frame pemanggil
    Jika NOSPLIT tidak ditentukan pada TEXT, ukuran argumen wajib diberikan. Untuk fungsi assembly yang memiliki prototipe Go, go vet memeriksa apakah ukuran argumen sudah benar
    Sumber: https://go.dev/doc/asm

  • Katanya “Go memakai bahasa assembly internalnya sendiri bernama Plan9”; apakah bahasa itu memang benar-benar disebut begitu?

    • Tidak. Itu hanya assembly Go
      Sintaksnya memang berasal dari Plan 9, tetapi kami menyebutnya assembly Go
      Lihat https://go.dev/doc/asm
    • Pertanyaan yang masuk akal. Awalnya saya juga mengira itu benar. Saya pikir orang yang meneliti topik ini tidak mungkin salah soal hal seperti ini, dan kalau sedikit mengenal proyeknya, nama itu juga terasa cukup masuk akal
      Namun semakin saya mencari, semakin saya merasa ini seperti halusinasi LLM
      Dokumen format assembly tidak memberinya nama resmi dan hanya menyebutnya go assembler
      Sumber 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”
    • Tidak ada nama tersendiri. Plan 9 adalah sistem operasi, dan gaya sintaks assembly ini berasal dari assembler yang digunakan di sistem operasi tersebut
      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

    • Dalam praktiknya memang efektif. Go menciptakan ekspektasi bahwa bahasa pemrograman baru seharusnya memiliki cross-compilation sebagai kemampuan dasar, dan pada waktu itu hampir tidak ada bahasa yang bagus dalam hal ini
    • Sebagai catatan, SpiderMonkey juga sudah melakukan hal yang kurang lebih sama selama sekitar 25 tahun, dan saya kira mesin virtual JavaScript lain juga begitu
  • 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 seperti simd.AddInt32(slice1, slice2, result)

  • 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/

    • Dalam praktiknya, saya rasa semua chip sejak sekitar 2008 mendukung SSE4.1
  • 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

    • Jujur? Tidak. Kesombongan karena menganggap LLM sebagai jalan pintas sehingga tidak perlu melakukan pekerjaan yang seharusnya dilakukan dengan benar itu memang patut dipermalukan
      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?

    • Masalah cgo adalah overhead pemanggilan fungsinya besar. Sebaiknya dipakai hanya untuk potongan pekerjaan yang cukup besar
      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
    • Saya rasa orang-orang pasti sudah mencobanya sejak dulu. Saya ingat kira-kira hampir 10 tahun lalu pernah menelepon saudara saya saat ia mencoba membuat library SIMD di Go (Skype mungkin?)
      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/
    • Usulan untuk menambahkan hal seperti ini ke standard library ditolak, tetapi ini membantu memahami latar belakangnya: https://github.com/golang/go/issues/53171
  • 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