景德镇网站建设景德镇找程序员的网站

当前位置: 首页 > news >正文

景德镇网站建设景德镇,找程序员的网站,绝对大气漂亮的响应式网站后台模板,荣耀手机官方网站首页》内核新视界文章汇总《 文章目录 1 cache 性能及影响因素1.1 内存访问和性能比较1.2 cache line 对性能的影响1.3 L1 和 L2 缓存大小1.4 指令集并行性对 cache 性能的影响1.5 缓存关联性对 cache 的影响1.6 错误的 cacheline 共享 (缓存一致性)1.7 硬件设计 2 cpu cache benc…》内核新视界文章汇总《 文章目录 1 cache 性能及影响因素1.1 内存访问和性能比较1.2 cache line 对性能的影响1.3 L1 和 L2 缓存大小1.4 指令集并行性对 cache 性能的影响1.5 缓存关联性对 cache 的影响1.6 错误的 cacheline 共享 (缓存一致性)1.7 硬件设计 2 cpu cache benchmark 工具2.1 使用 llcbench 工具对 cache 进行性能测试2.2 使用 pts 工具对内存缓存带宽进行测试2.3 lmbench 对 L1, L2, L3 cache 时延及带宽测试2.3.1 工具常规测试步骤2.3.2 stream 带宽及时延测试2.3.2 lat_mem_rd 不同数据长度的时延测试2.3.2.1 lat_mem_rd 替代实验 1 cache 性能及影响因素 1.1 内存访问和性能比较 有如下测试代码 #include stdio.h #include stdlib.h #include time.h #include sys/time.hint main(int argc, char *argv[]) {long length 64 * 1024 * 1024;int *arr malloc(64 * 1024 * 1024 * sizeof(int));long i0, j0;struct timeval tv1, tv2;struct timezone tz;unsigned long sec, usec;for (i 0; i length; i) arr[i] i;// 循环1gettimeofday (tv1, tz);for(j 0; j 10; j) {//每取一次arr[i], 通过 cache_line 顺便把后面 15 个 arr[i] 都取过来了for (i 0; i length; i) arr[i] * 3;}gettimeofday (tv2, tz);if (tv2.tv_usec tv1.tv_usec) {usec 1000000 tv2.tv_usec - tv1.tv_usec;sec tv2.tv_sec - tv1.tv_sec - 1;} else {usec tv2.tv_usec - tv1.tv_usec;sec tv2.tv_sec - tv1.tv_sec;}printf(time %ld.%06ld s\n, sec, usec);// 循环2gettimeofday (tv1, tz);for(j 0; j 10; j) {for (i 0; i length; i 16) arr[i] * 3;}gettimeofday (tv2, tz);if (tv2.tv_usec tv1.tv_usec) {usec 1000000 tv2.tv_usec - tv1.tv_usec;sec tv2.tv_sec - tv1.tv_sec - 1;} else {usec tv2.tv_usec - tv1.tv_usec;sec tv2.tv_sec - tv1.tv_sec;}printf(time %ld.%06ld s\n, sec, usec);return 0; }该测试代码预先分配大量内存并与一个数组指针管理使其可以通过数组索引访问该片申请内存接着对该内存域进行预读写使其在测试中不受 pagefault 影响测试结果。 该测试主要的测试点: 第一个循环按照字节顺序读写相同固定数量的数据。第二个循环跨 cacheline 读取比第一次少 16 倍的数据量。 理论不考虑 cache 则读写时间应该是 第二次 * 16 第一次实际测试结果如下 1HUAWEI Kunpeng 920

./a.out

time 2.522369 s time 0.339671 s# perf stat – ./a.out time 2.623479 s time 0.307353 sPerformance counter stats for ./a.out:3,154.14 msec task-clock # 0.992 CPUs utilized7 context-switches # 0.002 K/sec1 cpu-migrations # 0.000 K/sec6,024 page-faults # 0.002 M/sec8,200,710,113 cycles # 2.600 GHz15,203,228,361 instructions # 1.85 insn per cyclenot supported branches76,472 branch-misses3.180782182 seconds time elapsed3.084959000 seconds user0.069436000 seconds sys2Phytium S2500飞腾 S2500

./a.out

time 5.085658 s time 1.352258 s# perf stat – ./a.out time 5.535788 s time 0.826428 sPerformance counter stats for ./a.out:6,930.59 msec task-clock # 0.999 CPUs utilized50 context-switches # 0.007 K/sec25 cpu-migrations # 0.004 K/sec14,731 page-faults # 0.002 M/sec14,552,534,159 cycles # 2.100 GHz15,281,585,654 instructions # 1.05 insn per cyclenot supported branches254,832 branch-misses6.934533427 seconds time elapsed6.481427000 seconds user0.450099000 seconds sys可以看到两台机器第一次循环花费时间都远小于第二次的 16 这个倍数。 原因是第一次循环读写会从内存按照 cacheline 大小加载连续内存到 cache 中如果后续读写数据在 cache 中则可以高效读写数据而不会从内存中读取。 而第二次循环跨 cacheline 读写数据每次均有可能从内存读写数据。 从上述测试中可以看到cache 读 cpu 读写性能影响非常大。并且从测试也可以看到鲲鹏机器的 cache 读写速率高于飞腾机器。 1.2 cache line 对性能的影响 从 1.1 中的测试能够看到 cache 对性能的影响而 cache 的 cacheline 的大小对性能提升也有着直接作用。 cache line 对 cache 性能影响尤为重要, 程序对 cacheline 的优化会大幅提升运行性能, 这里对 1.1 中的例子进行测试, 将步长 16 从 1 依次成倍增长测试不同步长测试相同次数获取时延 脚本如下

./example2.sh

#set -xwork./a.out stride1 for i in seq 1 15; doperf stat -e cache-misses -e cache-references -e cpu-cycles -e instructions -e L1-dcache-load-misses -e L1-dcache-loads -e L1-dcache-store-misses -e L1-dcache-stores – \(work \)stridestride\(((\)stride*2)) done1HUAWEI Kunpeng 920

./example2.sh

stride 1, time 2.817894 s stride 2, time 2.920336 s stride 4, time 3.495400 s stride 8, time 4.565321 s stride 16, time 6.297579 s stride 32, time 7.362044 s stride 64, time 7.982766 s stride 128, time 10.473204 s stride 256, time 9.676618 s stride 512, time 12.120638 s stride 1024, time 13.391726 s stride 2048, time 17.123795 s stride 4096, time 23.674495 s stride 8192, time 24.273880 s stride 16384, time 28.081542 s可以看到读写时间随读写的步进大小逐渐递增至最后趋近平稳。而在 161024 步进时有大幅变化。 上述现象说明 cacheline 大小对性能影响的变化。 当步进在 Dcache-L1 时比如 64 字节此时在 Dcache-L1 中读写数据整体性能会非常好。 当步进从 L2 跨度到从 Dcache-L2 读写数据时此时读写性能将会有一个明显降低同理 L3 时会继续有个明显变化幅度直至最后趋于稳定时只能每次都从内存读写数据。 1.3 L1 和 L2 缓存大小 如 1.2 测试中当在 cacheline 范围内读写数据时性能最好那么 cacheline 越大性能则会越好。 1.4 指令集并行性对 cache 性能的影响 有如下代码 int steps 256 * 1024 * 1024; int a[2];// Loop 1 for (int i0; isteps; i) { a[0]; a[0]; }// Loop 2 for (int i0; isteps; i) { a[0]; a[1]; }循环上 loop2 比 loop1 更快两个代码有如下依赖 loop1 依赖关系 x a[0] - x - a[0] x - y a[0] - y - a[0] yloop2 依赖关系 ​ x a[0] - x - a[0] x y a[1] - y - a[1] y 即机器具有有并行性它可以同时访问 L1 中的两个内存位置loop1 中处理器无法利用这种指令级并行性但是 loop2 可以代码如下 #include stdio.h #include stdlib.h #include time.h#define barrier() asm volatile(: : :memory)static long timediff(clock_t t1, clock_t t2) {long elapsed;elapsed ((double)t2 - t1) / CLOCKS_PER_SEC * 1000;return elapsed; }int main(int argc, char *argv[]) {long i0, j0;clock_t start, end;int a[2] {0};unsigned long steps 2000 * 1024 * 1024;// loop1startclock();for (i 0; i steps; i) {a[0];barrier();a[0];barrier();}end clock();printf(%lu\n, timediff(start,end));// loop2startclock();for (i 0; i steps; i) {a[0];barrier();a[1];barrier();}end clock();printf(%lu\n, timediff(start,end));return 0; }测试如下 1HUAWEI Kunpeng 920 loop1 time 11.611784 s loop2 time 6.526364 s2Phytium S2500 loop1 time 15.546450 s loop2 time 7.637472 s3Intel® Xeon® Gold 5218 loop1 time 8.084105 s loop2 time 6.649898 s可以看到指令集并行性在能够并行时性能都会有明显提升另外三台机器中并行性最好的是华为鲲鹏然而最好的并行性和因特尔 5218 持平非并行性下 intel 具有最好的读写性能。 1.5 缓存关联性对 cache 的影响 目前有三种映射缓存方式 直接映射缓存 每个内存块只能存储在缓存中的一个特定插槽中。一种简单的解决方案是将索引为 chunk_index 的块映射到缓存槽 (chunk_index % cache_slots)。映射到同一插槽的两个内存块不能同时存储在缓存中。 N路组相联高速缓存 每个内存块都可以存储在缓存中 N 个特定插槽中的任何一个中。例如在 16 路缓存中每个内存块可以存储在 16 个不同的缓存槽中。通常具有相同最低位的索引的块将共享 16 个槽。 全关联缓存 每个内存块都可以存储在缓存中的任何槽中。实际上缓存的运行方式类似于哈希表。
直接映射缓存可能会发生冲突当多个值竞争缓存中的同一个插槽时它们会不断相互驱逐命中率直线下降。 全关联高速缓存在硬件中实现起来既复杂又昂贵。 N 路组关联高速缓存是处理器高速缓存的典型解决方案因为它们在实现简单性和良好的命中率之间取得了很好的平衡。 当前所有测试机器都是 N路组相联需要计算每个 waysets 使用 bit 再去推算每一组可能丢失命中的字节步长这里没有计算略过。 1.6 错误的 cacheline 共享 (缓存一致性) 在多核机器上缓存会遇到另一个问题缓存一致性也就是说多线程多核之间并行对 cache 访问一致性问题对性能同样会造成严重影响。 在测试机器中有 L1 L2 独占的也有超线程下共享 L1的。 这里有如下测试: for ( int j 0; j 100000000; j) { s_counter[position] s_counter[position] 3; } 使用四个不同的线程参数 0123调用上述代码和使用16324864调用上述代码16324864耗时比第一组小很多这就是缓存一致性引起代码如下 // example6.c #include stdio.h #include stdlib.h #include time.h #include pthread.h #include unistd.h #include stdio.hstatic int s_counter[1024] attribute ((aligned (64)));int aaa 1;static void update_counter(int posion) {for (int i 0; i 100000000; i) {s_counter[posion] s_counter[posion] 3;} }static void *thread_func1(void *param) {if (aaa)update_counter(0);elseupdate_counter(16);return NULL; }static void *thread_func2(void *param) {if (aaa)update_counter(1);elseupdate_counter(32);return NULL; }static void *thread_func3(void *param) {if (aaa)update_counter(2);elseupdate_counter(48);return NULL; }static void *thread_func4(void *param) {if (aaa)update_counter(3);elseupdate_counter(64);return NULL; }int main(int argc, char *argv[]) {pthread_t tid1, tid2, tid3, tid4;if (argc ! 1)aaa 0;pthread_create(tid1, NULL, thread_func1, NULL);pthread_create(tid2, NULL, thread_func2, NULL);pthread_create(tid3, NULL, thread_func3, NULL);pthread_create(tid4, NULL, thread_func4, NULL);pthread_join(tid1, NULL);pthread_join(tid2, NULL);pthread_join(tid3, NULL);pthread_join(tid4, NULL);return 0; }测试如下 1HUAWEI Kunpeng 920

gcc -O0 -g example6.c -lpthread

[rootnode-3 test_cache]# time ./a.out // 0,1,2,3real 0m1.889s user 0m6.994s sys 0m0.000s [rootnode-3 test_cache]# time ./a.out 1 // 16,32,48,64real 0m0.451s user 0m1.439s sys 0m0.000s[rootnode-3 test_cache]# time taskset -c 0 ./a.outreal 0m1.251s user 0m1.240s sys 0m0.000s [rootnode-3 test_cache]# time taskset -c 0 ./a.out 1real 0m1.250s user 0m1.239s sys 0m0.000s1Phytium S2500

gcc -O0 -g example6.c -lpthread

[rootnode-1 test_cache]# time ./a.outreal 0m0.467s user 0m1.829s sys 0m0.011s [rootnode-1 test_cache]# time ./a.out 1real 0m0.432s user 0m1.720s sys 0m0.001s[rootnode-1 test_cache]# time taskset -c 0 ./a.outreal 0m1.724s user 0m1.722s sys 0m0.001s [rootnode-1 test_cache]# time taskset -c 0 ./a.out 1real 0m1.727s user 0m1.714s sys 0m0.011s1.7 硬件设计 在某些处理器上如果 L1 缓存访问来自不同 bank 的缓存行则它们可以并行处理两个访问如果它们属于同一 bank则可以串行处理这是“硬件怪异”的一个奇怪例子 static int A, B, C, D, E, F, G; static void Weirdness() {for (int i 0; i 200000000; i){something} }something Time A; B; C; D; 719 ms A; C; E; G; 448 ms A; C; 518 ms递增字段 A、B、C、D 比递增字段 A、C、E、G 花费的时间更长。 仅递增 A 和 C 比递增 A 和 C 以及 E 和 G花费的时间更长。 2 cpu cache benchmark 工具 2.1 使用 llcbench 工具对 cache 进行性能测试 LLCbench (底层表征基准测试 Low-Level Characterization Benchmarks) 是一个基准测试工具, 集成了 MPBench, CacheBench 和 BLASBench 测试方法。这里只使用其中的 CacheBench 测试缓存性能。llcbench 工具目前可能需要一些科学上网才能获取 测试步骤:

tar -xvf llcbench-20170104.tar.gz

cd llcbench;make;make linux-lam;make cache-bench;make cache-run

make cache-scripts

make cache-graph测试大致原理: 对不同字节长度数组进行同样步长的循环读写测试统计时间同 1 中 1.1 测试类似不过 llcbench 工具更加标准和准确生成的测试数据如下 1HUAWEI Kunpeng 920

全局测试 numa 绑定最近节点: numactl -C 24 -m 1 make cache-run distances 10 numa 绑定最远节点: numactl -C 24 -m 3 make cache-run distances 24 通过跳变位置可以看到每个 cache 级别的大小以及内存读写速率位置。同样的 numa 对性能影响也是明显的体现在图标最后一个坡段。 2.2 使用 pts 工具对内存缓存带宽进行测试 pts (phoronix-test-suite) 的 cachebench 测试底层同样使用 llcbench 进行测试, 这里使用 pts 测试各平台内存缓存带宽。 测试步骤如下:

./install-sh # 安装 pts

phoronix-test-suite install pts/cachebench # 下载 cachebench 测试用例

phoronix-test-suite run pts/cachebench # 运行 cachebench 测试1HUAWEI Kunpeng 920

读测试 写测试 读-修改-写测试: 测试报告: https://openbenchmarking.org/result/2302010-NE-KUNPENG3610 2.3 lmbench 对 L1, L2, L3 cache 时延及带宽测试 lmbench 是一个微型评测工具, 它衡量两个关键特征: 反应时间和带宽。 使用工具集中的 stream 测试带宽对应的时延是在带宽跑满下的时延。 使用工具集中的 lat_mem_rd 测试时延主要对应不同数据大小下的时延。 2.3.1 工具常规测试步骤

编译问题

make results LDFLAGS-ltirpc

yum -y install libtirpc libtirpc-devel

cp /usr/include/tirpc/rpc/* /usr/include/rpc/ -rf

ln -s /usr/include/tirpc/netconfig.h /usr/include# 测试

cd src

make results

make rerun; make rerun; make rerun; cd results; make LISTOS; make ps;一个示例对应结果如下:

Memory latencies in nanoseconds - smaller is better(WARNING - may not be correct, check graphs)

Host OS Mhz L1 \( L2 \) Main mem Rand mem Guesses


easystack Linux 5.15.79 4083 1.2130 2.5860 8.6590 161.1显示结果分别为(单位纳秒): L1: L1 cache 时延 L2: L2 cache 时延 Main mem: 顺序读内存时延 Rand mem: 随机访问内存时延 Guesses: 当 L2 不存在时, 可能会打印 No L2 cache?对应分布图如下: lmbench 常规测试可以很好反应各级别 cache 时延, 以及不同数据跨度下 cache 时延的变化, 不过 lmbench 测试对 arm64 平台测试有问题, 且每次测试时间过长, 这里直接使用 stream 和 lat_mem_rd 进行测试。 2.3.2 stream 带宽及时延测试 HUAWEI Kunpeng 920同一个 numa 到不同 cpu 的时延及带宽 (node - cpux)

for i in \((seq 0 24 95); do echo cpu:\)i - node0; numactl -C $i -m 0 ./stream -W 50 -N 50 -M 64M; done

cpu:0 - node0 STREAM copy latency: 1.28 nanoseconds STREAM copy bandwidth: 12546.05 MB/sec STREAM scale latency: 1.31 nanoseconds STREAM scale bandwidth: 12207.16 MB/sec STREAM add latency: 1.94 nanoseconds STREAM add bandwidth: 12381.71 MB/sec STREAM triad latency: 2.18 nanoseconds STREAM triad bandwidth: 11019.52 MB/sec cpu:24 - node0 STREAM copy latency: 1.47 nanoseconds STREAM copy bandwidth: 10851.14 MB/sec STREAM scale latency: 1.51 nanoseconds STREAM scale bandwidth: 10610.51 MB/sec STREAM add latency: 2.03 nanoseconds STREAM add bandwidth: 11812.86 MB/sec STREAM triad latency: 2.34 nanoseconds STREAM triad bandwidth: 10247.19 MB/sec cpu:48 - node0 STREAM copy latency: 1.75 nanoseconds STREAM copy bandwidth: 9152.87 MB/sec STREAM scale latency: 2.54 nanoseconds STREAM scale bandwidth: 6298.64 MB/sec STREAM add latency: 2.35 nanoseconds STREAM add bandwidth: 10191.17 MB/sec STREAM triad latency: 3.81 nanoseconds STREAM triad bandwidth: 6303.67 MB/sec cpu:72 - node0 STREAM copy latency: 1.84 nanoseconds STREAM copy bandwidth: 8699.05 MB/sec STREAM scale latency: 2.83 nanoseconds STREAM scale bandwidth: 5661.04 MB/sec STREAM add latency: 2.56 nanoseconds STREAM add bandwidth: 9362.28 MB/sec STREAM triad latency: 4.14 nanoseconds STREAM triad bandwidth: 5792.24 MB/sec2.3.2 lat_mem_rd 不同数据长度的时延测试 HUAWEI Kunpeng 920同一个 numa 到不同 cpu 的时延 (node - cpux)

for i in \((seq 0 24 95); do echo cpu:\)i - node0; numactl -C $i -m 0 ./lat_mem_rd -W 10 -N 10 -t 64M; done

cpu:0 - node0 stride64 0.00049 1.552 0.00098 1.552 0.00195 1.552 0.00293 1.552 … 0.10938 1.552 0.11719 1.552 0.12500 3.104 0.14062 3.104 0.15625 3.103 0.17188 3.104 … 0.46875 3.104 0.50000 3.106 0.56250 7.389 0.62500 9.385 0.68750 10.238 0.75000 11.680 0.81250 11.735 0.87500 13.0772.3.2.1 lat_mem_rd 替代实验 使用 memory_latency.c 对不同数据长度进行相同步长测试: #include sys/types.h #include stdlib.h #include stdio.h #include sys/mman.h #include sys/time.h #include unistd.h#define ONE p (char **)p; #define FIVE ONE ONE ONE ONE ONE #define TEN FIVE FIVE #define FIFTY TEN TEN TEN TEN TEN #define HUNDRED FIFTY FIFTYstatic void usage() {printf(Usage: ./mem-lat -b xxx -n xxx -s xxx\n);printf( -b buffer size in KB\n);printf( -n number of read\n\n);printf( -s stride skipped before the next access\n\n);printf(Please dont use non-decimal based number\n); }int main(int argc, char argv[]) {unsigned long i, j, size, tmp;unsigned long memsize 0x800000; /* 14 LLC size of skylake, 15 of broadwell /unsigned long count 1048576; / memsize / 64 * 8 /unsigned int stride 64; / skipped amount of memory before the next access */unsigned long sec, usec;struct timeval tv1, tv2;struct timezone tz;unsigned int *indices;while (argc– 0) {if ((argv)[0] -) { / look at first char of next */switch ((argv)[1]) { / look at second */case b:argv;argc–;memsize atoi(*argv) * 1024;break;case n:argv;argc–;count atoi(*argv);break;case s:argv;argc–;stride atoi(argv);break;default:usage();exit(1);break;}}argv;}char mem mmap(NULL, memsize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);// trick3: init pointer chasing, per stride8 bytesize memsize / stride;indices malloc(size * sizeof(int));for (i 0; i size; i)indices[i] i;// trick 2: fill mem with pointer referencesfor (i 0; i size - 1; i)*(char **)memindices[i]*stridemem[indices[i1]stride];(char **)memindices[size-1]*stridemem[indices[0]*stride];register char **p (char **) mem;tmp count / 100;gettimeofday (tv1, tz);for (i 0; i tmp; i) {HUNDRED; //trick 1}gettimeofday (tv2, tz);char **touch p;if (tv2.tv_usec tv1.tv_usec) {usec 1000000 tv2.tv_usec - tv1.tv_usec;sec tv2.tv_sec - tv1.tv_sec - 1;} else {usec tv2.tv_usec - tv1.tv_usec;sec tv2.tv_sec - tv1.tv_sec;}printf(Buffer size: %ld KB, stride %d, time %ld.%06ld s, latency %.2f ns\n,memsize/1024, stride, sec, usec, (sec * 1000000 usec) * 1000.0 / (tmp *100));munmap(mem, memsize);free(indices); }mem-lat.sh #set -xwork./a.out buffer_size1 stride64for i in seq 1 15; dotaskset -ac 0 \(work -b \)buffer_size -s \(stridebuffer_size\)(($buffer_size*2)) doneHUAWEI Kunpeng 920

./mem-lat.sh

Buffer size: 1 KB, stride 64, time 0.001627 s, latency 1.55 ns Buffer size: 2 KB, stride 64, time 0.001627 s, latency 1.55 ns Buffer size: 4 KB, stride 64, time 0.001637 s, latency 1.56 ns Buffer size: 8 KB, stride 64, time 0.001627 s, latency 1.55 ns Buffer size: 16 KB, stride 64, time 0.001626 s, latency 1.55 ns Buffer size: 32 KB, stride 64, time 0.001627 s, latency 1.55 ns Buffer size: 64 KB, stride 64, time 0.001664 s, latency 1.59 ns Buffer size: 128 KB, stride 64, time 0.003489 s, latency 3.33 ns Buffer size: 256 KB, stride 64, time 0.003542 s, latency 3.38 ns Buffer size: 512 KB, stride 64, time 0.003605 s, latency 3.44 ns Buffer size: 1024 KB, stride 64, time 0.004646 s, latency 4.43 ns Buffer size: 2048 KB, stride 64, time 0.004625 s, latency 4.41 ns Buffer size: 4096 KB, stride 64, time 0.005523 s, latency 5.27 ns Buffer size: 8192 KB, stride 64, time 0.006047 s, latency 5.77 ns Buffer size: 16384 KB, stride 64, time 0.007000 s, latency 6.68 ns