几个影响 cpu cache 性能因素及 cache 测试工具介绍
===============================》内核新视界文章汇总《===============================
文章目录
- 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.h>int main(int argc, char *argv[])
{long length = 64 * 1024 * 1024;int *arr = malloc(64 * 1024 * 1024 * sizeof(int));long i=0, j=0;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 = 第一次
,实际测试结果如下:
(1)HUAWEI 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 cycle<not supported> branches76,472 branch-misses3.180782182 seconds time elapsed3.084959000 seconds user0.069436000 seconds sys
(2)Phytium 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 cycle<not 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
stride=1
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))
done
(1)HUAWEI 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
可以看到读写时间随读写的步进大小逐渐递增至最后趋近平稳。而在 16,1024 步进时有大幅变化。
上述现象说明 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 i=0; i<steps; i++) { a[0]++; a[0]++; }// Loop 2
for (int i=0; i<steps; i++) { a[0]++; a[1]++; }
循环上 loop2 比 loop1 更快两个代码有如下依赖:
- loop1 依赖关系
x = a[0] -> x++ -> a[0] = x -> y = a[0] -> y++ -> a[0] = y
- loop2 依赖关系
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 i=0, j=0;clock_t start, end;int a[2] = {0};unsigned long steps = 2000 * 1024 * 1024;// loop1start=clock();for (i = 0; i < steps; i++) {a[0]++;barrier();a[0]++;barrier();}end =clock();printf("%lu\n", timediff(start,end));// loop2start=clock();for (i = 0; i < steps; i++) {a[0]++;barrier();a[1]++;barrier();}end =clock();printf("%lu\n", timediff(start,end));return 0;
}
测试如下:
(1)HUAWEI Kunpeng 920
loop1 time 11.611784 s
loop2 time 6.526364 s
(2)Phytium S2500
loop1 time 15.546450 s
loop2 time 7.637472 s
(3)Intel® 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路组相联,需要计算每个 way,sets 使用 bit 再去推算每一组可能丢失命中的字节步长,这里没有计算,略过。
1.6 错误的 cacheline 共享 (缓存一致性)
在多核机器上,缓存会遇到另一个问题,缓存一致性,也就是说多线程,多核之间并行对 cache 访问一致性问题对性能同样会造成严重影响。
在测试机器中,有 L1 L2 独占的,也有超线程下共享 L1的。
这里有如下测试:
for ( int j = 0; j < 100000000; j++) { s_counter[position] = s_counter[position] + 3;
}
使用四个不同的线程参数 0,1,2,3调用上述代码和使用16,32,48,64调用上述代码,16,32,48,64耗时比第一组小很多,这就是缓存一致性引起,代码如下:
// example6.c
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>static 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;
}
测试如下:
(1)HUAWEI Kunpeng 920
# gcc -O0 -g example6.c -lpthread
[root@node-3 test_cache]# time ./a.out // 0,1,2,3real 0m1.889s
user 0m6.994s
sys 0m0.000s
[root@node-3 test_cache]# time ./a.out 1 // 16,32,48,64real 0m0.451s
user 0m1.439s
sys 0m0.000s[root@node-3 test_cache]# time taskset -c 0 ./a.outreal 0m1.251s
user 0m1.240s
sys 0m0.000s
[root@node-3 test_cache]# time taskset -c 0 ./a.out 1real 0m1.250s
user 0m1.239s
sys 0m0.000s
(1)Phytium S2500
# gcc -O0 -g example6.c -lpthread
[root@node-1 test_cache]# time ./a.outreal 0m0.467s
user 0m1.829s
sys 0m0.011s
[root@node-1 test_cache]# time ./a.out 1real 0m0.432s
user 0m1.720s
sys 0m0.001s[root@node-1 test_cache]# time taskset -c 0 ./a.outreal 0m1.724s
user 0m1.722s
sys 0m0.001s
[root@node-1 test_cache]# time taskset -c 0 ./a.out 1real 0m1.727s
user 0m1.714s
sys 0m0.011s
1.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 工具更加标准和准确),生成的测试数据如下:
(1)HUAWEI 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 测试
(1)HUAWEI 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 LIST=<OS>; 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/sec
2.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
"stride=64
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.077
2.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 don't use non-decimal based number\n");
}int main(int argc, char* argv[])
{unsigned long i, j, size, tmp;unsigned long memsize = 0x800000; /* 1/4 LLC size of skylake, 1/5 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 stride=8 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 **)&mem[indices[i]*stride]= (char*)&mem[indices[i+1]*stride];*(char **)&mem[indices[size-1]*stride]= (char*)&mem[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_size=1
stride=64for i in `seq 1 15`; dotaskset -ac 0 $work -b $buffer_size -s $stridebuffer_size=$(($buffer_size*2))
done
HUAWEI 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
相关文章:

几个影响 cpu cache 性能因素及 cache 测试工具介绍
》内核新视界文章汇总《 文章目录 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…...

Java从入门到精通(二)· 基本语法
Java从入门到精通(二) 基本语法 一 变量 1.字面量 计算机是用来处理数据的,字面量就是告诉程序员:数据在程序中的书写格式。 特殊的字符: \n 表示换行, \t 表示一个制表符,即一个tab 2.变量…...

云安全攻防(三)之 面向云原生环境的安全体系
面向云原生环境的安全体系 根据云原生环境的构成,面向云原生环境的安全体系可包含三个层面的安全体制,它们分别是容器安全、编排系统安全和云原生应用安全,下面,我们逐步来讲解这三点: 容器安全 容器环境࿰…...

BGP汇总和破解水平分割
一,BGP的宣告问题 在BGP协议中每台运行BGP的设备上,宣告本地直连路由在BGP协议中运行BGP协议的设备来宣告,通过IGP学习到的,未运行BGP协议设备产生的路由; 在BGP协议中宣告本地路由表中路由条目时,将携带本…...

BUG:pm2启动verdaccio报错:Invalid or unexpected toke
输入命令: pm2 state verdaccio 问题描述: pm2 logs verdaccio报错翻译:数据格式错误 导致我呢提原因,没有找到运行文件, 发现问题:因为命令默认查找verdaccio是去系统盘查找。 解决方式 1:…...

Zookeeper笔记
为什么要使用Zookeeper dubbo需要一个注册中心,而Zookeeper是我们在使用Dubbo是官方推荐的注册中心 Zookeeper介绍 Zookeeper的集群机制 Zookeeper是为了其他分布式程序提供服务的,所以不能随便就挂了。Zookeeper的集群机制采取的是半数存活机制。也…...

【视觉SLAM入门】5.1. 特征提取和匹配--FAST,ORB(关键点描述子),2D-2D对极几何,本质矩阵,单应矩阵,三角测量,三角化矛盾
"不言而善应" 0. 基础知识1. 特征提取和匹配1.1 FAST关键点1.2 ORB的关键点--改进FAST1.3 ORB的描述子--BRIEF1.4 总结 2. 对极几何,对极约束2.1 本质矩阵(对极约束)2.1.1 求解本质矩阵2.1.2 恢复相机运动 R , t R,t R,…...

【能量管理系统( EMS )】基于粒子群算法对光伏、蓄电池等分布式能源DG进行规模优化调度研究(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...

绘制Circos基因圈图
写在前面 昨天在绘制Circos圈图,已经隔了2年左右没有做这类的图了。这时间过得真是快,但是文章和成果依旧是没有很明显的成效。只能安慰自己,后面的时间继续加油吧!关于Cirocs图的制作,我从刚开始到现在都是是使用TBt…...

openGauss学习笔记-26 openGauss 高级数据管理-约束
文章目录 openGauss学习笔记-26 openGauss 高级数据管理-约束26.1 NOT NULL约束26.2 UNIQUE约束26.3 PRIMARY KEY26.4 FOREIGN KEY26.5 CHECK约束 openGauss学习笔记-26 openGauss 高级数据管理-约束 约束子句用于声明约束,新行或者更新的行必须满足这些约束才能成…...

学习React(四)
学习React(四) componentWillMount(被放弃使用)rendercomponentDidMountshouldComponentUpdate(nextProps,nextState)componentWillUpdate(被放弃使用)componentDidUpdatecomponentWillReceiveProps&#x…...

如何将单体项目拆分成微服务
1、如何将单体项目拆分成微服务 如何拆分微服务?其实对不同的业务项目场景,对应有不同的拆分方案。需要项目人员详细的分析项目需求、团队现状、业务边界、业务逻辑等方方面面,拆分的粒度既不能过细,也不能过粗,需要把…...

【Vue框架】Vuex状态管理
前言 在上一篇 【Vue框架】Vue路由配置 结尾时说到store.js,在代码里new Vuex.Store()传入了getters对象;本篇专门针对getters的内容进行整理。 1、getters.js 1.1 代码 // 用于存储获取状态的方法 const getters {// 这里的state参数,是…...

Linked List
文章目录 链表定义专业术语代码链表分类常见算法链表创建和常用算法 链表总结 链表 补充知识 typedef 给类型换名字,比如 typedef struct Student {int sid;char name[100];char sex; }ST;//ST就代表了struct Student //即这上方一大坨都可以用ST表示 //原先结构体…...

javascript数组基础
文章和代码已经归档至【Github仓库:https://github.com/timerring/front-end-tutorial 】或者公众号【AIShareLab】回复 javascript 也可获取。 文章目录 数组的基本使用定义数组和数组单元访问数组和数组索引数据单元值类型数组长度属性操作数组 数组:(…...

【模型预测控制MPC】使用离散、连续、线性或非线性模型对预测控制进行建模(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...

Golang之路---01 Golang VS Code创建项目
Golang VS Code创建项目 代码组织 Golang使用包和模块来组织代码,包对应到文件系统就是文件夹,模块就是xxx.go的go源文件。一个包中会有多个模块,或者多个子包。 早期使用的是gopath来管理项目,不方便,比较麻烦&…...

vue 表单form-item模板(编辑,查看,新建)
目录 formatFormData 后端数据格式编辑 JSON解析和生成 加载(请求前,await后) formComp formatFormData 后端数据格式 为空的,可以直接不提交/提交null/undefined JSON解析和生成 var str {"name": "…...

【IC设计】DC工具的target、link、synthetic、symbol库
Specifying Libraries You use dc_shell variables to specify the libraries used by Design Compiler. Table 4-1 lists the variables for each library type as well as the typical file extension for the library. 你使用dc_shell变量去指定dc要使用的库。下表列出了每种…...

redisson常用APi-Example
中文文档目录 redisson中文文档目录 分布式对象 package com.example.redissondemo.test;import com.example.redissondemo.RedissonDemoApplication; import com.example.redissondemo.test.domain.Order; import lombok.Data; import lombok.extern.slf4j.Slf4j; import o…...

小程序学习(四):WXML模板语法
WXML模板语法-数据绑定 1.数据绑定的基本原则 ①在data中定义数据 ②在WXML中使用数据 2.动态绑定属性 WXML模板语法-事件绑定 3.什么是事件 4.小程序中常用的事件 5.事件对象的属性列表 6.target和currentTarget的区别 7.bindtap的语法格式 8.在事件处理函数中为data中的数据…...

IDEA好用的插件总结
IdeaVim 这个看个人喜好,我比较喜欢用vim,并且支持自定义修改按键绑定alibaba java code guidelines alibaba的java编程规范plantUML 绘制UML,支持语言显示plantUML integration 能够直接将代码转化为UML图,非常方便rainbow brack…...

如何在Linux系统中安装ActiveMQ
1、环境 ActiveMQ是一个纯Java程序,这里安装5.18.2版ActiveMQ,该版MQ运行在JDK 11环境内,为此需要先搭建JDK 11环境,这里安装JDK 15。 1.1、卸载 卸载开源JDK软件包,如下所示: [rootlocalhost ~]# rpm -…...

【Latex】常用公式编辑与符号:公式换行,标号居中、常用符号等
【Latex】常用公式编辑与符号 文章目录 【Latex】常用公式编辑与符号1. 公式换行,且标号居中2. 常用符号3. 常用的希腊字母 1. 公式换行,且标号居中 \begin{equation}\label{eq14} \begin{aligned}a & b/c, \\d & e/f \end{aligned} \end{equ…...

【ArcGIS Pro二次开发】(55):给多个要素或表批量添加字段
在工作中可能会遇到这样的场景:有多个GDB要素、表格,或者是SHP文件,需要给这个要素或表添加相同的多个字段。 在这种情况下,手动添加就变得很繁琐,于是就做了这个工具。 需求具体如下图: 左图是待处理数据…...

CentOS7.3 安装 docker
亲测、截图 阿里云服务器 文章目录 更新源2345 启动开机自启 更新源 sudo yum update -y2 sudo yum install -y yum-utils device-mapper-persistent-data lvm23 sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo4 sudo yum …...

代码随想录算法训练营第五十二天 | 300.最长递增子序列、674.最长连续递增序列、718.最长重复子数组
文章目录 一、300.最长递增子序列二、674.最长连续递增序列三、718.最长重复子数组 一、300.最长递增子序列 题目链接 代码如下: class Solution { public:int lengthOfLIS(vector<int>& nums) {if (nums.size() < 1) return nums.size();vector<…...

1、Tomcat
java介绍 Java语言和平台由以下几个主要部分组成: 1、Java编程语言(Java Language):这是Java的核心部分,包括Java语法、关键字、数据类型、运算符、控制结构等。程序员使用Java语言来编写应用程序的源代码。 2、Java开发工具包(Java Developm…...

centos 内网实现mail发送
文章目录 1、frp 穿透公网和内网2、邮件 配置2.1、mail配置文件 3、测试 1、frp 穿透公网和内网 参考地址:https://zhaosongbin.blog.csdn.net/article/details/88865890 frps端部署在内网,frpc端部署在外网 frps端配置和上面文章中的一样,…...

【雕爷学编程】MicroPython动手做(25)——语音合成与语音识别2
知识点:什么是掌控板? 掌控板是一块普及STEAM创客教育、人工智能教育、机器人编程教育的开源智能硬件。它集成ESP-32高性能双核芯片,支持WiFi和蓝牙双模通信,可作为物联网节点,实现物联网应用。同时掌控板上集成了OLED…...