Aritmetika SSE

Operasi packed integer dan floating point untuk empat nilai sekaligus pada register xmm.

Daftar isi

Topik ini membahas aritmetika packed pada register xmm, yaitu operasi yang menghitung empat nilai sekaligus dalam satu instruksi. Pasangan addps dan mulps mengolah empat float, sedangkan paddd dan pmulld mengolah empat integer 32-bit. Mekanisme empat ke satu ini diperkenalkan terlebih dahulu, dilanjutkan dengan keluarga instruksinya. Satu program di bagian akhir menghitung konversi suhu empat nilai sekaligus serta kuadrat empat integer, semuanya diperiksa lewat keluaran gdb yang asli.

Satu instruksi, empat hasil#

Setiap instruksi aritmetika packed bekerja pada keempat lane secara serentak dan independen. Lane 0 dijumlahkan dengan lane 0 operandnya, lane 1 dengan lane 1, dan seterusnya, tanpa ada nilai yang meluber ke lane lain. Dari sisi logika, satu addps menggantikan empat addss skalar, sebagaimana satu muat movaps menggantikan empat muat 32-bit. Jumlah instruksi untuk mengolah blok data menjadi seperempat dari versi skalar, dan itulah sumber keuntungan utama SIMD. Instruksi pergeseran dan perbandingan melengkapi keluarga ini, tetapi empat operasi dasarnya sudah cukup untuk membangun hampir semua perhitungan packed sederhana.

Klaim “empat kali lebih cepat” perlu dibaca dengan hati-hati karena waktu eksekusi nyata juga ditentukan throughput instruksi, latensi memori, dan kemampuan prosesor mengeksekusi beberapa instruksi sekaligus. Apa yang pasti adalah jumlah operasi yang tercatat di kode, yaitu satu instruksi membawa empat perhitungan, sehingga compiler dan pemrogram memiliki jalan untuk memperbanyak pekerjaan sekaligus pada register yang sama. Pengukuran yang adil memerlukan benchmark, yaitu pengukuran waktu pada program yang benar-benar berjalan, bukan pembacaan jumlah instruksi saja.

Instruksi float dan integer#

SSE menyediakan satu keluarga lengkap untuk float 32-bit, di antaranya addps, subps, mulps, dan divps, yang semuanya menerima dua operand dengan salah satunya boleh berupa alamat memori. Untuk integer 32-bit, paddd menjumlahkan dan psubd mengurangkan sebagai bagian dari SSE2, sedangkan pmulld melipatgandakan sebagai tambahan SSE4.1. Muat integer menggunakan movdqa, kembaran movaps untuk penafsiran integer, dan keduanya menuntut alamat selaras yang sama. Nama-nama ini mengikuti pola yang konsisten, yaitu akhiran ps untuk packed single dan akhiran d untuk dword, sehingga bentuk operan instruksi baru pada keluarga ini mudah ditebak.

Pola konstanta packed itu tampak pada program di bagian akhir. Selain menghindari pembacaan byte milik label lain, konstanta empat lane membuat maksud kode mudah dibaca, karena setiap lane menerima faktor yang sama. Pemilihan xmm1 sebagai register kerja juga bebas dilakukan, karena konvensi pemanggilan menggolongkan seluruh register xmm sebagai register yang dijaga pemanggil, sehingga isinya memang tidak dijamin utuh melewati pemanggilan fungsi.

Konversi suhu empat nilai sekaligus#

Konversi Celsius ke Fahrenheit digunakan sebagai contoh karena rumusnya pendek, yaitu F=C×1,8+32F = C \times 1{,}8 + 32, tetapi melibatkan dua operasi aritmetika berbeda yang keduanya harus diterapkan pada setiap nilai. Pada versi skalar, dua operasi itu ditulis berulang untuk setiap suhu, sedangkan versi packed menuliskannya sekali dan membiarkan lane menyebarluaskannya. Kedua operasi juga ditulis berurutan pada register yang sama, sehingga hasil antara tidak pernah keluar dari register dan tidak pernah ditulis ke memori.

Program berikut mengonversi empat suhu sekaligus menggunakan mulps dan addps, kemudian menghitung kuadrat empat bilangan genap menggunakan paddd dan pmulld. gdb menghentikan program setelah enam instruksi perhitungan selesai dan mencetak kedua register hasilnya.

nasm aritmetika-sse.asm
default rel

section .data
    align 16
celsius:   dd 0.0, 25.0, 50.0, 100.0
    align 16
faktor:    dd 1.8, 1.8, 1.8, 1.8
    align 16
offset:    dd 32.0, 32.0, 32.0, 32.0
    align 16
jumlah:    dd 3, 5, 7, 9
satu:      dd 1, 1, 1, 1

section .text
global _start

_start:
    movaps xmm0, [celsius]   ; muat empat suhu
    mulps  xmm0, [faktor]    ; kali 1.8 pada empat lane sekaligus
    addps  xmm0, [offset]    ; tambah 32 pada empat lane sekaligus

    movdqa xmm1, [jumlah]    ; muat empat integer
    paddd  xmm1, [satu]      ; tambah satu pada empat lane
    pmulld xmm1, xmm1        ; kuadratkan empat lane

    mov  eax, 60
    xor  edi, edi
    syscall
bash
# rakit dan tautkan program
nasm -f elf64 aritmetika-sse.asm -o aritmetika-sse.o
ld aritmetika-sse.o -o aritmetika-sse

# hasil kedua perhitungan setelah enam instruksi dijalankan
gdb -batch -ex 'break _start' -ex 'run' -ex 'stepi 6' -ex 'p $xmm0.v4_float' -ex 'p $xmm1.v4_int32' ./aritmetika-sse
text Keluaran
Breakpoint 1 at 0x401000

Breakpoint 1, 0x0000000000401000 in _start ()
0x000000000040102a in _start ()
$1 = {32, 77, 122, 212}
$2 = {16, 36, 64, 100}

Keluaran menunjukkan xmm0 berisi {32, 77, 122, 212}, yaitu hasil konversi dari 0, 25, 50, dan 100 derajat Celsius dalam urutan lane yang sama dengan array asalnya. Nilai-nilai tersebut tampak bulat tanpa pecahan karena pasangan nilai suhu dan faktor pembandingnya dipilih sedemikian rupa sehingga hasilnya terwakili dengan tepat pada float 32-bit, sedangkan arahan align 16 menjamin setiap label konstanta memenuhi syarat alamat operan memori. Perhitungan yang sama pada versi skalar membutuhkan delapan instruksi aritmetika untuk empat suhu, dibandingkan dengan dua instruksi packed di sini. Register xmm1 terbaca {16, 36, 64, 100}, yaitu kuadrat dari 4, 6, 8, dan 10 yang diperoleh setelah paddd menambah satu pada setiap lane. Dua register dengan tipe penafsiran berbeda bekerja pada program yang sama tanpa saling mengganggu, sesuai sifat register xmm yang bitnya netral terhadap tipe data.

Ringkasan#

  • Instruksi packed seperti addps, mulps, paddd, dan pmulld menghitung empat lane sekaligus, sehingga jumlah instruksi untuk blok data menjadi seperempat versi skalar.
  • Operan memori instruksi packed dibaca 16 byte utuh, sehingga konstantanya harus berupa vektor empat lane yang selaras.
  • paddd berasal dari SSE2, sedangkan pmulld membutuhkan SSE4.1, dan keduanya mengolah integer 32-bit pada register yang sama dengan float.
  • Hasil tiap lane tetap berurutan dari array asalnya, yaitu lane 0 menampung elemen pertama.

Referensi#