Saya berada di Whiskey Lake i7-8565U dan menganalisis penghitung perf dan waktu untuk menyalin data 512 KiB (dua kali lebih banyak dari ukuran cache L2) dan menghadapi beberapa kesalahpahaman mengenai pekerjaan prefetcher L2 HW.
Dalam Intel Manual Vol.4 MSR terdapat MSR 0x1A4
bit 0 of adalah untuk controlloing L2 HW prefetcher (1 untuk menonaktifkan).
Pertimbangkan tolok ukur berikut:
memcopy.h
:
void *avx_memcpy_forward_lsls(void *restrict, const void *restrict, size_t);
memcopy.S
:
avx_memcpy_forward_lsls:
shr rdx, 0x3
xor rcx, rcx
avx_memcpy_forward_loop_lsls:
vmovdqa ymm0, [rsi + 8*rcx]
vmovdqa [rdi + rcx*8], ymm0
vmovdqa ymm1, [rsi + 8*rcx + 0x20]
vmovdqa [rdi + rcx*8 + 0x20], ymm1
add rcx, 0x08
cmp rdx, rcx
ja avx_memcpy_forward_loop_lsls
ret
main.c
:
#include <string.h>
#include <stdlib.h>
#include <inttypes.h>
#include <x86intrin.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include "memcopy.h"
#define ITERATIONS 1000
#define BUF_SIZE 512 * 1024
_Alignas(64) char src[BUF_SIZE];
_Alignas(64) char dest[BUF_SIZE];
static void __run_benchmark(unsigned runs, unsigned run_iterations,
void *(*fn)(void *, const void*, size_t), void *dest, const void* src, size_t sz);
#define run_benchmark(runs, run_iterations, fn, dest, src, sz) \
do{\
printf("Benchmarking " #fn "\n");\
__run_benchmark(runs, run_iterations, fn, dest, src, sz);\
}while(0)
int main(void){
int fd = open("/dev/urandom", O_RDONLY);
read(fd, src, sizeof src);
run_benchmark(20, ITERATIONS, avx_memcpy_forward_lsls, dest, src, BUF_SIZE);
}
static inline void benchmark_copy_function(unsigned iterations, void *(*fn)(void *, const void *, size_t),
void *restrict dest, const void *restrict src, size_t sz){
while(iterations --> 0){
fn(dest, src, sz);
fn(dest, src, sz);
fn(dest, src, sz);
fn(dest, src, sz);
fn(dest, src, sz);
fn(dest, src, sz);
fn(dest, src, sz);
fn(dest, src, sz);
fn(dest, src, sz);
fn(dest, src, sz);
fn(dest, src, sz);
fn(dest, src, sz);
fn(dest, src, sz);
fn(dest, src, sz);
fn(dest, src, sz);
fn(dest, src, sz);
fn(dest, src, sz);
fn(dest, src, sz);
fn(dest, src, sz);
fn(dest, src, sz);
fn(dest, src, sz);
fn(dest, src, sz);
fn(dest, src, sz);
fn(dest, src, sz);
fn(dest, src, sz);
fn(dest, src, sz);
fn(dest, src, sz);
fn(dest, src, sz);
fn(dest, src, sz);
fn(dest, src, sz);
fn(dest, src, sz);
fn(dest, src, sz);
}
}
static void __run_benchmark(unsigned runs, unsigned run_iterations,
void *(*fn)(void *, const void*, size_t), void *dest, const void* src, size_t sz){
unsigned current_run = 1;
while(current_run <= runs){
benchmark_copy_function(run_iterations, fn, dest, src, sz);
printf("Run %d finished\n", current_run);
current_run++;
}
}
Pertimbangkan 2 run yang dikompilasi main.c
Aku .
MSR:
$ sudo rdmsr -p 0 0x1A4
0
Run:
$ taskset -c 0 sudo ../profile.sh ./bin
Performance counter stats for './bin':
10 486 164 071 L1-dcache-loads (12,13%)
10 461 354 384 L1-dcache-load-misses # 99,76% of all L1-dcache hits (12,05%)
10 481 930 413 L1-dcache-stores (12,05%)
10 461 136 686 l1d.replacement (12,12%)
31 466 394 422 l1d_pend_miss.fb_full (12,11%)
211 853 643 294 l1d_pend_miss.pending (12,09%)
1 759 204 317 LLC-loads (12,16%)
31 007 LLC-load-misses # 0,00% of all LL-cache hits (12,16%)
3 154 901 630 LLC-stores (6,19%)
15 867 315 545 l2_rqsts.all_pf (9,22%)
0 sw_prefetch_access.t1_t2 (12,22%)
1 393 306 l2_lines_out.useless_hwpf (12,16%)
3 549 170 919 l2_rqsts.pf_hit (12,09%)
12 356 247 643 l2_rqsts.pf_miss (12,06%)
0 load_hit_pre.sw_pf (12,09%)
3 159 712 695 l2_rqsts.rfo_hit (12,06%)
1 207 642 335 l2_rqsts.rfo_miss (12,02%)
4 366 526 618 l2_rqsts.all_rfo (12,06%)
5 240 013 774 offcore_requests.all_data_rd (12,06%)
19 936 657 118 offcore_requests.all_requests (12,09%)
1 761 660 763 offcore_response.demand_data_rd.any_response (12,12%)
287 044 397 bus-cycles (12,15%)
36 816 767 779 resource_stalls.any (12,15%)
36 553 997 653 resource_stalls.sb (12,15%)
38 035 066 210 uops_retired.stall_cycles (12,12%)
24 766 225 119 uops_executed.stall_cycles (12,09%)
40 478 455 041 uops_issued.stall_cycles (12,05%)
24 497 256 548 cycle_activity.stalls_l1d_miss (12,02%)
12 611 038 018 cycle_activity.stalls_l2_miss (12,09%)
10 228 869 cycle_activity.stalls_l3_miss (12,12%)
24 707 614 483 cycle_activity.stalls_mem_any (12,22%)
24 776 110 104 cycle_activity.stalls_total (12,22%)
48 914 478 241 cycles (12,19%)
12,155774555 seconds time elapsed
11,984577000 seconds user
0,015984000 seconds sys
II
MSR:
$ sudo rdmsr -p 0 0x1A4
1
Run:
$ taskset -c 0 sudo ../profile.sh ./bin
Performance counter stats for './bin':
10 508 027 832 L1-dcache-loads (12,05%)
10 463 643 206 L1-dcache-load-misses # 99,58% of all L1-dcache hits (12,09%)
10 481 296 605 L1-dcache-stores (12,12%)
10 444 854 468 l1d.replacement (12,15%)
29 287 445 744 l1d_pend_miss.fb_full (12,17%)
205 569 630 707 l1d_pend_miss.pending (12,17%)
5 103 444 329 LLC-loads (12,17%)
33 406 LLC-load-misses # 0,00% of all LL-cache hits (12,17%)
9 567 917 742 LLC-stores (6,08%)
1 157 237 980 l2_rqsts.all_pf (9,12%)
0 sw_prefetch_access.t1_t2 (12,17%)
301 471 l2_lines_out.useless_hwpf (12,17%)
218 528 985 l2_rqsts.pf_hit (12,17%)
938 735 722 l2_rqsts.pf_miss (12,17%)
0 load_hit_pre.sw_pf (12,17%)
4 096 281 l2_rqsts.rfo_hit (12,17%)
4 972 640 931 l2_rqsts.rfo_miss (12,17%)
4 976 006 805 l2_rqsts.all_rfo (12,17%)
5 175 544 191 offcore_requests.all_data_rd (12,17%)
15 772 124 082 offcore_requests.all_requests (12,17%)
5 120 635 892 offcore_response.demand_data_rd.any_response (12,17%)
292 980 395 bus-cycles (12,17%)
37 592 020 151 resource_stalls.any (12,14%)
37 317 091 982 resource_stalls.sb (12,11%)
38 121 826 730 uops_retired.stall_cycles (12,08%)
25 430 699 605 uops_executed.stall_cycles (12,04%)
41 416 190 037 uops_issued.stall_cycles (12,04%)
25 326 579 070 cycle_activity.stalls_l1d_miss (12,04%)
25 019 148 253 cycle_activity.stalls_l2_miss (12,03%)
7 384 770 cycle_activity.stalls_l3_miss (12,03%)
25 442 709 033 cycle_activity.stalls_mem_any (12,03%)
25 406 897 956 cycle_activity.stalls_total (12,03%)
49 877 044 086 cycles (12,03%)
12,231406658 seconds time elapsed
12,226386000 seconds user
0,004000000 seconds sys
Saya perhatikan konter:
12 611 038 018 cycle_activity.stalls_l2_miss
v / s
25 019 148 253 cycle_activity.stalls_l2_miss
menunjukkan bahwa MSR menonaktifkan prefetcher L2 HW sedang diterapkan. Juga hal-hal lain yang terkait dengan l2 / LLC berbeda secara signifikan. Perbedaannya dapat direproduksi di berbagai jalur . Masalahnya adalah hampir tidak ada perbedaan dalam total time
dan siklus:
48 914 478 241 cycles
v / s
49 877 044 086 cycles
12,155774555 seconds time elapsed
v / s
12,231406658 seconds time elapsed
PERTANYAAN:
Apakah L2 hilang disembunyikan oleh pembatas kinerja lainnya?
Jika demikian, dapatkah Anda menyarankan penghitung apa yang harus dilihat untuk memahaminya?
Jawaban:
Ya, L2 streamer sangat membantu banyak waktu.
memcpy tidak memiliki latensi komputasi untuk disembunyikan, jadi saya kira itu bisa membiarkan sumber daya OoO exec (ukuran ROB) menangani latensi beban tambahan yang Anda dapatkan dari lebih banyak kesalahan L2, setidaknya dalam kasus ini di mana Anda mendapatkan semua hit L3 dari menggunakan perangkat kerja ukuran sedang (1MiB) yang pas di L3, tidak perlu prefetching untuk membuat hit L3 terjadi.
Dan satu-satunya instruksi adalah load / store (dan loop overhead), sehingga jendela OoO termasuk beban permintaan untuk cukup jauh ke depan.
IDK jika prefetcher spasial L2 dan prefetcher L1 membantu di sini.
Prediksi untuk menguji hipotesis ini : buat array Anda lebih besar sehingga Anda kehilangan L3 dan Anda mungkin akan melihat perbedaan waktu keseluruhan begitu eksekutif OoO tidak cukup untuk menyembunyikan latensi beban hingga DRAM. Preferensi HW memicu lebih jauh ke depan dapat membantu beberapa.
Manfaat besar lainnya dari pengambilan gambar HW datang ketika dapat mengikuti perhitungan Anda, sehingga Anda mendapatkan hit L2. (Dalam satu loop yang memiliki komputasi dengan rantai ketergantungan yang diangkut dengan panjang sedang tetapi tidak.)
Permintaan banyak dan OoO exec dapat melakukan banyak hal sejauh menggunakan bandwidth memori yang tersedia (single threaded), ketika tidak ada tekanan lain pada kapasitas ROB.
Juga catat bahwa pada Intel CPU, setiap cache miss dapat dikenai biaya ulangan back-end (dari RS / scheduler) dari uops dependen , masing-masing untuk L1d dan L2 meleset saat data diharapkan tiba. Dan setelah itu, ternyata core secara optimis mengirim spam saat menunggu data tiba dari L3.
(Lihat https://chat.stackoverflow.com/rooms/206639/discussion-on-question-by-beeonrope-are-load-ops-deallocated-from-the-rs-when-th dan Apakah ops beban deallocated dari RS ketika mereka mengirim, menyelesaikan atau lain waktu? )
Bukan beban cache-miss itu sendiri; dalam hal ini akan menjadi instruksi toko. Lebih khusus lagi, uop data toko untuk port 4. Itu tidak masalah di sini; menggunakan toko 32-byte dan bottlenecking pada bandwidth L3 berarti kita tidak dekat dengan 1 port 4 uop per jam.
sumber
16MiB
buffer dan10
iterasi dan memang mendapat14,186868883 seconds
vs43,731360909 seconds
dan46,76% of all LL-cache hits
vs99,32% of all LL-cache hits
;1 028 664 372 LLC-loads
vs1 587 454 298 LLC-loads
.Ya, prefetcher L2 HW sangat membantu!
Misalnya, temukan hasil di bawah ini di mesin saya (i7-6700HQ) menjalankan tinymembench . Kolom pertama hasil dengan semua prefetcher aktif, kolom hasil kedua adalah dengan L2 streamer dimatikan (tetapi semua prefetcher lainnya masih aktif).
Tes ini menggunakan 32 sumber MIB dan buffer tujuan, yang jauh lebih besar dari L3 pada mesin saya, jadi itu akan menguji sebagian besar meleset ke DRAM.
Dalam tes ini memiliki streamer L2 tidak pernah lebih lambat dan seringkali hampir dua kali lebih cepat.
Secara umum, Anda mungkin memperhatikan pola berikut dalam hasil:
standard memset
danSTOSB fill
(ini mendidih ke hal yang sama pada platform ini) adalah yang paling tidak terpengaruh, dengan hasil pengambilan awal hanya beberapa% lebih cepat daripada tanpa.memcpy
mungkin adalah satu-satunya salinan di sini yang menggunakan instruksi AVX 32-byte, dan itu adalah salah satu yang paling terpengaruh dari salinan - tetapi prefetching masih ~ 40% lebih cepat daripada tanpa.Saya juga mencoba menghidupkan dan mematikan tiga prefetcher lainnya, tetapi mereka umumnya hampir tidak memiliki efek yang dapat diukur untuk tolok ukur ini.
sumber
vmovdqa
apakah AVX1 meskipun "integer".) Apakah menurut Anda loop OP memberikan bandwidth yang lebih rendah daripada glibc memcpy? Dan itu sebabnya 12 LFB cukup untuk mengimbangi beban permintaan pergi ke L3, tanpa mengambil keuntungan dari MLP tambahan dari L2 <-> L3 superqueue dimana streamer L2 dapat terus ditempati? Itu mungkin perbedaan dalam tes Anda. L3 harus berjalan pada kecepatan yang sama dengan inti; Anda berdua memiliki quad-core Skylake-client microarchitectures jadi mungkin mirip L3 latency?uarch-bench
disebutkan dalam komentar).