PS3でTLBレイテンシを計測
Handbookによると、PPEのTLBは1024エントリもあって、4MB分。で、512KBのL2キャッシュの8倍もの領域をカバーする。(なので、キャッシュに乗らない影響なのかTLBが足りない影響なのかがわかりにくいのだが…)
とりあえず3000ページほど読ませたところ↓このぐらい。(時間はmftbの値を40倍している。PS3では+-1ぐらいの誤差でサイクル数と同じになるはず)
$ ./a.out 3000 3010 0 # hugetlb無し 3000: 590.528125 3001: 591.494606 3002: 590.975053 3003: 591.010071 3004: 591.499888 3005: 591.094205 3006: 591.120480 3007: 590.863311 3008: 591.832548 3009: 591.158010 $ ./a.out 3000 3010 1 # hugetlb有り 3000: 444.871432 3001: 444.612902 3002: 444.610143 3003: 444.606500 3004: 444.732154 3005: 444.541714 3006: 444.599772 3007: 444.594319 3008: 444.619245 3009: 444.625589
TLBミスった時のペナルティは150cycleぐらいか…
ついでにSPEも測っとこう。
$ ./tlb 1 16 0 1 # hugetlb無し 0001: 595.939941 0002: 577.701416 0003: 571.557617 0004: 570.562744 0005: 569.965332 0006: 569.510905 0007: 569.212123 0008: 569.064026 0009: 569.189996 0010: 568.953369 0011: 568.426403 0012: 568.741455 0013: 568.719764 0014: 568.782610 0015: 568.745280 $ ./tlb 1 16 1 1 # hugetlb有り 0001: 572.163086 0002: 558.167725 0003: 553.348796 0004: 550.966187 0005: 549.134766 0006: 548.186442 0007: 547.724609 0008: 546.977539 0009: 546.493327 0010: 545.870850 0011: 545.509588 0012: 545.153402 0013: 545.004132 0014: 544.826486 0015: 544.730143
TLBが当たってるときは、540ぐらい。この時点で違いがあるのはなんで?
SPEのTLBは256個なので、512個ぐらいアクセスして、
$ ./tlb 512 520 0 1 # hugetlb無し 0512: 1040.265656 0513: 1038.673246 0514: 1038.606290 0515: 1039.193871 0516: 1040.109163 0517: 1038.715320 0518: 1038.623914 0519: 1039.168322 $ ./tlb 512 520 1 1 # hugetlb有り 0512: 544.081879 0513: 543.906022 0514: 543.887935 0515: 543.974894 0516: 543.947584 0517: 543.920303 0518: 543.921785 0519: 543.926195
こんな感じ。ほぼ倍だなー。
特に結論は無いが、所感を書いておくと…
- x86はページテーブルがL2にキャッシュされるのでTLBミスってもペナルティは20cycle程度になる。
- PPCは多分ページテーブルがキャッシュされないので、ペナルティ150cycleぐらいになる。
- (上のはPPEだが、多分、PowerPC全体的にそうなってんじゃないかという気がする。根拠は無い)
- そのかわりPPCに比べるとx86はTLBエントリの数が少ない。あとPhoenomのTLBエラッタは多分ここらへんの複雑さが原因なんかも。
- PPEはキャッシュ少ないのでTLBミスよりキャッシュミスのほうが影響大きいが、キャッシュが数MBぐらいに大きいCPUだと、アプリケーションによっては全体で数十%ぐらい変わる可能性はあるかも。
コードは以下
#include <stdlib.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <sys/mman.h> #include <ppu_intrinsics.h> #include <libspe2.h> #ifdef __x86__ static unsigned int rdtsc() { int a, d; asm volatile ("rdtsc" :"=a"(a), "=d"(d)); return a; } #else static unsigned int rdtsc() { return __mftb()*40; } #endif char * alloc16M(int huge) { if (! huge) { return malloc(4096*4096); } else { int fd = open("/hugetlb/16M", O_CREAT|O_RDWR, 0755); char *ptr; ptr = mmap(0, 4096*4096, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); unlink("/hugetlb/16M"); return ptr; } } int main(int argc, char **argv) { int begin = atoi(argv[1]); int end = atoi(argv[2]); int huge = atoi(argv[3]); int spu = atoi(argv[4]); char *data = alloc16M(huge); int i, j, k; memset(data, 0, 4096*4096); if (spu) { unsigned int mbox_data; spe_context_ptr_t spe = spe_context_create(0,0); spe_program_handle_t *h; unsigned int entry = SPE_DEFAULT_ENTRY; mbox_data = begin; spe_in_mbox_write(spe, &mbox_data, 1, SPE_MBOX_ALL_BLOCKING); mbox_data = end; spe_in_mbox_write(spe, &mbox_data, 1, SPE_MBOX_ALL_BLOCKING); h = spe_image_open("tlb-spu"); spe_program_load(spe, h); spe_context_run(spe, &entry, 0, data, NULL, NULL); } else { for (i=begin; i<end; i+=1) { int l; int x = rdtsc(), y; for (l=0; l<1024; l++) { for (j=0; j<i; j++) { if (1) { char c = data[j*4096+(j*64)%4096]; asm("#"::"r"(c)); } else { data[j*4096+(j*64)%4096] = 0; } } } y = rdtsc(); printf("%04d: %f\n", i, (y-x)/(double)(l*i)); } } return 0; }
#include <spu_mfcio.h> typedef unsigned long long ea_t; static unsigned char ls_buffer[128] __attribute__((aligned(128))); int main(unsigned long long spe, unsigned long long argv) { ea_t buffer = argv; int begin = spu_read_in_mbox(); int end = spu_read_in_mbox(); int i,j; spu_write_decrementer(~0); for (i=begin; i<end; i++) { int l; int x = spu_read_decrementer()*40, y; for (l=0; l<1024; l++) { for (j=0; j<i; j++) { ea_t addr = buffer + j*4096; mfc_get(ls_buffer, addr, 128, 0, 0, 0); mfc_write_tag_mask(~0); mfc_read_tag_status_all(); } } y = spu_read_decrementer()*40; printf("%04d: %f\n", i, (x-y)/(double)(l*i)); } return 0; }