Saya ingin menggunakan ditingkatkan REP MOVSB (ERMSB) untuk mendapatkan bandwidth yang tinggi untuk kustom memcpy
.
ERMSB diperkenalkan dengan mikroarsitektur Ivy Bridge. Lihat bagian "Ditingkatkan REP MOVSB dan STOSB operasi (ERMSB)" di Intel optimasi manual jika anda don't tahu apa yang ERMSB ini.
Satu-satunya cara yang saya tahu untuk melakukan hal ini secara langsung adalah dengan inline assembly. Saya punya berikut fungsi dari https://groups.google.com/forum/#!topik/gnu.gcc.bantuan/-Bmlm_EG_fE
static inline void *__movsb(void *d, const void *s, size_t n) {
asm volatile ("rep movsb"
: "=D" (d),
"=S" (s),
"=c" (n)
: "0" (d),
"1" (s),
"2" (n)
: "memory");
return d;
}
Ketika saya menggunakan ini namun, bandwidth jauh lebih sedikit dibandingkan dengan memcpy
.
__movsb
mendapat 15 GB/s dan memcpy
mendapatkan 26 GB/s dengan saya i7-6700HQ (Skylake) sistem, Ubuntu 16.10, DDR4@2400 MHz dual channel 32 GB, GCC 6.2.
Mengapa bandwidth yang jauh lebih rendah dengan REP MOVSB
? Apa yang bisa saya lakukan untuk memperbaikinya?
Berikut adalah kode yang saya digunakan untuk tes ini.
//gcc -O3 -march=native -fopenmp foo.c
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stddef.h>
#include <omp.h>
#include <x86intrin.h>
static inline void *__movsb(void *d, const void *s, size_t n) {
asm volatile ("rep movsb"
: "=D" (d),
"=S" (s),
"=c" (n)
: "0" (d),
"1" (s),
"2" (n)
: "memory");
return d;
}
int main(void) {
int n = 1<<30;
//char *a = malloc(n), *b = malloc(n);
char *a = _mm_malloc(n,4096), *b = _mm_malloc(n,4096);
memset(a,2,n), memset(b,1,n);
__movsb(b,a,n);
printf("%d\n", memcmp(b,a,n));
double dtime;
dtime = -omp_get_wtime();
for(int i=0; i<10; i++) __movsb(b,a,n);
dtime += omp_get_wtime();
printf("dtime %f, %.2f GB/s\n", dtime, 2.0*10*1E-9*n/dtime);
dtime = -omp_get_wtime();
for(int i=0; i<10; i++) memcpy(b,a,n);
dtime += omp_get_wtime();
printf("dtime %f, %.2f GB/s\n", dtime, 2.0*10*1E-9*n/dtime);
}
Alasan saya tertarik pada rep movsb
didasarkan dari komentar-komentar ini
Perhatikan bahwa pada Ivybridge dan Haswell, dengan buffer yang besar untuk muat di MLC anda dapat mengalahkan movntdqa menggunakan rep movsb; movntdqa menimbulkan RFO menjadi LLC, rep movsb tidak... rep movsb secara signifikan lebih cepat daripada movntdqa ketika streaming ke memori di Ivybridge dan Haswell (tapi diketahui bahwa pra-Ivybridge itu lambat!)
Berikut ini adalah hasil saya pada sistem yang sama dari tinymembnech.
C copy backwards : 7910.6 MB/s (1.4%)
C copy backwards (32 byte blocks) : 7696.6 MB/s (0.9%)
C copy backwards (64 byte blocks) : 7679.5 MB/s (0.7%)
C copy : 8811.0 MB/s (1.2%)
C copy prefetched (32 bytes step) : 9328.4 MB/s (0.5%)
C copy prefetched (64 bytes step) : 9355.1 MB/s (0.6%)
C 2-pass copy : 6474.3 MB/s (1.3%)
C 2-pass copy prefetched (32 bytes step) : 7072.9 MB/s (1.2%)
C 2-pass copy prefetched (64 bytes step) : 7065.2 MB/s (0.8%)
C fill : 14426.0 MB/s (1.5%)
C fill (shuffle within 16 byte blocks) : 14198.0 MB/s (1.1%)
C fill (shuffle within 32 byte blocks) : 14422.0 MB/s (1.7%)
C fill (shuffle within 64 byte blocks) : 14178.3 MB/s (1.0%)
---
standard memcpy : 12784.4 MB/s (1.9%)
standard memset : 30630.3 MB/s (1.1%)
---
MOVSB copy : 8712.0 MB/s (2.0%)
MOVSD copy : 8712.7 MB/s (1.9%)
SSE2 copy : 8952.2 MB/s (0.7%)
SSE2 nontemporal copy : 12538.2 MB/s (0.8%)
SSE2 copy prefetched (32 bytes step) : 9553.6 MB/s (0.8%)
SSE2 copy prefetched (64 bytes step) : 9458.5 MB/s (0.5%)
SSE2 nontemporal copy prefetched (32 bytes step) : 13103.2 MB/s (0.7%)
SSE2 nontemporal copy prefetched (64 bytes step) : 13179.1 MB/s (0.9%)
SSE2 2-pass copy : 7250.6 MB/s (0.7%)
SSE2 2-pass copy prefetched (32 bytes step) : 7437.8 MB/s (0.6%)
SSE2 2-pass copy prefetched (64 bytes step) : 7498.2 MB/s (0.9%)
SSE2 2-pass nontemporal copy : 3776.6 MB/s (1.4%)
SSE2 fill : 14701.3 MB/s (1.6%)
SSE2 nontemporal fill : 34188.3 MB/s (0.8%)
Perhatikan bahwa pada sistem saya SSE2 copy prefetched
juga lebih cepat dari MOVSB copy
.
Di awal saya tes saya tidak menonaktifkan turbo. Saya dinonaktifkan turbo dan diuji lagi dan itu tampaknya tidak membuat banyak perbedaan. Namun, mengubah daya manajemen tidak membuat perbedaan besar.
Ketika saya melakukan
sudo cpufreq-set -r -g performance
Kadang-kadang saya melihat lebih dari 20 GB/s dengan rep movsb
.
dengan
sudo cpufreq-set -r -g powersave
yang terbaik yang saya lihat adalah sekitar 17 GB/s. Tapi memcpy
tampaknya tidak menjadi sensitif terhadap manajemen daya.
Aku memeriksa frekuensi (menggunakan turbostat
) dengan dan tanpa SpeedStep diaktifkan, dengan kinerja
dan dengan penghematan energi
untuk menganggur, 1 core beban dan 4 core beban. Aku berlari Intel's MKL padat matriks perkalian untuk membuat memuat dan mengatur jumlah benang menggunakan OMP_SET_NUM_THREADS
. Berikut ini adalah tabel hasil (angka dalam GHz).
SpeedStep idle 1 core 4 core
powersave OFF 0.8 2.6 2.6
performance OFF 2.6 2.6 2.6
powersave ON 0.8 3.5 3.1
performance ON 3.5 3.5 3.1
Hal ini menunjukkan bahwa dengan penghematan energi
bahkan dengan SpeedStep keluarga CPU
masih jam ke menganggur frekuensi 0.8 GHz
. It's hanya dengan kinerja
tanpa SpeedStep bahwa CPU berjalan pada frekuensi konstan.
Aku digunakan e.g sudo cpufreq-set-r kinerja
(karena cpufreq-set
memberikan hasil yang aneh) untuk mengubah pengaturan daya. Ini ternyata turbo kembali sehingga saya harus menonaktifkan turbo setelah.
Ini adalah topik yang cukup dekat dengan hati saya dan penyelidikan baru-baru ini, jadi saya'll melihat dari beberapa sudut pandang: sejarah, beberapa catatan teknis (kebanyakan akademik), hasil tes pada kotak saya, dan akhirnya suatu usaha untuk menjawab pertanyaan yang sebenarnya dari kapan dan di mana rep movsb
mungkin masuk akal.
Sebagian, ini adalah panggilan untuk berbagi hasil - jika anda dapat menjalankan Tinymembench dan berbagi hasil bersama dengan rincian dari CPU dan RAM konfigurasi ini akan menjadi besar. Terutama jika anda memiliki 4-channel setup, Ivy Bridge kotak, kotak server, dll.
Sejarah kinerja yang cepat string copy petunjuk telah sedikit tangga-langkah affair - yaitu, periode stagnan kinerja bergantian dengan upgrade besar yang membawa mereka ke baris atau bahkan lebih cepat daripada bersaing pendekatan. Misalnya, ada lonjakan kinerja di Nehalem (sebagian besar menargetkan startup overhead) dan lagi di Ivy Bridge (paling menargetkan total throughput yang besar eksemplar). Anda dapat menemukan berumur satu dekade wawasan tentang kesulitan menerapkan rep movs
petunjuk dari Intel engineer dalam hal ini thread.
Misalnya, dalam panduan sebelumnya pengenalan Ivy Bridge, khas saran adalah untuk menghindari mereka atau menggunakan mereka sangat hati-hati1.
Saat ini (yah, juni 2016) panduan memiliki berbagai membingungkan dan agak tidak konsisten saran, seperti2:
varian tertentu dari implementasi yang dipilih pada waktu eksekusi berdasarkan letak data, keselarasan dan counter (ECX) nilai. Untuk contoh, MOVSB/STOSB dengan REP awalan harus digunakan dengan counter nilai kurang dari atau sama dengan tiga untuk kinerja terbaik. Jadi untuk salinan dari 3 atau kurang byte? Anda don't perlu
rep
awalan untuk itu di tempat pertama, karena dengan mengklaim startup latency ~9 siklus anda hampir pasti lebih baik dengan sederhana DWORD atau QWORDmov
dengan sedikit bit-memutar-mutar untuk masker off yang tidak terpakai byte (atau mungkin dengan 2 eksplisit byte, wordmov ini jika anda tahu ukuran yang tepat tiga). Mereka pergi dengan mengatakan: String MEMINDAHKAN/MENYIMPAN instruksi memiliki beberapa data granularitas. Untuk efisien pergerakan data, data yang lebih besar granularitas yang lebih baik. Ini berarti efisiensi yang lebih baik dapat dicapai dengan menguraikan sebuah sewenang-wenang nilai counter menjadi nomor ganda kata-kata plus satu byte bergerak dengan menghitung nilai kurang dari atau sama dengan 3. Hal ini tentu tampaknya salah pada saat ini perangkat keras dengan ERMSB mana
rep movsbsetidaknya lebih cepat, atau lebih cepat, daripada
movdatau
movqvarian untuk besar eksemplar. Secara umum, bagian itu (3.7.5) saat ini panduan ini berisi campuran yang wajar dan sangat usang saran. Ini adalah umum throughput Intel manual, karena mereka diperbarui secara inkremental fashion untuk setiap arsitektur (dan dimaksudkan untuk menutupi hampir dua dekade senilai arsitektur bahkan pada saat ini manual), dan bagian yang sering tidak diperbarui untuk menggantikan atau membuat bersyarat saran yang doesn't berlaku untuk arsitektur saat ini. Mereka kemudian pergi untuk menutupi ERMSB secara eksplisit di bagian 3.7.6. Saya tidak't pergi selama sisa saran secara mendalam, tapi saya'll merangkum bagian-bagian yang baik dalam "mengapa menggunakan itu" di bawah ini. Penting lainnya klaim dari panduan ini adalah bahwa pada Haswell,
rep movsb` telah ditingkatkan untuk menggunakan 256-bit operasi internal.Pertimbangan Teknis
Ini adalah hanya ringkasan yang mendasari keuntungan dan kerugian yang
rep
instruksi dari implementation standpoint.Keuntungan bagi
rep movs
- Ketika
rep
movs instruksi ini dikeluarkan, CPU knows bahwa seluruh blok dari ukuran yang dikenal akan ditransfer. Hal ini dapat membantu mengoptimalkan pengoperasian di jalan yang tidak diskrit dengan petunjuk, misalnya:
- Menghindari RFO permintaan ketika ia tahu seluruh baris cache akan ditimpa.
- Mengeluarkan prefetch permintaan segera dan tepat. Hardware prefetching melakukan pekerjaan yang baik dalam mendeteksi
memcpy
-seperti pola, tetapi masih membutuhkan waktu beberapa berbunyi untuk menendang dan akan "lebih-prefetch" banyak cache garis luar akhir disalin wilayah.rep movsb
yang tahu persis wilayah ukuran dan dapat prefetch persis.
- Rupanya, tidak ada jaminan pemesanan di antara toko-toko dalam3 tunggal
rep movs
yang dapat membantu menyederhanakan koherensi lalu lintas dan hanya aspek-aspek lain dari blok bergerak, versus sederhanamov
petunjuk yang harus mematuhi agak ketat memori pemesanan4.- Pada prinsipnya,
rep movs
instruksi bisa mengambil keuntungan dari arsitektur berbagai trik yang tidak't terpapar dalam ISA. Misalnya, arsitektur mungkin memiliki luas internal jalur data bahwa ISA memperlihatkan5 danrep movs
bisa menggunakannya secara internal.Kekurangan
rep movsb
harus menerapkan semantik tertentu yang mungkin lebih kuat dari yang mendasari kebutuhan perangkat lunak. Secara khusus,memcpy
melarang tumpang tindih daerah, dan jadi mungkin mengabaikan kemungkinan itu, tapirep movsb
memungkinkan mereka dan harus menghasilkan hasil yang diharapkan. Pada saat ini implementasi sebagian besar mempengaruhi untuk startup overhead, tapi mungkin tidak untuk yang besar-blok throughput. Demikian pula,rep movsb
harus mendukung byte-butiran salinan bahkan jika anda benar-benar menggunakannya untuk menyalin blok besar yang merupakan kelipatan dari beberapa kekuatan besar dari 2.- Perangkat lunak yang mungkin memiliki informasi tentang keselarasan, copy ukuran dan mungkin aliasing yang tidak dapat dikomunikasikan kepada hardware jika menggunakan
rep movsb
. Compiler sering dapat menentukan keselarasan dari memori blok6 dan agar dapat menghindari banyak startup pekerjaan yangrep movs
harus dilakukan pada every doa.Hasil Tes
Berikut ini adalah hasil tes untuk berbagai metode copy dari
tinymembench
pada saya i7-6700HQ pada 2.6 GHz (terlalu buruk aku punya identik CPU sehingga kita tidak't mendapatkan data baru titik...):
C copy backwards : 8284.8 MB/s (0.3%)
C copy backwards (32 byte blocks) : 8273.9 MB/s (0.4%)
C copy backwards (64 byte blocks) : 8321.9 MB/s (0.8%)
C copy : 8863.1 MB/s (0.3%)
C copy prefetched (32 bytes step) : 8900.8 MB/s (0.3%)
C copy prefetched (64 bytes step) : 8817.5 MB/s (0.5%)
C 2-pass copy : 6492.3 MB/s (0.3%)
C 2-pass copy prefetched (32 bytes step) : 6516.0 MB/s (2.4%)
C 2-pass copy prefetched (64 bytes step) : 6520.5 MB/s (1.2%)
---
standard memcpy : 12169.8 MB/s (3.4%)
standard memset : 23479.9 MB/s (4.2%)
---
MOVSB copy : 10197.7 MB/s (1.6%)
MOVSD copy : 10177.6 MB/s (1.6%)
SSE2 copy : 8973.3 MB/s (2.5%)
SSE2 nontemporal copy : 12924.0 MB/s (1.7%)
SSE2 copy prefetched (32 bytes step) : 9014.2 MB/s (2.7%)
SSE2 copy prefetched (64 bytes step) : 8964.5 MB/s (2.3%)
SSE2 nontemporal copy prefetched (32 bytes step) : 11777.2 MB/s (5.6%)
SSE2 nontemporal copy prefetched (64 bytes step) : 11826.8 MB/s (3.2%)
SSE2 2-pass copy : 7529.5 MB/s (1.8%)
SSE2 2-pass copy prefetched (32 bytes step) : 7122.5 MB/s (1.0%)
SSE2 2-pass copy prefetched (64 bytes step) : 7214.9 MB/s (1.4%)
SSE2 2-pass nontemporal copy : 4987.0 MB/s
Beberapa kunci takeaways:
rep movs
metode ini adalah lebih cepat daripada metode lain yang tidak't "non-temporal"7, dan jauh lebih cepat dari "C" pendekatan yang copy 8 byte pada suatu waktu. rep movs
- namun yang's jauh lebih kecil delta dari salah satu yang dilaporkan (26 GB/s vs 15 GB/s = ~73%). memcpy
) tapi itu tidak mungkin't peduli karena catatan di atas. rep movs
pendekatan yang terletak di tengah. rep movsd
tampaknya untuk menggunakan sihir sama seperti rep movsb
pada chip ini. Yang's menarik karena ERMSB hanya secara eksplisit target movsb
dan sebelumnya tes pada awal gapura dengan ERMSB show movsb
melakukan jauh lebih cepat daripada movsd
. Ini adalah sebagian besar akademik sejak movsb
lebih umum dari movsd
pula. Melihat Haswell hasil baik yang disediakan oleh iwillnotexist di komentar, kita lihat sama tren umum (hasil paling relevan diekstrak):
C copy : 6777.8 MB/s (0.4%)
standard memcpy : 10487.3 MB/s (0.5%)
MOVSB copy : 9393.9 MB/s (0.2%)
MOVSD copy : 9155.0 MB/s (1.6%)
SSE2 copy : 6780.5 MB/s (0.4%)
SSE2 nontemporal copy : 10688.2 MB/s (0.3%)
The rep movsb
pendekatan ini masih lebih lambat dari non-temporal memcpy
, tetapi hanya sekitar 14% di sini (dibandingkan dengan ~26% di Skylake test). Keuntungan dari PB teknik-teknik di atas mereka sementara sepupu sekarang ~57%, bahkan lebih banyak dari yang teoritis manfaat dari pengurangan bandwidth.
rep movs
?Akhirnya menusuk anda sebenarnya pertanyaan: kapan atau mengapa anda harus menggunakan ini? Itu menggambar di atas dan memperkenalkan beberapa ide-ide baru. Sayangnya tidak ada jawaban yang sederhana: anda'll harus trade off berbagai faktor, termasuk beberapa yang anda mungkin dapat't bahkan tahu persis, seperti perkembangan masa depan.
Catatan bahwa alternatif untuk rep movsb
dapat dioptimalkan libc memcpy
(termasuk salinan inline oleh compiler), atau mungkin linting tangan memcpy
versi. Beberapa manfaat di bawah ini berlaku hanya dibandingkan dengan satu atau yang lain dari alternatif-alternatif tersebut (misalnya, "kesederhanaan" membantu melawan hand-rolled versi, tetapi tidak terhadap built-in memcpy
), tetapi beberapa berlaku untuk keduanya.
Dalam beberapa lingkungan ada pembatasan pada instruksi tertentu atau menggunakan register tertentu. Misalnya, dalam kernel Linux, penggunaan SSE/AVX atau FP register umumnya dianulir. Oleh karena itu sebagian besar dioptimalkan memcpy
varian tidak dapat digunakan karena mereka bergantung pada SSE atau AVX register, dan polos 64-bit mov
-berdasarkan copy digunakan pada x86. Untuk platform ini, menggunakan rep movsb
memungkinkan sebagian besar dari kinerja yang dioptimalkan memcpy
tanpa melanggar pembatasan pada SIMD kode.
Yang lebih umum contoh mungkin kode yang telah menargetkan banyak generasi hardware, dan yang doesn't menggunakan hardware khusus pengiriman (misalnya, menggunakan cpuid
). Di sini anda mungkin dipaksa untuk hanya menggunakan set instruksi yang lebih tua, yang aturan apapun AVX, dll. rep movsb
mungkin menjadi pendekatan yang baik di sini, karena itu memungkinkan "tersembunyi" akses yang lebih luas beban dan toko-toko tanpa menggunakan instruksi baru. Jika anda menargetkan pra-ERMSB hardware anda'a harus melihat jika rep movsb
kinerja yang dapat diterima di sana, meskipun...
Baik aspek rep movsb
adalah bahwa hal itu dapat, in theory mengambil keuntungan dari arsitektur perbaikan di masa depan arsitektur, tanpa perubahan kode sumber, yang eksplisit tidak bisa bergerak. Misalnya, ketika 256-bit jalur data diperkenalkan, rep movsb
mampu mengambil keuntungan dari mereka (seperti yang diklaim oleh Intel) tanpa ada perubahan yang diperlukan untuk perangkat lunak. Perangkat lunak menggunakan 128-bit bergerak (yang optimal sebelum Haswell) harus dimodifikasi dan dikompilasi ulang.
Jadi, ini adalah kedua perangkat lunak pemeliharaan menguntungkan (tidak perlu mengubah sumber) dan manfaat bagi yang ada binari (tidak perlu mengerahkan baru binari untuk mengambil keuntungan dari perbaikan).
Betapa pentingnya hal ini tergantung pada model pemeliharaan (misalnya, seberapa sering baru binari yang digunakan dalam praktek) dan sangat sulit untuk membuat penilaian dari seberapa cepat petunjuk ini kemungkinan akan di masa depan. Setidaknya Intel adalah jenis membimbing menggunakan dalam arah ini, meskipun, dengan berkomitmen untuk setidaknya reasonable kinerja di masa depan (15.3.3.6):
REP MOVSB dan REP STOSB akan terus tampil cukup baik pada prosesor masa depan.
Tumpang tindih dengan pekerjaan berikutnya
Manfaat ini tidak't muncul di dataran
memcpy
patokan saja, yang menurut definisi doesn't memiliki pekerjaan berikutnya tumpang tindih, begitu besarnya manfaat yang akan harus hati-hati diukur dalam dunia nyata skenario. Mengambil keuntungan maksimum yang mungkin memerlukan re-organisasi kode yang mengelilingimemcpy
. Manfaat ini ditunjukkan oleh Intel dalam optimasi manual (bagian 11.16.3.4) dan dalam kata-kata mereka: Ketika menghitung diketahui setidaknya seribu byte atau lebih, dengan menggunakan ditingkatkan REP MOVSB/STOSB dapat memberikan keuntungan lain sehingga biaya non-mengkonsumsi kode. Heuristik dapat dipahami menggunakan nilai Cnt = 4096 dan memset() sebagai contoh:• 256-bit SIMD pelaksanaan memset() akan perlu untuk mengeluarkan/menjalankan pensiun 128 contoh 32 - byte toko operasi dengan VMOVDQA, sebelum non-mengkonsumsi urutan instruksi yang dapat membuat jalan mereka ke pensiun.
• contoh ditingkatkan REP STOSB dengan ECX= 4096 diterjemahkan sebagai panjang micro-op aliran yang disediakan oleh perangkat keras, tetapi mengundurkan diri sebagai salah satu instruksi. Ada banyak store_data operasi yang harus menyelesaikan sebelum hasil memset() dapat dikonsumsi. Karena penyelesaian dari menyimpan data-data operasional adalah de-coupled dari program-rangka pensiun, sebagian besar dari non-mengkonsumsi kode stream dapat melalui proses masalah/mengeksekusi dan pensiun, pada dasarnya bebas biaya jika non-mengkonsumsi urutan tidak bersaing untuk menyimpan sumber daya buffer. Jadi Intel mengatakan bahwa setelah beberapa uops kode setelah
rep movsb
telah dikeluarkan, tetapi sementara banyak toko-toko yang masih dalam penerbangan danrep movsb
secara keseluruhan belum't pensiun namun, uops dari petunjuk berikut dapat membuat lebih banyak kemajuan melalui out-of-order mesin dari yang mereka bisa jika kode yang muncul setelah copy lingkaran. Yang uops dari eksplisit memuat dan menyimpan loop semua harus benar-benar pensiun secara terpisah dalam rangka program. Yang harus terjadi untuk membuat ruang dalam MERAMPOK untuk mengikuti uops. Ada doesn't tampaknya akan banyak informasi rinci tentang bagaimana sangat panjang microcoded instruksi sepertirep movsb
bekerja, persis. Kami don't tahu persis bagaimana micro-kode cabang permintaan yang berbeda aliran uops dari microcode sequencer, atau bagaimana uops pensiun. Jika individu uops don't harus pensiun secara terpisah, mungkin seluruh instruksi yang hanya membutuhkan satu slot di ROB? Ketika front-end yang feed OoO mesin melihatrep movsb
instruksi dalam uop cache, mengaktifkan Microcode Sequencer ROM (MS-ROM) untuk mengirim microcode uops ke antrian yang memakan masalah/mengganti nama panggung. It's mungkin tidak mungkin bagi yang lain uops untuk mencampur dengan yang dan masalah/tugas8 sementararep movsb
masih mengeluarkan, tapi petunjuk selanjutnya dapat diambil/decoded dan masalah tepat setelah terakhirrep movsb
uop tidak, sementara beberapa copy belum't dieksekusi belum. Ini hanya berguna jika setidaknya beberapa dari anda selanjutnya kode doesn't tergantung pada hasil darimemcpy
(yang isn't yang tidak biasa). Sekarang, ukuran dari manfaat ini adalah terbatas: paling-paling anda bisa mengeksekusi N instruksi (uops sebenarnya) di luar lambatrep movsb
instruksi, di mana titik anda'll kios, di mana N adalah ROB size. Saat ini ROB ukuran ~200 (192 pada Haswell, 224 di Skylake), yang's manfaat maksimal dari ~200 siklus kerja gratis untuk kode berikutnya dengan IPC dari 1. Dalam 200 siklus anda dapat menyalin suatu tempat sekitar 800 byte pada 10 GB/s, sehingga untuk salinan yang ukuran yang anda mungkin mendapatkan pekerjaan bebas dekat dengan biaya copy (dengan cara membuat copy gratis). Sebagai salin mendapatkan ukuran yang jauh lebih besar, bagaimanapun, kepentingan relatif dari ini berkurang dengan cepat (misalnya, jika anda menyalin 80 KB sebaliknya, pekerjaan bebas adalah hanya 1% dari copy biaya). Namun, hal ini cukup menarik untuk sederhana berukuran eksemplar. Copy loop don't benar-benar blok berikutnya instruksi dari pelaksana, baik. Intel tidak pergi ke detail pada ukuran dari manfaat, atau pada jenis salinan atau sekitarnya kode yang ada adalah yang paling menguntungkan. (Panas atau dingin tujuan atau sumber, tinggi ILP atau rendah ILP latensi tinggi kode setelah).Kode Ukuran
Dieksekusi kode ukuran (dalam beberapa byte) adalah mikroskopis dibandingkan dengan khas dioptimalkan
memcpy
rutin. Jika kinerja adalah semua dibatasi oleh i-cache (termasuk uop cache) meleset, mengurangi ukuran kode mungkin bermanfaat. Sekali lagi, kita dapat terikat besarnya manfaat ini didasarkan pada ukuran salin. Saya tidak't benar-benar bekerja secara numerik, tetapi intuisi adalah bahwa mengurangi dinamis kode ukuran B byte dapat menyimpan paling banyakC * B
cache-merindukan, untuk beberapa konstan C. Setiap call untukmemcpy
menimbulkan cache miss biaya (atau manfaat) sekali, tetapi keuntungan dari throughput yang lebih tinggi timbangan dengan jumlah byte yang disalin. Jadi untuk transfer, throughput yang lebih tinggi akan mendominasi cache efek. Sekali lagi, ini bukan sesuatu yang akan muncul di dataran acuan, di mana seluruh loop tidak diragukan lagi cocok di uop cache. Anda'll kebutuhan dunia nyata, di tempat tes untuk mengevaluasi efek ini.Arsitektur Tertentu Optimasi
Anda melaporkan bahwa pada hardware anda,
rep movsb
itu jauh lebih lambat dari platformmemcpy
. Namun, bahkan di sini ada laporan dari hasil yang berlawanan pada awal hardware (seperti Ivy Bridge). Yang's sepenuhnya masuk akal, karena tampaknya bahwa string memindahkan operasi mendapatkan cinta secara berkala - tapi tidak setiap generasi, sehingga mungkin akan lebih cepat atau setidaknya diikat (di mana titik itu mungkin menang berdasarkan keuntungan lainnya) pada arsitektur di mana itu telah dibawa up to date, hanya untuk jatuh di belakang dalam berikutnya hardware. Quoting Andy Glew, yang harus tahu satu atau dua hal tentang ini setelah menerapkan ini pada P6: big kelemahan lakukan cepat string di microcode adalah [...] yang microcode turun selaras dengan setiap generasi, semakin lambat dan lebih lambat sampai seseorang mendapat sekitar untuk memperbaiki itu. Sama seperti perpustakaan laki-laki copy falls out of tune. Saya kira bahwa itu adalah mungkin bahwa salah satu dari kehilangan kesempatan untuk menggunakan 128-bit beban dan toko-toko ketika mereka menjadi tersedia, dan sebagainya. Dalam hal ini, dapat dilihat hanya sebagai salah satu "platform tertentu" optimasi untuk menerapkan khas tiap-trik-in-the-bookmemcpy
rutinitas anda temukan di perpustakaan standar dan JIT compiler: tapi hanya untuk digunakan pada arsitektur di mana itu lebih baik. Untuk JIT atau AOT-disusun hal-hal ini adalah mudah, tapi bagi kita yang dihimpun binari ini tidak memerlukan spesifik platform pengiriman, tapi yang sering sudah ada (kadang-kadang dilaksanakan pada saat link), ataumtune
argumen yang dapat digunakan untuk membuat statis mengambil keputusan.Kesederhanaan
Bahkan pada Skylake, di mana tampaknya seperti itu telah jatuh di belakang mutlak tercepat non-temporal teknik, hal ini masih lebih cepat dari yang paling mendekati dan dengan sangat hati simple. Ini berarti lebih sedikit waktu di validasi, lebih sedikit misteri bug, kurang waktu tuning dan memperbarui monster
memcpy
pelaksanaan (atau, sebaliknya, kurang ketergantungan pada keinginan dari perpustakaan standar pelaksana jika anda bergantung pada itu).Latency Terikat Platform
Memori throughput bound algoritma9 sebenarnya dapat beroperasi di dua utama secara keseluruhan rezim: DRAM bandwidth terikat atau concurrency/latency terikat. Modus pertama adalah salah satu yang anda mungkin akrab dengan: DRAM subsistem tertentu memiliki teori bandwidth yang anda dapat menghitung dengan cukup mudah didasarkan pada jumlah saluran, data rate/lebar dan frekuensi. Misalnya, saya DDR4-2133 sistem dengan 2 saluran yang memiliki bandwidth max dari 2.133 8 2 = 34.1 GB/s, sama seperti dilaporkan pada ARK. Anda memenangkan't mempertahankan lebih dari itu menilai dari DRAM (dan biasanya agak kurang karena berbagai inefisiensi) ditambahkan di semua core pada soket (yaitu, itu adalah batas global untuk single-socket sistem). Lainnya batas yang dikenakan oleh berapa banyak permintaan bersamaan inti dapat benar-benar masalah untuk subsistem memori. Bayangkan jika sebuah inti hanya bisa memiliki 1 permintaan dalam kemajuan sekaligus, untuk 64-byte cache line - ketika permintaan selesai, anda bisa masalah lain. Asumsikan juga sangat cepat 50ns latency memori. Maka meskipun besar 34.1 GB/s DRAM bandwidth, anda'd benar-benar hanya mendapatkan 64 byte / 50 ns = 1.28 GB/s, atau kurang dari 4% dari max bandwidth. Dalam prakteknya, core bisa mengeluarkan lebih dari satu permintaan pada suatu waktu, tetapi tidak terbatas jumlah. Hal ini biasanya dipahami bahwa hanya ada 10 line mengisi buffers per inti antara L1 dan sisa memori hirarki, dan mungkin 16 atau jadi mengisi buffer antara L2 dan DRAM. Prefetching bersaing untuk sumber daya yang sama, tapi setidaknya membantu mengurangi efektif latency. Untuk lebih jelasnya lihat di salah satu posting yang besar Dr. Bandwidth yang telah tertulis pada topic, sebagian besar pada Intel forum. Namun, most terbaru Cpu dibatasi oleh this faktor, bukan RAM bandwidth. Biasanya mereka mencapai 12 - 20 GB/s per core, sedangkan RAM bandwidth dapat 50+ GB/s (pada 4 channel system). Hanya beberapa gen 2-channel "klien" core, yang tampaknya memiliki baik uncore, mungkin lebih line buffer dapat memukul DRAM batas pada single core, dan kami Skylake chip tampaknya menjadi salah satu dari mereka. Sekarang tentu saja, ada alasan Intel desain sistem dengan 50 GB/s DRAM bandwidth, sementara hanya untuk mempertahankan < 20 GB/s per core karena concurrency batas: batas mantan adalah socket-lebar dan yang terakhir adalah per core. Jadi setiap core pada 8 core sistem dapat mendorong 20 GB/s senilai permintaan, pada saat mana mereka akan DRAM terbatas lagi. Mengapa saya akan terus dan terus tentang ini? Karena yang terbaik
memcpy
implementasi sering tergantung pada rezim yang anda beroperasi di. Setelah anda DRAM BW limited (sebagai chip rupanya, tapi paling tidak't pada single core), menggunakan non-temporal menulis menjadi sangat penting karena menyimpan baca-untuk-kepemilikan yang biasanya limbah 1/3 dari bandwidth anda. Anda melihat bahwa sebenarnya dalam hasil tes di atas: memcpy implementasi yang don't menggunakan NT toko kehilangan 1/3 dari bandwidth mereka. Jika anda concurrency terbatas, namun, situasi menyetarakan dan kadang-kadang berbalik, namun. Anda memiliki DRAM bandwidth untuk cadangan, jadi NT toko don't membantu dan mereka bahkan dapat merugikan karena mereka dapat meningkatkan latency sejak handoff waktu untuk line buffer mungkin lebih dari sebuah skenario di mana prefetch membawa RFO baris ke LLC (atau bahkan L2) dan kemudian simpan selesai di LLC yang efektif latency yang lebih rendah. Akhirnya, server uncores cenderung memiliki jauh lebih lambat NT toko dari klien (dan bandwidth yang tinggi), yang menonjolkan efek ini. Jadi pada platform lain anda mungkin menemukan bahwa NT toko-toko yang kurang berguna (setidaknya ketika anda peduli tentang single-threaded kinerja) dan mungkinrep movsb
menang mana (jika hal itu mendapatkan yang terbaik dari kedua dunia). Benar-benar, ini item terakhir adalah panggilan untuk sebagian besar pengujian. Saya tahu bahwa NT toko kehilangan keuntungan nyata untuk single-threaded tes pada kebanyakan gapura (termasuk server saat ini gapura), tapi aku don't tahu bagaimanarep movsb
akan tampil relatif...Referensi
Sumber yang baik lainnya dari info yang tidak terintegrasi di atas. comp.arch investigasi dari
rep movsb
dibandingkan dengan alternatif lain. Banyak catatan yang bagus tentang prediksi cabang, dan merupakan implementasi dari pendekatan I've sering disarankan untuk blok kecil: menggunakan tumpang tindih pertama dan/atau terakhir membaca/menulis daripada mencoba untuk menulis hanya apa yang diperlukan jumlah byte (misalnya, menerapkan semua salinan dari 9 sampai 16 byte seperti dua 8-byte salinan yang mungkin tumpang tindih dalam hingga 7 byte).1 Mungkin maksudnya adalah untuk membatasi kasus-kasus di mana, misalnya, kode-ukuran ini sangat penting. 2 Lihat Bagian 3.7.5: REP Awalan dan Data Gerakan. 3 Ini adalah kunci untuk catatan ini hanya berlaku untuk berbagai macam toko dalam satu instruksi itu sendiri: setelah selesai, blok dari toko-toko masih muncul memerintahkan sehubungan dengan sebelum dan setelah toko-toko. Sehingga kode dapat melihat toko-toko dari
rep movs
out of order with menghormati satu sama other tapi tidak dengan hormat sebelumnya atau berikutnya toko (dan's yang terakhir jaminan anda biasanya perlu). Itu hanya akan menjadi masalah jika anda menggunakan ujung salin tujuan sebagai sinkronisasi bendera, bukan toko yang terpisah. 4 Perhatikan bahwa non-temporal diskrit toko juga menghindari sebagian besar memesan persyaratan, meskipun dalam praktekrep movs
memiliki lebih banyak kebebasan karena masih ada beberapa pemesanan kendala di WC/NT toko-toko. 5 Ini adalah umum di bagian akhir dari 32-bit era, di mana banyak chip 64-bit jalur data (e.g, untuk mendukung FPUs yang memiliki dukungan untuk 64-bitdouble
jenis). Hari ini, "baik" chip seperti Pentium atau Celeron merek memiliki AVX tamu dengan kebutuhan khusus, tapi mungkinrep movs
microcode masih dapat menggunakan 256b beban/toko. 6 E. g., karena bahasa keselarasan aturan, keselarasan atribut atau operator, aliasing aturan atau informasi lain yang ditentukan pada waktu kompilasi. Dalam kasus keselarasan, bahkan jika keselarasan yang tepat dapat't dapat ditentukan, mereka mungkin setidaknya akan mampu mengerek keselarasan cek keluar dari loop atau menghilangkan berlebihan pemeriksaan. 7 I'm membuat asumsi bahwa "s"memcpy
lebih memilih non-temporal pendekatan, yang sangat mungkin untuk ini ukuran dari buffer. 8 Yang isn't harus jelas, karena itu bisa menjadi kasus bahwa uop aliran yang dihasilkan olehrep movsb
hanya memonopoli pengiriman dan kemudian itu akan terlihat sangat banyak seperti eksplisitmov
kasus. Tampaknya itu doesn't bekerja seperti itu namun - uops dari instruksi berikutnya dapat berbaur dengan uops dari microcodedrep movsb
. 9 I. e., orang-orang yang dapat mengeluarkan sejumlah besar memori independen permintaan dan karenanya jenuh tersedia DRAM-untuk-core bandwidth, yangmemcpy
akan menjadi anak poster (dan sebagai apposed untuk murni latency terikat beban seperti pointer mengejar).
Mikroarsitektur Ivy Bridge (prosesor yang dirilis pada tahun 2012 dan 2013) diperkenalkan Enhanced REP MOVSB (kita masih perlu untuk memeriksa bit yang sesuai) dan memungkinkan kita untuk menyalin memori cepat. Termurah versi kemudian prosesor - Kaby Danau Celeron dan Pentium, dirilis pada 2017, don't memiliki AVX yang bisa digunakan untuk memori cepat copy, tetapi masih harus Ditingkatkan REP MOVSB. REP MOVSB (ERMSB) hanya lebih cepat dari AVX copy atau umum digunakan daftarkan copy jika ukuran blok adalah setidaknya 256 byte. Untuk blok di bawah 64 bytes, hal ini JAUH lebih lambat, karena ada internal yang tinggi startup di ERMSB - sekitar 35 siklus. Melihat Intel Manual di Optimasi, bagian 3.7.6 Ditingkatkan REP MOVSB dan STOSB operasi (ERMSB) http://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-optimization-manual.pdf
- biaya startup 35 siklus;
- kedua alamat sumber dan tujuan harus selaras dengan 16-Byte batas;
- sumber daerah tidak boleh tumpang tindih dengan wilayah tujuan;
- panjang harus kelipatan 64 untuk menghasilkan kinerja yang lebih tinggi;
- arah harus maju (CLD). Seperti yang saya katakan sebelumnya, REP MOVSB mulai mengungguli metode lain ketika suhu udara setidaknya 256 byte, tetapi untuk melihat jelas manfaat lebih dari AVX copy, panjangnya harus lebih dari 2048 byte. Pada efek dari keselarasan jika REP MOVSB vs AVX copy, Intel Manual memberikan informasi berikut:
- jika sumber buffer adalah tidak selaras, dampak pada ERMSB pelaksanaan versus 128-bit AVX adalah serupa;
- jika buffer tujuan tidak selaras, dampak pada ERMSB pelaksanaan dapat 25% degradasi, sementara 128-bit AVX pelaksanaan memcpy dapat menurunkan hanya 5%, relatif terhadap 16-byte blok skenario. Saya telah membuat tes pada Intel Core i5-6600, di bawah 64-bit, dan saya telah dibandingkan REP MOVSB memcpy() dengan sederhana MOV RAX, [SRC]; MOV [DST], RAX implementasi ketika data sesuai dengan L1 cache: REP MOVSB memcpy():
- 1622400000 data blocks of 32 bytes took 17.9337 seconds to copy; 2760.8205 MB/s
- 1622400000 data blocks of 64 bytes took 17.8364 seconds to copy; 5551.7463 MB/s
- 811200000 data blocks of 128 bytes took 10.8098 seconds to copy; 9160.5659 MB/s
- 405600000 data blocks of 256 bytes took 5.8616 seconds to copy; 16893.5527 MB/s
- 202800000 data blocks of 512 bytes took 3.9315 seconds to copy; 25187.2976 MB/s
- 101400000 data blocks of 1024 bytes took 2.1648 seconds to copy; 45743.4214 MB/s
- 50700000 data blocks of 2048 bytes took 1.5301 seconds to copy; 64717.0642 MB/s
- 25350000 data blocks of 4096 bytes took 1.3346 seconds to copy; 74198.4030 MB/s
- 12675000 data blocks of 8192 bytes took 1.1069 seconds to copy; 89456.2119 MB/s
- 6337500 data blocks of 16384 bytes took 1.1120 seconds to copy; 89053.2094 MB/s
MOV RAX... memcpy():
- 1622400000 data blocks of 32 bytes took 7.3536 seconds to copy; 6733.0256 MB/s
- 1622400000 data blocks of 64 bytes took 10.7727 seconds to copy; 9192.1090 MB/s
- 811200000 data blocks of 128 bytes took 8.9408 seconds to copy; 11075.4480 MB/s
- 405600000 data blocks of 256 bytes took 8.4956 seconds to copy; 11655.8805 MB/s
- 202800000 data blocks of 512 bytes took 9.1032 seconds to copy; 10877.8248 MB/s
- 101400000 data blocks of 1024 bytes took 8.2539 seconds to copy; 11997.1185 MB/s
- 50700000 data blocks of 2048 bytes took 7.7909 seconds to copy; 12710.1252 MB/s
- 25350000 data blocks of 4096 bytes took 7.5992 seconds to copy; 13030.7062 MB/s
- 12675000 data blocks of 8192 bytes took 7.4679 seconds to copy; 13259.9384 MB/s
Jadi, bahkan pada 128-bit blok, REP MOVSB lebih lambat dari yang sederhana MOV RAX copy dalam satu lingkaran (tidak berdinding). Yang ERMSB pelaksanaan mulai mengungguli MOV RAX loop hanya mulai bentuk 256-byte blok.
Anehnya, sebelumnya arsitektur (Nehalem dan kemudian), yang tidak't namun telah Ditingkatkan REP MOVB, sudah cukup cepat REP MOVSD/MOVSQ (tapi tidak REP MOVSB/MOVSW) implementasi untuk blok besar, tetapi tidak cukup besar untuk outsize L1 cache. Intel Optimasi Manual (2.5.6 REP String Peningkatan) memberikan informasi berikut ini terkait dengan mikroarsitektur Nehalem - Intel Core i5, i7 dan prosesor Xeon yang dirilis pada tahun 2009 dan 2010.
Latency untuk MOVSB, 9 siklus jika ECX < 4; jika tidak, REP MOVSB dengan ECX > 9 memiliki 50-siklus biaya startup.
Kutipan dari Intel Optimasi Manual (2.5.6 REP String Peningkatan):
- string Pendek (ECX <= 12): latency dari REP MOVSW/MOVSD/MOVSQ adalah sekitar 20 siklus.
- Cepat string (ECX >= 76: tidak termasuk REP MOVSB): prosesor implementasi menyediakan optimasi hardware dengan bergerak sebagai banyak potongan-potongan data di 16 byte sebanyak mungkin. Latency dari REP string latency akan berbeda jika salah satu dari 16-byte data transfer membentang di cache garis batas: = Split-gratis: latency terdiri dari biaya startup sekitar 40 siklus dan masing-masing 64 byte data menambahkan 4 siklus. = Cache split: latency terdiri dari biaya startup sekitar 35 siklus dan masing-masing 64 byte data menambahkan 6 siklus.
- panjang string Menengah: latency dari REP MOVSW/MOVSD/MOVSQ memiliki biaya startup sekitar 15 siklus plus satu siklus untuk setiap iterasi dari pergerakan data di word/dword/qword. Intel tampaknya tidak benar di sini. Dari kutipan di atas kita memahami bahwa untuk sangat besar blok memori, REP MOVSW secepat REP MOVSD/MOVSQ, tapi tes telah menunjukkan bahwa hanya REP MOVSD/MOVSQ cepat, sementara REP MOVSW bahkan lebih lambat dari REP MOVSB pada Nehalem dan Westmere. Menurut informasi yang diberikan oleh Intel di manual, pada mikroarsitektur Intel sebelumnya (sebelum tahun 2008) biaya startup yang lebih tinggi. Kesimpulan: jika anda hanya perlu menyalin data yang sesuai dengan L1 cache, hanya 4 siklus untuk menyalin 64 byte data yang sangat baik, dan anda don't perlu menggunakan XMM register!
REP MOVSD/MOVSQ adalah solusi universal yang bekerja baik pada semua prosesor Intel (tidak ERMSB diperlukan) jika data cocok L1 cache
Berikut ini adalah tes REP MOVS* bila sumber dan tujuan berada di L1 cache, blok yang cukup besar untuk tidak menjadi serius dipengaruhi oleh biaya startup, tetapi tidak yang besar untuk melebihi L1 cache size. Sumber: http://users.atw.hu/instlatx64/ Yonah (2006-2008)
REP MOVSB 10.91 B/c
REP MOVSW 10.85 B/c
REP MOVSD 11.05 B/c
Nehalem (2009-2010)
REP MOVSB 25.32 B/c
REP MOVSW 19.72 B/c
REP MOVSD 27.56 B/c
REP MOVSQ 27.54 B/c
Westmere (2010-2011)
REP MOVSB 21.14 B/c
REP MOVSW 19.11 B/c
REP MOVSD 24.27 B/c
Ivy Bridge (2012-2013) - dengan Peningkatan REP MOVSB
REP MOVSB 28.72 B/c
REP MOVSW 19.40 B/c
REP MOVSD 27.96 B/c
REP MOVSQ 27.89 B/c
SkyLake (2015-2016) - dengan Peningkatan REP MOVSB
REP MOVSB 57.59 B/c
REP MOVSW 58.20 B/c
REP MOVSD 58.10 B/c
REP MOVSQ 57.59 B/c
Kaby Lake (2016-2017) - dengan Peningkatan REP MOVSB
REP MOVSB 58.00 B/c
REP MOVSW 57.69 B/c
REP MOVSD 58.00 B/c
REP MOVSQ 57.89 B/c
Seperti yang anda lihat, pelaksanaan REP MOVS berbeda secara signifikan dari satu mikroarsitektur lain. Pada beberapa prosesor, seperti Ivy Bridge - REP MOVSB adalah tercepat, meskipun hanya sedikit lebih cepat daripada REP MOVSD/MOVSQ, tetapi tidak ada keraguan bahwa pada semua prosesor sejak Nehalem, REP MOVSD/MOVSQ bekerja sangat baik - anda bahkan don't perlu "Ditingkatkan REP MOVSB", karena, pada Ivy Bridge (2013) dengan Enhacnced REP MOVSB, REP MOVSD menunjukkan hal yang sama byte per jam data seperti pada Nehalem (2010) tanpa Enhacnced REP MOVSB, padahal REP MOVSB menjadi sangat cepat hanya karena SkyLake (2015) - dua kali lebih cepat dari Ivy Bridge. Jadi, ini Enhacnced REP MOVSB sedikit di CPUID mungkin membingungkan - itu hanya menunjukkan bahwa REP MOVSB
per se adalah OK, tetapi tidak bahwa setiap REP MOVS*
lebih cepat.
Yang paling membingungkan ERMBSB implementasi pada mikroarsitektur Ivy Bridge. Ya, sangat tua prosesor, sebelum ERMSB, REP MOVS untuk blok besar yang tidak menggunakan cache protokol fitur yang tidak tersedia untuk reguler kode (no-RFO). Tetapi protokol ini tidak lagi digunakan pada Ivy Bridge yang memiliki ERMSB. Menurut Andy Glew's komentar pada jawaban "kenapa rumit memcpy/memset unggul?" dari Peter Cordes jawaban, cache protokol fitur yang tidak tersedia untuk kode biasa sekali digunakan pada prosesor yang lebih tua, tetapi tidak lagi pada Ivy Bridge. Dan ada penjelasan mengapa biaya startup yang begitu tinggi untuk REP MOVS: "besar overhead untuk memilih dan menetapkan metode yang tepat terutama karena kurangnya microcode cabang prediksi". Ada juga sebuah catatan menarik yang Pentium Pro (P6) pada tahun 1996 dilaksanakan REP MOVS* dengan 64 bit pengendali beban dan toko-toko dan no-RFO cache protokol - mereka tidak melanggar memori memesan, tidak seperti ERMSB di Ivy Bridge.
Anda mengatakan bahwa anda ingin:
jawaban yang menunjukkan kapan ERMSB berguna
Tapi aku'm tidak yakin itu berarti apa yang anda pikirkan itu berarti. Melihat 3.7.6.1 docs anda link ke, ia secara eksplisit mengatakan:
melaksanakan memcpy menggunakan ERMSB mungkin tidak mencapai tingkat yang sama dari throughput menggunakan 256-bit atau 128-bit AVX alternatif, tergantung pada suhu udara dan keselarasan faktor.
Jadi hanya karena CPUID
menunjukkan dukungan untuk ERMSB, yang isn't jaminan bahwa REP MOVSB akan menjadi cara tercepat untuk menyalin memori. Itu hanya berarti itu tidak't mengisap seburuk itu memiliki beberapa sebelumnya Cpu.
Namun hanya karena mungkin ada alternatif yang dapat, dalam kondisi tertentu, berjalan lebih cepat doesn't berarti bahwa REP MOVSB adalah sia-sia. Sekarang bahwa kinerja hukuman bahwa instruksi ini digunakan untuk dikenakan pergi, itu berpotensi berguna instruksi lagi.
Ingat, itu adalah sedikit kode (2 byte!) dibandingkan dengan beberapa lebih terlibat memcpy rutinitas saya telah melihat. Sejak loading dan menjalankan potongan besar dari kode juga memiliki hukuman (melempar beberapa kode lainnya keluar dari cpu's cache), kadang-kadang 'manfaat' dari AVX et al akan diimbangi oleh dampaknya pada sisa dari kode anda. Tergantung pada apa yang anda lakukan.
Anda juga bertanya:
Mengapa bandwidth yang jauh lebih rendah dengan REP MOVSB? Apa yang bisa saya lakukan untuk memperbaikinya?
Ini isn't akan menjadi mungkin untuk "melakukan sesuatu" untuk membuat REP MOVSB menjalankan lebih cepat. Itu tidak apa yang dilakukannya.
Jika anda ingin kecepatan yang lebih tinggi anda melihat dari dari memcpy, anda dapat menggali sumber itu. It's di luar sana di suatu tempat. Atau anda bisa melacak ke dalamnya dari debugger dan melihat kode yang sebenarnya jalur yang diambil. Harapan saya adalah bahwa hal itu's menggunakan beberapa instruksi AVX untuk bekerja dengan 128 atau 256bits pada suatu waktu.
Atau anda hanya dapat... Baik, anda meminta kami untuk tidak mengatakan hal itu.
memset()
/memmove()
/memcpy()
(lihat mis.gcc/config/i386/i386.c:expand_set_or_movmem_via_rep() di GCC sumber; juga mencari stringop_algs
di file yang sama untuk melihat arsitektur tergantung varian). Jadi, tidak ada alasan untuk mengharapkan keuntungan besar dengan menggunakan anda sendiri varian dengan GCC (kecuali anda've lupa hal-hal penting seperti keselarasan atribut untuk anda selaras data, atau tidak memungkinkan cukup optimasi spesifik seperti -O2 -maret= -mtune=
). Jika anda setuju, maka jawaban untuk pertanyaan dinyatakan lebih atau kurang relevan dalam praktek.
(Aku hanya berharap ada memrepeat()
, kebalikan dari memcpy()
dibandingkan dengan memmove()
, yang akan ulangi bagian awal dari sebuah buffer untuk mengisi seluruh buffer.) Saat ini saya memiliki Ivy Bridge mesin yang di gunakan (Core i5-6200U laptop, Linux 4.4.0 x86-64 kernel, dengan erms
di /proc/cpuinfo
bendera). Karena saya ingin mengetahui apakah saya dapat menemukan kasus di mana seorang kustom memcpy() varian berdasarkan rep movsb
akan mengungguli langsung memcpy()
, saya menulis terlalu rumit acuan.
Inti idenya adalah bahwa program utama mengalokasikan tiga besar daerah memori: asli
, saat ini
, dan benar
, masing-masing ukuran yang sama, dan setidaknya halaman-blok. Salinan operasi dikelompokkan ke dalam set, dengan masing-masing set memiliki sifat-sifat yang berbeda, seperti semua sumber-sumber dan target yang sejajar (untuk beberapa jumlah byte), atau semua panjang berada dalam kisaran yang sama. Setiap set dijelaskan dengan menggunakan sebuah array dari src
, dst
, n
kembar tiga, di mana semua src
untuk src+n-1
dan dst
untuk dst+n-1
benar-benar dalam saat ini
daerah.
A Xorshift* PRNG ini digunakan untuk menginisialisasi asli
untuk data acak. (Seperti saya memperingatkan di atas, ini terlalu rumit, tapi saya ingin memastikan saya'm tidak meninggalkan apapun mudah cara pintas untuk compiler.) Benar
area diperoleh dengan memulai dengan asli
data saat ini
, menerapkan semua kembar tiga di set saat ini, menggunakan memcpy()
yang disediakan oleh C perpustakaan, dan menyalin saat ini
area benar
. Hal ini memungkinkan masing-masing mengacu kepada fungsi yang akan diverifikasi untuk berperilaku dengan benar.
Masing-masing set copy operasi adalah waktunya sejumlah besar kali menggunakan fungsi yang sama, dan rata-rata ini digunakan untuk perbandingan. (Menurut saya, rata-rata yang paling masuk akal dalam benchmarking, dan memberikan masuk akal semantik -- fungsi ini setidaknya yang cepat setidaknya separuh waktu.)
Untuk menghindari optimasi compiler, saya memiliki program yang memuat fungsi-fungsi dan tolok ukur yang dinamis, pada waktu berjalan. Fungsi semua memiliki bentuk yang sama, fungsi void(void *, const void *, size_t)
-- perhatikan bahwa tidak seperti memcpy()
dan memmove()
, mereka tidak kembali. Tolok ukur (bernama set copy operasi) yang dihasilkan secara dinamis dengan fungsi call (yang membawa pointer ke saat ini
area dan ukuran sebagai parameter, antara lain).
Sayangnya, saya belum menemukan di mana setiap set
static void rep_movsb(void *dst, const void *src, size_t n)
{
__asm__ __volatile__ ( "rep movsb\n\t"
: "+D" (dst), "+S" (src), "+c" (n)
:
: "memory" );
}
akan mengalahkan
static void normal_memcpy(void *dst, const void *src, size_t n)
{
memcpy(dst, src, n);
}
gcc -Dinding -O2 -maret=ivybridge -mtune=ivybridge
menggunakan GCC 5.4.0 tersebut pada Core i5-6200U laptop yang menjalankan linux-4.4.0 kernel 64-bit. Menyalin 4096 byte selaras dan potongan seukuran datang dekat, namun.
Ini berarti bahwa setidaknya sejauh ini, saya belum menemukan kasus di mana menggunakan rep movsb
memcpy varian akan masuk akal. Itu tidak berarti tidak ada kasus seperti itu; saya hanya ingin't menemukan satu.
(Pada titik ini kode adalah spaghetti mess I'm lebih malu daripada bangga, jadi saya akan menghilangkan penerbitan sumber-sumber kecuali jika seseorang meminta. Uraian di atas harus cukup untuk menulis yang lebih baik, meskipun.) Ini tidak mengejutkan saya banyak, meskipun. C compiler dapat menyimpulkan banyak informasi tentang keselarasan dari operan pointer, dan apakah jumlah byte untuk copy adalah compile-time konstan, beberapa yang cocok daya dari dua. Informasi ini dapat, dan akan/harus, digunakan oleh compiler untuk menggantikan C library memcpy()
/memmove()
fungsi dengan sendiri.
GCC tidak tepat ini (lihat misalnya gcc/config/i386/i386.c:expand_set_or_movmem_via_rep() di GCC sumber; juga mencari stringop_algs
di file yang sama untuk melihat arsitektur tergantung varian). Memang, memcpy()
/memset()
/memmove()
telah dioptimalkan secara terpisah untuk beberapa prosesor x86 varian, itu akan cukup mengejutkan saya jika GCC pengembang belum termasuk erms dukungan.
GCC menyediakan beberapa fungsi attributes yang dapat digunakan oleh pengembang untuk memastikan kode yang dihasilkan. Misalnya, alloc_align (n)
GCC mengatakan bahwa fungsi mengembalikan memori selaras untuk setidaknya n
byte. Aplikasi atau perpustakaan dapat memilih implementasi fungsi untuk penggunaan pada waktu berjalan, dengan menciptakan "penyelesai fungsi" (yang mengembalikan fungsi pointer), dan mendefinisikan fungsi dengan menggunakan ifunc (resolver)
atribut.
Salah satu pola yang paling umum yang saya gunakan dalam kode saya untuk ini adalah
some_type *pointer = __builtin_assume_aligned(ptr, alignment);
di mana ptr
adalah beberapa pointer, keselarasan
adalah jumlah byte hal ini selaras dengan; GCC kemudian tahu/mengasumsikan bahwa pointer
sejajar keselarasan
byte.
Lain yang berguna built-in, meskipun jauh lebih sulit untuk digunakan benar, adalah __builtin_prefetch()
. Untuk memaksimalkan bandwidth keseluruhan/efisiensi, saya telah menemukan bahwa meminimalkan latency di masing-masing sub-operasi, hasil yang terbaik. (Untuk menyalin elemen tersebar berturut-turut untuk penyimpanan sementara, ini lebih sulit, karena prefetching biasanya melibatkan penuh cache line; jika terlalu banyak elemen yang prefetched, sebagian besar cache yang terbuang dengan menyimpan barang yang tidak terpakai.)
Ada jauh lebih efisien cara untuk memindahkan data. Hari ini, pelaksanaan memcpy
akan menghasilkan arsitektur yang spesifik kode dari compiler yang dioptimalkan berdasarkan memori penyelarasan data dan faktor-faktor lain. Hal ini memungkinkan lebih baik menggunakan non-temporal cache instruksi dan XMM dan register lainnya di x86 dunia.
Ketika anda keras-kode rep movsb
mencegah penggunaan intrinsik.
Oleh karena itu, untuk sesuatu seperti memcpy
, kecuali jika anda menulis sesuatu yang diikat akan menjadi sangat spesifik perangkat keras dan kecuali jika anda akan meluangkan waktu untuk menulis sangat dioptimalkan memcpy
fungsi dalam majelis (atau menggunakan C tingkat intrinsik), anda berada jauh lebih baik memungkinkan compiler untuk mencari jalan keluar untuk anda.
Sebagai seorang jenderal memcpy()
panduan:
a) Jika data yang disalin adalah kecil (kurang dari sekitar 20 byte) dan memiliki ukuran yang tetap, biarkan compiler melakukannya. Alasan: Compiler dapat menggunakan normal mov
petunjuk dan menghindari startup overhead.
b) Jika data yang disalin adalah kecil (kurang dari sekitar 4 KiB) dan dijamin akan selaras, gunakan rep movsb
(jika ERMSB didukung) atau rep movsd
(jika ERMSB tidak didukung). Alasan: Menggunakan SSE atau AVX alternatif memiliki sejumlah besar "startup overhead" sebelum itu salinan apa-apa.
c) Jika data yang disalin adalah kecil (kurang dari sekitar 4 KiB) dan tidak dijamin untuk menjadi sejajar, menggunakan rep movsb
. Alasan: Menggunakan SSE atau AVX, atau menggunakan rep movsd
untuk sebagian besar itu ditambah beberapa rep movsb
di awal atau di akhir, memiliki terlalu banyak overhead.
d) Untuk semua kasus lain menggunakan sesuatu seperti ini:
mov edx,0
.again:
pushad
.nextByte:
pushad
popad
mov al,[esi]
pushad
popad
mov [edi],al
pushad
popad
inc esi
pushad
popad
inc edi
pushad
popad
loop .nextByte
popad
inc edx
cmp edx,1000
jb .again
Alasan: hal Ini akan menjadi sangat lambat sehingga hal ini akan memaksa programmer untuk menemukan alternatif yang doesn't melibatkan menyalin besar gumpalan data, dan perangkat lunak yang dihasilkan akan secara signifikan lebih cepat karena menyalin besar gumpalan data dapat dihindari.