几个影响 cpu cache 性能因素及 cache 测试工具介绍

news2024/11/16 0:44:15

===============================》内核新视界文章汇总《===============================

文章目录

      • 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;

    // 循环1
    gettimeofday (&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);

    // 循环2
    gettimeofday (&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 影响测试结果。
该测试主要的测试点:

  1. 第一个循环按照字节顺序读写相同固定数量的数据。
  2. 第二个循环跨 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 s

 Performance counter stats for './a.out':

          3,154.14 msec task-clock                #    0.992 CPUs utilized
                 7      context-switches          #    0.002 K/sec
                 1      cpu-migrations            #    0.000 K/sec
             6,024      page-faults               #    0.002 M/sec
     8,200,710,113      cycles                    #    2.600 GHz
    15,203,228,361      instructions              #    1.85  insn per cycle
   <not supported>      branches
            76,472      branch-misses

       3.180782182 seconds time elapsed

       3.084959000 seconds user
       0.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 s

 Performance counter stats for './a.out':

          6,930.59 msec task-clock                #    0.999 CPUs utilized
                50      context-switches          #    0.007 K/sec
                25      cpu-migrations            #    0.004 K/sec
            14,731      page-faults               #    0.002 M/sec
    14,552,534,159      cycles                    #    2.100 GHz
    15,281,585,654      instructions              #    1.05  insn per cycle
   <not supported>      branches
           254,832      branch-misses

       6.934533427 seconds time elapsed

       6.481427000 seconds user
       0.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 -x

work=./a.out
stride=1
for i in `seq 1 15`; do
    perf 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 $stride
    stride=$(($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 更快两个代码有如下依赖:

  1. loop1 依赖关系
    x = a[0] -> x++ -> a[0] = x -> y = a[0] -> y++ -> a[0] = y
  2. 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;

    // loop1
    start=clock();
    for (i = 0; i < steps; i++) {
        a[0]++;
        barrier();
        a[0]++;
        barrier();
    }
    end =clock();
    printf("%lu\n", timediff(start,end));

    // loop2
    start=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 的影响

目前有三种映射缓存方式:

  1. 直接映射缓存

    每个内存块只能存储在缓存中的一个特定插槽中。一种简单的解决方案是将索引为 chunk_index 的块映射到缓存槽 (chunk_index % cache_slots)。映射到同一插槽的两个内存块不能同时存储在缓存中。

  2. N路组相联高速缓存

    每个内存块都可以存储在缓存中 N 个特定插槽中的任何一个中。例如,在 16 路缓存中,每个内存块可以存储在 16 个不同的缓存槽中。通常,具有相同最低位的索引的块将共享 16 个槽。

  3. 全关联缓存

    每个内存块都可以存储在缓存中的任何槽中。实际上,缓存的运行方式类似于哈希表。

直接映射缓存可能会发生冲突,当多个值竞争缓存中的同一个插槽时,它们会不断相互驱逐,命中率直线下降。

全关联高速缓存在硬件中实现起来既复杂又昂贵。

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);
    else
        update_counter(16);
	return NULL;
}

static void *thread_func2(void *param)
{
    if (aaa)
	    update_counter(1);
    else
        update_counter(32);
	return NULL;
}

static void *thread_func3(void *param)
{
    if (aaa)
	    update_counter(2);
    else
        update_counter(48);
	return NULL;
}

static void *thread_func4(void *param)
{
    if (aaa)
	    update_counter(3);
    else
        update_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,3

real    0m1.889s
user    0m6.994s
sys     0m0.000s
[root@node-3 test_cache]# time ./a.out  1	// 16,32,48,64

real    0m0.451s
user    0m1.439s
sys     0m0.000s

[root@node-3 test_cache]# time taskset -c 0 ./a.out

real    0m1.251s
user    0m1.240s
sys     0m0.000s
[root@node-3 test_cache]# time taskset -c 0 ./a.out  1

real    0m1.250s
user    0m1.239s
sys     0m0.000s

(1)Phytium S2500

# gcc -O0 -g example6.c -lpthread
[root@node-1 test_cache]# time ./a.out

real    0m0.467s
user    0m1.829s
sys     0m0.011s
[root@node-1 test_cache]# time ./a.out 1

real    0m0.432s
user    0m1.720s
sys     0m0.001s

[root@node-1 test_cache]# time taskset -c 0 ./a.out

real    0m1.724s
user    0m1.722s
sys     0m0.001s
[root@node-1 test_cache]# time taskset -c 0 ./a.out 1

real    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

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

  1. 全局测试
    在这里插入图片描述
  2. numa 绑定最近节点: numactl -C 24 -m 1 make cache-run distances 10
    在这里插入图片描述
  3. 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

  1. 读测试:
    在这里插入图片描述
  2. 写测试
    在这里插入图片描述
  3. 读-修改-写测试:
    在这里插入图片描述
  4. 测试报告: 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 FIFTY

static 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 byte
    size = memsize / stride;
    indices = malloc(size * sizeof(int));

    for (i = 0; i < size; i++)
        indices[i] = i;

    // trick 2: fill mem with pointer references
    for (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 -x

work=./a.out
buffer_size=1
stride=64

for i in `seq 1 15`; do
    taskset -ac 0 $work -b $buffer_size -s $stride
    buffer_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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/805172.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【EI/SCOPUS会议征稿】第四届机器学习与计算机应用国际学术会议(ICMLCA 2023)

ICMLCA 2023 第四届机器学习与计算机应用国际学术会议 2023 4th International Conference on Machine Learning and Computer Application 第四届机器学习与计算机应用国际学术会议(ICMLCA 2023)定于2023年10月27-29日在中国杭州隆重举行。本届会议将主要关注机器学习和计算…

rk3568 Debian11 如何打开热点

思路&#xff1a;1. 下载必要工具&#xff08;hostapt、dnsmasq&#xff09;2. 配置网络&#xff08;无线网卡配置静态IP&#xff09;3. 配置hostapt配置文件4. 配置DHCP服务5. 启动服务&#xff08;hostapd/dnsmasq/network&#xff09;6. IP转发&#xff08;这一步决定了是否…

【QT 网络云盘客户端】——登录界面功能的实现

目录 1.注册账号 2.服务器ip地址和端口号设置 3. 登录功能 4.读取配置文件 5.显示主界面 1.注册账号 1.点击注册页面&#xff0c;将数据 输入 到 用户名&#xff0c;昵称&#xff0c;密码&#xff0c;确认密码&#xff0c;手机&#xff0c;邮箱 的输入框中&#xff0c; 点…

Vue3 导出word

&#x1f642;博主&#xff1a;锅盖哒 &#x1f642;文章核心&#xff1a;导出word 目录 1.首先&#xff0c;你需要安装docxtemplater库。可以使用npm或yarn来安装&#xff1a; 2.在Vue组件中&#xff0c;你可以使用docxtemplater来生成Word文档并提供一个导出按钮供用户下载…

线性表之顺序表

在计算机科学中&#xff0c;数据结构是非常重要的基础知识之一。数据结构为我们提供了组织和管理数据的方法和技巧&#xff0c;使得我们可以高效地存储、检索和操作数据。而顺序表作为数据结构中最基本、最常用的一种存储结构&#xff0c;也是我们学习数据结构的第一步。 本文将…

idea 关于高亮显示与选中字符串相同的内容

dea 关于高亮显示与选中字符串相同的内容&#xff0c;本文作为个人备忘的同时也希望可以作为大家的参考。 依次修改File-settings-Editor-Color Scheme-General菜单下的Code-Identifier under caret和Identifier under caret(write)的Backgroud色值&#xff0c;可以参考下图。…

阿里云域名备案

最好的爱情&#xff0c;不是因为我们彼此需要在一起&#xff0c;而是因为我们彼此想要在一起。 阿里云的域名如何备案&#xff0c;域名备案和ICP备案一样吗&#xff1f;&#xff1f; 截至我所掌握的知识&#xff08;2021年9月&#xff09;&#xff0c;阿里云的域名备案和ICP备案…

【GoLang】基础语法(上)

Go基础语法(上) 文章目录 Go基础语法(上)01注释02变量定义初始化打印内存地址变量交换匿名变量变量的作用域 03常量iota 04基本数据类型布尔类型数字类型整型浮点型 字符与字符串 05数据类型转换06运算符算术运算符关系运算符逻辑运算符位运算符赋值运算符 07获取键盘输入 01注…

Java 设计模式 - 简单工厂模式 - 创建对象的简便之道

简单工厂模式是一种创建型设计模式&#xff0c;它提供了一种简单的方式来创建对象&#xff0c;而无需暴露对象创建的逻辑。在本篇博客中&#xff0c;我们将深入了解简单工厂模式的概念、实现方式以及如何在Java中使用它来创建对象。 为什么使用简单工厂模式&#xff1f; 在软…

PC音频框架学习

1.整体链路 下行播放&#xff1a; App下发音源→CPU Audio Engine 信号处理→DSP数字信号处理→Codec DAC→PA→SPK 上行录音&#xff1a; MIC拾音→集成运放→Codec ADC→DSP数字信号处理→CPU Audio Engine 信号处理→App 2.硬件 CPU PCH DSP(可选) Codec PA SPKbox MIC…

Vue 3 中的插槽(Slots)用法

插槽&#xff08;Slots&#xff09;是 Vue 组件中一种非常有用的功能&#xff0c;用于在父组件中向子组件传递内容。Vue 3 引入了 <script setup> 语法&#xff0c;使得组件的写法更加简洁和易读。在本篇博客中&#xff0c;我们将探讨在 Vue 3 中使用插槽的不同方式&…

数据结构与算法基础-学习-27-图之最短路径之Dijkstra(迪杰斯特拉)算法

一、最短路径应用案例 例如从北京到上海旅游&#xff0c;有多条路可以到目的地&#xff0c;哪条路线最短&#xff0c;哪条路线最省钱&#xff0c;就是典型的最短路径问题。 二、最短路径问题分类 最短路径问题可以分为两类&#xff0c;第一类为&#xff1a;两点间最短路径。第…

函数栈帧的创建和毁销【C语言版】

大家好&#xff0c;我是深鱼~ 【前言】前期学习的时候&#xff0c;我们可能有很多的困惑 比如&#xff1a; 局部变量是怎么创建的呢&#xff1f; 为什么局部变量的值是随机值&#xff1f; 函数是怎么传参的&#xff1f;传参的顺序是怎么样的&#xff1f; 形参和实参是什么关系…

上海亚商投顾:沪指放量大涨1.84% 证券股掀涨停潮

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 市场情绪 三大指数今日低开高走&#xff0c;沪指午后放量涨近2%&#xff0c;上证50盘中大涨超3%。大金融板块全线爆发&#…

Wireshark从下载到使用完整教程分享

surfshark如何下载呢&#xff1f;surfshark安卓&#xff0c;苹果以及电脑上使用的完整教程分享可以戳后面地址: https://qptool.net/shark.html Wireshark是一款流行的网络协议分析工具&#xff0c;用于捕获和分析网络数据包。它可以帮助网络管理员和安全专家监视和诊断网络问…

[每日习题]跳石板(动态规划) 手套(贪心)——牛客习题

hello,大家好&#xff0c;这里是bang___bang_&#xff0c;今天来记录2道习题跳石板和手套&#xff01; 目录 1️⃣跳石板 2️⃣手套 1️⃣跳石板 跳石板_牛客题霸_牛客网 (nowcoder.com) 描述 小易来到了一条石板路前&#xff0c;每块石板上从1挨着编号为&#xff1a;1、2、…

批量删除python代码中的注释

ctrlh&#xff0c;调出替换功能窗口 启用正则表达式&#xff0c;输入 (#.*) 点击替换就能删除全部的注释了

【图论】LCA(倍增)

一.LCA介绍 LCA通常指的是“最近共同祖先”&#xff08;Lowest Common Ancestor&#xff09;。LCA是一种用于解决树或图结构中两个节点的最低共同祖先的问题的算法。 在树结构中&#xff0c;LCA是指两个节点的最近层级的共同祖先节点。例如&#xff0c;考虑一棵树&#xff0c;…

16.Netty源码之ChannelPipeline

highlight: arduino-light 服务编排层:ChannelPipeline协调ChannelHandlerHandler EventLoop可以说是 Netty 的调度中心&#xff0c;负责监听多种事件类型&#xff1a;I/O 事件、信号事件、定时事件等&#xff0c;然而实际的业务处理逻辑则是由 ChannelPipeline 中所定义的 Cha…

《Elasticsearch 源码解析与优化实战》第5章:选主流程

《Elasticsearch 源码解析与优化实战》第5章&#xff1a;选主流程 - 墨天轮 一、简介 Discovery 模块负责发现集群中的节点&#xff0c;以及选择主节点。ES 支持多种不同 Discovery 类型选择&#xff0c;内置的实现称为Zen Discovery ,其他的包括公有云平台亚马逊的EC2、谷歌…