Pustaka intrinsik x86 SIMD baru - debug immintrin

Dengan setiap generasi baru prosesor Intel, instruksi vektor baru dan semakin kompleks bermunculan. Meskipun panjang vektor (512 bit) tidak akan tumbuh dalam waktu dekat, tipe data baru dan tipe instruksi akan muncul. Sebagai contoh, siapa yang dapat memahami secara sekilas apa yang dilakukan oleh intrinsik (dan instruksi prosesor yang sesuai)?

Logika ternary bitwise yang menyediakan kemampuan untuk mengimplementasikan fungsi biner tiga-operan; fungsi biner spesifik ditentukan oleh nilai dalam imm8.

__m512i _mm512_mask_ternarylogic_epi32 (__m512i src, __mmask8 k, __m512i a, __m512i b, int imm8) FOR j := 0 to 15 i := j*32 IF k[j] FOR h := 0 to 31 index[2:0] := (src[i+h] << 2) OR (a[i+h] << 1) OR b[i+h] dst[i+h] := imm8[index[2:0]] ENDFOR ELSE dst[i+31:i] := src[i+31:i] FI ENDFOR dst[MAX:512] := 0 

OK, katakanlah kita menemukan cara kerjanya. Tingkat kerumitan berikutnya adalah kode debug yang secara intensif menggunakan intrinsik tersebut.

Mereka yang secara teratur menggunakan intrinsik tahu situs yang sangat berguna - panduan intrinsik Intel . Jika Anda hati-hati melihat cara kerjanya, mudah untuk melihat bahwa javascript front-end mengunduh file data-3.xxxml, yang menjelaskan secara rinci semua intrinsik, dengan kode yang mirip dengan Matlab. (Misalnya, yang saya salin dalam judul posting.)

Tetapi ketika kita menggunakan intrinsik untuk mempercepat kode, kita menulis bukan di Matlab, tetapi di C dan C ++! Tiga bulan lalu, satu klien bertanya kepada saya apakah ada implementasi vektor intrinsik dalam C untuk debugging, dan saya memutuskan untuk menulis parser yang menerjemahkan kode dari Intrinsics Guide ke C. Ternyata perpustakaan yang mengimplementasikan hampir semua intrinsik sehingga Anda dapat masuk ke dalam dengan debugger langkah-demi-langkah ( atau tambahkan debug printf).

Misalnya, operasi dari judul posting berubah menjadi

 for (int j = 0; j <= 15; j++) { if (k & (1 << j)) { for (int h = 0; h <= 31; h++) { int index = ((((src_vec[j] & (1 << h)) >> h) << 2) | (((a_vec[j] & (1 << h)) >> h) << 1) | ((b_vec[j] & (1 << h)) >> h)) & 0x7; dst_vec[j] = (dst_vec[j] & ~(1 << h)) | ((((imm8 & (1 << index)) >> index)) << h); } } else { dst_vec[j] = src_vec[j]; } } 

Benar, ini jauh lebih bisa dimengerti? Tidak juga? Yah, saya baru saja memilih fungsi yang kompleks sebagai contoh. Biasanya, ketika Anda men-debug kode dengan intrinsik, (misalnya, DSP), Anda harus mengingat baik algoritma maupun fitur-fitur dari setiap instruksi. Mengingat bahwa instruksi bekerja dengan vektor yang panjang, dan algoritma DSP sering didasarkan pada matematika yang serius, kepala saya tidak mengatasinya - tidak ada cukup memori jangka pendek dan konsentrasi. Saya menduga bahwa saya tidak sendirian - beberapa kali saya bahkan berpikir bahwa saya telah menemukan bug dalam instruksi. Kemudian, tentu saja, setiap kali ternyata saya salah, dan tidak berhasil membuka bug FDIV baru. Tetapi jika dalam kasus-kasus tersebut saya dapat melakukan debug langkah demi langkah di dalam instruksi, saya akan segera memahami dalam kondisi apa nilai muncul di komponen vektor saya yang tidak saya harapkan.

Pelanggan mengatakan kepada saya bahwa mereka menggunakan perpustakaan ini untuk men-debug fungsi individu dengan intrinsik AVX-512 pada laptop yang hanya mendukung AVX2. Tentu saja, Intel SDE jauh lebih cocok untuk ini - karena sangat akurat meniru semua set instruksi. Saya memiliki satu set unit test (juga secara otomatis dihasilkan) yang untuk setiap intrinsik dari perpustakaan membandingkan hasil pekerjaannya dengan hasil pelaksanaan instruksi assembler yang sesuai. Seperti layaknya tes unit, sebagian besar berfungsi seperti yang diharapkan. Tetapi beberapa intrinsik debug titik mengambang (presisi ganda dan tunggal) tidak selalu bekerja 100% dengan benar. Saya akan mengatakan bahwa kadang-kadang ini semacam matematika terakhir . Dan ada mekanisme pembulatan yang berbeda! IEE754 memiliki banyak kehalusan ...

Ada fitur penting lainnya menggunakan immintrin debug bukan SDE (yang saya tidak setujui dengan cara apa pun, tapi saya tidak bisa menghentikannya). Jika Anda mengkompilasi gcc atau dentang dengan opsi, misalnya, -march = nehalem , maka gcc dan dentang mengembalikan vektor 512-bit dari fungsi pada stack dari fungsi, dan ICC masih mengembalikannya ke ZMM0. Jadi kompiler Intel tidak dapat digunakan dalam mode ini. Dan gcc memiliki opsi berguna -Og , yang membantu dengan debugging, termasuk dengan debug immintrin.

Ada beberapa intrinsik yang tindakan utamanya adalah mengubah isi register, misalnya, atau bendera. Saya tidak menerapkan instruksi seperti itu. Yah, sementara parser saya belum siap, implementasi sekitar 10% dari intrinsik belum tersedia.

Menggunakan immintrin debug sangat sederhana - Anda tidak perlu mengubah kode sumber, tetapi Anda harus menambahkan kompilasi bersyarat untuk menyertakan immintrin_dbg.h daripada immintrin.h dalam kasus debug build.

Anda dapat mengunduhnya di github .

Source: https://habr.com/ru/post/id450964/


All Articles