Perang antara RISC dan CISC pada akhir 1990-an telah lama berakhir, dan saat ini diyakini bahwa perbedaan antara RISC dan CISC sama sekali tidak relevan. Banyak orang mengklaim bahwa set perintah tidak relevan.
Namun, set perintah sebenarnya penting. Mereka menempatkan batasan pada jenis pengoptimalan yang dapat dengan mudah ditambahkan ke mikroprosesor.
Baru-baru ini saya melihat lebih dekat informasi tentang arsitektur set instruksi (ISA) RISC-V, dan berikut adalah beberapa aspek yang benar-benar membuat saya terkesan tentang RISC-V ISA:
- Ini adalah set perintah RISC yang kecil dan mudah dipelajari. Sangat disukai bagi mereka yang tertarik untuk menimba ilmu tentang mikroprosesor.
- , , .
- CPU ISA RISC-V.
- , , RISC-V.
RISC
Ketika saya mulai memahami RISC-V dengan lebih baik, saya menyadari bahwa RISC-V ternyata merupakan kembalinya radikal ke apa yang diyakini banyak orang sebagai era komputasi lampau. Dari sudut pandang desain, RISC-V pada mesin yang sama dengan waktu gerakan untuk klasik R educed saya nstruction S et C omputer (RISC, «komputer dengan satu set perintah pendek") awal 80-an dan 90-an.
Dalam beberapa tahun terakhir, banyak yang berpendapat bahwa pembagian menjadi RISC dan CISC tidak lagi masuk akal, karena begitu banyak instruksi telah ditambahkan ke prosesor RISC seperti ARM, dan sementara banyak dari mereka cukup kompleks, bahwa pada tahap saat ini lebih merupakan hibrida daripada prosesor RISC murni. Pertimbangan serupa telah diterapkan pada prosesor RISC lain seperti PowerPC.
RISC-V, di sisi lain, adalah perwakilan prosesor RISC yang benar-benar "hardcore". Jika Anda membaca tentang diskusi RISC-V di Internet, Anda akan menemukan orang-orang yang mengklaim bahwa RISC-V dikembangkan oleh beberapa radikal RISC sekolah lama yang menolak mengikuti perkembangan zaman.
Mantan insinyur ARM Erin Shepherd menulis kritik menarik tentang RISC-V beberapa tahun lalu :
ISA RISC-V . , .. (, , ) , .
Saya akan memberikan sedikit konteks secara singkat. Ukuran kode yang kecil memiliki keunggulan kinerja karena membuatnya lebih mudah untuk menyimpan kode yang dapat dieksekusi di dalam cache berkecepatan tinggi prosesor.
Kritik di sini adalah bahwa desainer RISC-V terlalu fokus pada penyediaan set instruksi kecil. Bagaimanapun, ini adalah salah satu tujuan awal RISC.
Menurut Erin, konsekuensi dari hal ini adalah program yang sebenarnya membutuhkan lebih banyak instruksi untuk menyelesaikan tugasnya, yaitu membutuhkan lebih banyak ruang memori.
Secara tradisional, selama bertahun-tahun, diyakini bahwa lebih banyak instruksi harus ditambahkan ke prosesor RISC agar lebih mirip CISC. Idenya adalah bahwa perintah yang lebih khusus dapat menggantikan penggunaan beberapa perintah umum.
Kompresi Perintah dan Penggabungan Operasi Makro
Namun, ada dua inovasi dalam arsitektur prosesor yang membuat strategi penambahan instruksi yang lebih kompleks ini menjadi mubazir:
- Instruksi Terkompresi - Instruksi dikompresi dalam memori dan didekompresi pada tahap pertama prosesor.
- Operasi Makro Fusion - Dua atau lebih instruksi sederhana dibaca oleh prosesor dan digabungkan menjadi satu instruksi yang lebih kompleks.
Faktanya, ARM sudah menggunakan kedua strategi ini, dan prosesor x86 menggunakan yang kedua, jadi RISC-V tidak melakukan trik baru di sini.
Namun, ada seluk-beluk di sini: RISC-V mendapat lebih banyak manfaat dari dua strategi ini karena dua alasan penting:
- Perintah terkompresi awalnya ditambahkan. Arsitektur lain, seperti ARM, memikirkan hal ini nanti dan mengencangkannya dengan cara yang agak terburu-buru.
- Di sinilah obsesi RISC dengan sejumlah kecil tim unik membenarkan dirinya sendiri. Ada lebih banyak ruang tersisa untuk menambahkan perintah terkompresi.
Poin kedua membutuhkan klarifikasi. Dalam arsitektur RISC, perintah biasanya berukuran lebar 32 bit. Bit-bit ini perlu digunakan untuk menyandikan berbagai informasi. Katakanlah kita memiliki perintah seperti ini (ada komentar setelah titik koma):
ADD x1, x4, x8 ; x1 ← x4 + x8
Ia menambahkan isi register
x4
dan
x8
menyimpan hasilnya
x1
. Jumlah bit yang diperlukan untuk menyandikan instruksi ini tergantung pada jumlah register yang tersedia. RISC-V dan ARM memiliki 32 register. Angka 32 dapat diekspresikan dalam 5 bit:
2⁵ = 32
Karena perintah perlu menentukan tiga register berbeda, total 15 bit (3 × 5) diperlukan untuk menyandikan operan (data input untuk operasi penambahan).
Oleh karena itu, semakin banyak fitur yang ingin kita dukung dalam set instruksi, semakin banyak bit yang akan kita ambil dari 32 bit yang tersedia untuk kita. Tentu saja, kita dapat beralih ke perintah 64-bit, tetapi ini akan menghabiskan terlalu banyak memori, yang berarti kinerja akan terganggu.
Dalam upaya agresif untuk menjaga jumlah instruksi tetap kecil, RISC-V menyisakan lebih banyak ruang untuk menambahkan bit untuk menunjukkan bahwa kami menggunakan instruksi terkompresi. Jika prosesor melihat bahwa bit tertentu disetel dalam perintah, maka ia memahami bahwa ia perlu ditafsirkan sebagai terkompresi.
Ini berarti bahwa alih-alih menempelkan 32 bit dari satu instruksi di dalamnya, kita dapat memasukkan dua instruksi dengan lebar masing-masing 16 bit. Biasanya, tidak semua perintah RISC-V dapat diekspresikan dalam format 16-bit. Oleh karena itu, subset dari instruksi 32-bit dipilih berdasarkan kegunaan dan frekuensi penggunaannya. Sedangkan instruksi yang tidak terkompresi dapat menerima 3 operan (data masukan), maka instruksi yang dikompresi hanya dapat menerima 2 operan. Artinya, perintah terkompresi
ADD
akan terlihat seperti ini:
C.ADD x4, x8 ; x4 ← x4 + x8
Kode perakitan RISC-V menggunakan awalan
C.
untuk menunjukkan bahwa perintah harus dirakit menjadi perintah terkompresi.
Pada dasarnya, instruksi terkompresi mengurangi jumlah operan. Tiga register operan akan membutuhkan 15 bit, hanya menyisakan 1 bit untuk menunjukkan operasi! Jadi, ketika menggunakan dua operan untuk menunjukkan opcode (operasi yang akan dilakukan), kami memiliki 6 bit tersisa.
Ini sebenarnya mendekati cara kerja assembler x86 ketika bit yang dicadangkan tidak cukup untuk menggunakan tiga register operan. Prosesor x86 menggunakan bit untuk memungkinkan, misalnya, perintah untuk
ADD
membaca data yang masuk dari memori dan register.
Namun, memang benarkami mendapatkan keuntungan dari menggabungkan kompresi perintah dengan fusi operasi makro. Ketika prosesor menerima kata 32-bit yang berisi dua instruksi 16-bit terkompresi, itu dapat menggabungkannya menjadi satu instruksi yang lebih kompleks.
Kedengarannya tidak masuk akal - apakah kita kembali ke awal?
Tidak, karena kami melewati kebutuhan untuk mengisi spesifikasi ISA dengan sekumpulan instruksi yang rumit (yaitu mengikuti strategi ARM). Sebaliknya, kami, pada dasarnya, mengekspresikan seluruh perintah kompleks secara tidak langsung , melalui berbagai kombinasi perintah sederhana.
Dalam keadaan normal, fusi makro akan menyebabkan masalah: meskipun dua instruksi diganti dengan satu, instruksi tersebut masih menggunakan memori dua kali lebih banyak. Namun, saat mengompresi perintah, kami tidak menggunakan ruang ekstra. Kami memanfaatkan kedua arsitektur tersebut.
Mari kita lihat salah satu contoh yang diberikan oleh Erin Shepherd. Dalam artikel kritisnya tentang ISA RISC-V, dia menunjukkan fungsi sederhana di C. Untuk membuatnya lebih jelas, saya mengambil kebebasan untuk menulis ulang:
int get_index(int *array, int i) {
return array[i];
}
Pada x86, ini akan mengkompilasi ke kode assembly berikut ini:
mov eax, [rdi+rsi*4]
ret
Ketika suatu fungsi dipanggil dalam bahasa pemrograman, argumen biasanya diteruskan ke fungsi dalam register sesuai dengan urutan yang ditetapkan yang bergantung pada set instruksi yang digunakan. Pada x86, argumen pertama ditempatkan di register
rdi
, argumen kedua masuk
rsi
. Secara standar, nilai kembali harus ditempatkan dalam register
eax
.
Perintah pertama mengalikan konten
rsi
dengan 4. Ini berisi variabel
i
. Mengapa berkembang biak? Karena
array
terdiri dari elemen integer yang dipisahkan oleh 4 byte. Oleh karena itu, elemen ketiga dari array berada pada offset 3 × 4 = 12 byte.
Selanjutnya kami menambahkan ini
rdi
yang berisi alamat dasar
array
... Ini memberi kita alamat akhir dari
i
elemen th
array
. Kami membaca isi sel memori di alamat ini dan menyimpannya di
eax
: tugas selesai.
Di ARM, semuanya terjadi dengan cara yang sama:
LDR r0, [r0, r1, lsl #2]
BX lr ;return
Di sini kita tidak mengalikan dengan 4, tetapi menggeser register
r1
2 bit ke kiri, yang setara dengan mengalikan dengan 4. Ini mungkin deskripsi yang lebih akurat tentang apa yang terjadi pada x86. Saya ragu apakah mungkin untuk mengalikan dengan apa pun yang bukan kelipatan 2, karena perkalian adalah operasi yang agak rumit, dan perpindahan itu tidak mahal dan mudah.
Dari uraian saya tentang x86, sisanya adalah tebakan siapa pun. Sekarang mari kita ke RISC-V, di mana kesenangan sebenarnya dimulai! (komentar dimulai dengan titik koma)
SLLI a1, a1, 2 ; a1 ← a1 << 2
ADD a0, a0, a1 ; a0 ← a0 + a1
LW a0, a0, 0 ; a0 ← [a0 + 0]
RET
Di RISC-V, register
a0
dan
a1
hanyalah alias untuk
x10
dan
x11
. Di sinilah argumen pertama dan kedua dari pemanggilan fungsi ditempatkan.
RET
Adalah pseudo-command (singkatan):
JALR x0, 0(ra) ; sp ← 0 + ra
; x0 ← sp + 4 ignoring result
JALR
menavigasi ke alamat yang disimpan di
ra
yang merujuk ke alamat pengirim.
ra
Apakah nama samaran
x1
.
Dan semuanya terlihat sangat buruk, bukan? Perintah dua kali lebih banyak untuk operasi sederhana dan umum digunakan seperti melakukan pencarian indeks pada tabel dan mengembalikan hasil.
Ini benar-benar terlihat buruk. Inilah mengapa Erin Shepherd sangat kritis terhadap keputusan desain yang dibuat oleh pengembang RISC-V. Dia menulis:
Penyederhanaan RISC-V membuat decoder (yaitu prosesor front-end) lebih sederhana, tetapi memerlukan lebih banyak instruksi. Namun, menskalakan lebar pipeline adalah tugas yang rumit, sementara decoding beberapa (atau sangat) instruksi yang tidak biasa dipelajari dengan baik (kesulitan utama muncul saat menentukan panjang instruksi tidak sepele - karena prefiksnya yang tak terbatas, x86 adalah kasus yang terabaikan).
Namun, berkat kompresi perintah dan fusi makro-op, situasinya dapat diubah menjadi lebih baik.
C.SLLI a1, 2 ; a1 ← a1 << 2
C.ADD a0, a1 ; a0 ← a0 + a1
C.LW a0, a0, 0 ; a0 ← [a0 + 0]
C.JR ra
Sekarang instruksi menggunakan jumlah ruang memori yang sama persis dengan contoh untuk ARM.
Oke, sekarang mari kita lakukan fusi Macro-op !
Salah satu syarat RISC-V untuk memungkinkan operasi penggabungan menjadi satu adalah bahwa register target cocok . Kondisi ini terpenuhi untuk perintah
ADD
dan
LW
(memuat kata, "memuat kata"). Oleh karena itu, prosesor akan mengubahnya menjadi satu instruksi.
Jika kondisi ini terpenuhi untuk SLLI, maka kita bisa menggabungkan ketiga perintah menjadi satu . Artinya, prosesor akan melihat sesuatu yang menyerupai instruksi ARM yang lebih kompleks:
LDR r0, [r0, r1, lsl #2]
Tetapi mengapa kita tidak dapat menulis operasi makro yang kompleks ini secara langsung di dalam kode?
Karena ISA tidak mendukung operasi makro seperti itu! Ingatlah bahwa kita memiliki jumlah bit yang terbatas . Kalau begitu, mari kita buat perintahnya lebih lama! Tidak, ini akan memakan terlalu banyak memori dan meluap lebih cepat cache prosesor yang berharga.
Namun, jika sebaliknya kami mengeluarkan instruksi panjang dan semi-kompleks ini di dalam prosesor, maka tidak ada masalah yang muncul. Prosesor tidak pernah memiliki lebih dari beberapa ratus instruksi pada saat yang bersamaan. Oleh karena itu, jika kita menghabiskan setiap perintah, katakanlah, 128 bit, maka ini tidak akan menimbulkan kesulitan. Masih akan ada cukup silikon untuk semuanya.
Ketika decoder menerima perintah biasa, biasanya ia mengubahnya menjadi satu atau lebih operasi mikro. Operasi mikro ini adalah instruksi yang sebenarnya digunakan oleh prosesor. Mereka bisa sangat luas dan berisi banyak informasi berguna tambahan. Awalan "mikro" terdengar ironis, karena lebih lebar. Namun, pada kenyataannya, "mikro" berarti mereka memiliki jumlah tugas yang terbatas.
Sekering operasi makro mengubah pekerjaan decoder sedikit terbalik: alih-alih mengubah satu perintah menjadi beberapa operasi mikro, kami mengambil banyak operasi dan mengubahnya menjadi satu operasi mikro.
Artinya, apa yang terjadi di prosesor modern mungkin terlihat agak aneh:
- Pertama, ini menggabungkan dua tim menjadi satu menggunakan kompresi .
- Dia kemudian membaginya menjadi dua menggunakan membongkar .
- Ini kemudian menggabungkannya kembali menjadi satu operasi menggunakan fusi makro-op .
Perintah lain dapat dipecah menjadi beberapa operasi mikro, dan tidak dapat digabungkan. Mengapa beberapa tim bergabung sementara yang lain berpisah? Apakah ada sistem dalam kegilaan ini?
Aspek kunci dari transisi ke operasi mikro adalah tingkat kerumitan yang diinginkan:
- Tidak terlalu rumit, karena jika tidak, mereka tidak akan dapat menyelesaikan dalam jumlah siklus clock tetap yang dialokasikan untuk setiap perintah.
- Tidak terlalu sederhana, karena kalau tidak kita hanya akan menyia-nyiakan sumber daya prosesor. Melakukan dua operasi mikro akan memakan waktu dua kali lebih lama daripada melakukan hanya satu.
Semuanya dimulai dengan prosesor CISC. Intel mulai membagi instruksi CISC yang kompleks menjadi operasi mikro untuk membuatnya lebih mudah untuk dimasukkan ke dalam pipeline prosesor seperti instruksi RISC. Namun, dalam konstruksi berikutnya, pengembang menyadari bahwa banyak tim CISC dapat digabungkan menjadi satu tim yang cukup kompleks. Jika ada lebih sedikit perintah untuk dijalankan, pekerjaan akan selesai lebih cepat.
Manfaat yang didapat
Banyak detail yang sudah kita bahas, jadi sekarang pasti sulit bagi Anda untuk memahami apa arti semua karya ini. Untuk apa semua kompresi dan penggabungan ini? Mereka sepertinya melakukan banyak pekerjaan yang tidak perlu.
Pertama, kompresi perintah sama sekali tidak seperti kompresi zip. Kata "kompresi" agak menyesatkan, karena kompresi atau dekompresi instan suatu perintah sangatlah mudah. Tidak ada waktu yang terbuang untuk ini.
Hal yang sama berlaku untuk fusi operasi makro. Meskipun proses ini mungkin tampak rumit, sistem serupa telah digunakan dalam mikroprosesor modern. Oleh karena itu, biaya yang ditambahkan oleh semua kerumitan ini telah dibayar.
Namun, tidak seperti perancang ARM, MIPS, dan x86, ketika mereka mulai merancang ISA mereka, pembuat RISC-V mengetahui tentang kompresi perintah dan fusi operasi makro. Melalui berbagai tes dengan set instruksi minimal pertama, mereka membuat dua penemuan penting:
- Program RISC-V biasanya menempati ruang memori yang hampir sama atau lebih sedikit daripada arsitektur prosesor lainnya. Termasuk x86, yang harus menggunakan memori secara efisien, mengingat itu adalah ISA CISC.
- Itu perlu melakukan operasi mikro lebih sedikit daripada ISA lainnya.
Faktanya, dengan merancang set instruksi dasar dengan fusi dalam pikiran, mereka mampu menggabungkan instruksi yang cukup sehingga prosesor untuk program apapun harus melakukan operasi mikro yang lebih sedikit daripada prosesor pesaing.
Hal ini mendorong tim pengembangan RISC-V untuk menggandakan upaya untuk mengimplementasikan fusi operasi makro sebagai strategi RISC-V yang mendasar. Manual RISC-V memiliki banyak catatan tentang operasi apa yang dapat Anda gabungkan. Ini juga mencakup beberapa perbaikan untuk membuatnya lebih mudah untuk menggabungkan perintah yang ditemukan dalam pola umum.
ISA kecil memudahkan siswa untuk belajar. Ini berarti lebih mudah bagi mahasiswa arsitektur prosesor untuk mendesain prosesor mereka sendiri yang berjalan pada instruksi RISC-V. Perlu diingat bahwa kompresi perintah dan fusi makro-op bersifat opsional.
RISC-V memiliki set perintah dasar kecil yang harus diterapkan. Namun, semua perintah lain diimplementasikan sebagai bagian dari ekstensi. Perintah terkompresi hanyalah ekstensi opsional.
Fusi makro-op hanyalah pengoptimalan. Ini tidak mengubah perilaku secara umum, dan oleh karena itu tidak perlu diterapkan di prosesor RISC-V Anda sendiri.
Strategi desain RISC-V
RISC-V mengambil semua yang kami ketahui tentang prosesor modern saat ini dan menggunakan pengetahuan tersebut untuk merancang prosesor ISA. Misalnya, kita tahu bahwa:
- Inti prosesor saat ini memiliki sistem prediksi cabang yang canggih.
- Inti prosesor adalah superskalar, yaitu menjalankan banyak instruksi secara paralel.
- Untuk memastikan superscalarity, eksekusi perintah dengan perubahan urutan (Eksekusi Out-of-Order) digunakan.
- Mereka memiliki konveyor.
Artinya, fitur seperti eksekusi bersyarat yang didukung ARM tidak lagi diperlukan. Dukungan ARM untuk fungsi ini menghilangkan bit dari format instruksi. RISC-V dapat menyimpan bit ini.
Eksekusi bersyarat pada awalnya dirancang untuk menghindari garpu, karena berdampak buruk pada jaringan pipa. Untuk mempercepat kerja prosesor, biasanya ia menerima perintah berikut terlebih dahulu, sehingga segera setelah yang sebelumnya dijalankan pada tahap pertama prosesor, ia dapat mengambil perintah berikutnya.
Dengan percabangan bersyarat, kita tidak dapat mengetahui sebelumnya di mana perintah berikutnya akan berada ketika kita mulai mengisi pipeline. Namun, prosesor superscalar dapat menjalankan kedua cabang secara paralel.
Karena inilah RISV-C juga tidak memiliki register status, karena mereka membuat ketergantungan antar perintah. Semakin independen setiap perintah, semakin mudah untuk dijalankan secara paralel dengan perintah lain.
Pada dasarnya, strategi RISC-V adalah kita dapat membuat ISA sesederhana mungkin dan implementasi minimal dari prosesor RISC-V sesederhana mungkin tanpa perlu keputusan desain yang tidak memungkinkan untuk membuat prosesor berkinerja tinggi.
Periklanan
Perusahaan kami menawarkan server tidak hanya dengan CPU Intel, tetapi juga server dengan prosesor AMD EPYC. Seperti jenis server lainnya, ada banyak pilihan sistem operasi untuk instalasi otomatis, OS apa pun dapat diinstal dari gambar Anda sendiri. Coba sekarang!