gpt4 book ai didi

performance - 是什么导致在 Cortex-A72 上使用 -O0 而不是 -O3 的简单紧密循环的周期变化如此之大?

转载 作者:行者123 更新时间:2023-12-04 02:37:32 24 4
gpt4 key购买 nike

我正在为一段代码获得高度一致的运行时进行一些实验。我目前正在计时的代码是一个相当随意的 CPU 绑定(bind)工作负载:

int cpu_workload_external_O3(){
int x = 0;
for(int ind = 0; ind < 12349560; ind++){
x = ((x ^ 0x123) + x * 3) % 123456;
}
return x;
}

我编写了一个禁用中断的内核模块,然后运行上述函数的 10 次试验,通过获取前后时钟周期计数器的差异来计时每次试验。其他需要注意的事项:
  • 机器是 ARM Cortex-A72,有 4 个插槽,每个插槽有 4 个内核(每个都有自己的 L1 缓存)
  • 时钟频率缩放关闭
  • 不支持超线程
  • 除了一些基本系统进程之外,机器几乎没有运行任何东西

  • 换句话说,我相信系统可变性的大多数/所有来源都被考虑在内,特别是当作为内核模块运行并通过 spin_lock_irqsave() 禁用中断时,代码应该在运行之间实现几乎相同的性能(可能是一个小的性能当某些指令第一次被拉入缓存时,第一次运行时命中,仅此而已)。

    事实上,当使用 -O3 编译基准代码时,我看到平均约 135,845,192 个循环中最多有 200 个循环,大多数试验花费的时间完全相同。然而,当使用 -O0 编译时,范围在 ~262,710,916 中高达 158,386 个周期。我所说的范围是指最长和最短运行时间之间的差异。此外,对于 -O0 代码,哪个试验是最慢/最快的并没有太大的一致性——违反直觉的是,在某些情况下,最快的是第一个,最慢的是紧随其后的!

    所以 :是什么导致了 -O0 代码中可变性的上限如此之高?查看程序集,似乎 -O3 代码将所有内容(?)存储在一个寄存器中,而 -O0 代码有一堆对 sp 的引用,因此它似乎正在访问内存。但即便如此,我仍希望所有内容都被带入 L1 缓存并以相当确定的访问时间坐在那里。

    代码

    被基准测试的代码在上面的代码片段中。组装如下。两者都是用 gcc 7.4.0 编译的,除了 -O0-O3 之外没有其他标志。
    -O0
    0000000000000000 <cpu_workload_external_O0>:
    0: d10043ff sub sp, sp, #0x10
    4: b9000bff str wzr, [sp, #8]
    8: b9000fff str wzr, [sp, #12]
    c: 14000018 b 6c <cpu_workload_external_O0+0x6c>
    10: b9400be1 ldr w1, [sp, #8]
    14: 52802460 mov w0, #0x123 // #291
    18: 4a000022 eor w2, w1, w0
    1c: b9400be1 ldr w1, [sp, #8]
    20: 2a0103e0 mov w0, w1
    24: 531f7800 lsl w0, w0, #1
    28: 0b010000 add w0, w0, w1
    2c: 0b000040 add w0, w2, w0
    30: 528aea61 mov w1, #0x5753 // #22355
    34: 72a10fc1 movk w1, #0x87e, lsl #16
    38: 9b217c01 smull x1, w0, w1
    3c: d360fc21 lsr x1, x1, #32
    40: 130c7c22 asr w2, w1, #12
    44: 131f7c01 asr w1, w0, #31
    48: 4b010042 sub w2, w2, w1
    4c: 529c4801 mov w1, #0xe240 // #57920
    50: 72a00021 movk w1, #0x1, lsl #16
    54: 1b017c41 mul w1, w2, w1
    58: 4b010000 sub w0, w0, w1
    5c: b9000be0 str w0, [sp, #8]
    60: b9400fe0 ldr w0, [sp, #12]
    64: 11000400 add w0, w0, #0x1
    68: b9000fe0 str w0, [sp, #12]
    6c: b9400fe1 ldr w1, [sp, #12]
    70: 528e0ee0 mov w0, #0x7077 // #28791
    74: 72a01780 movk w0, #0xbc, lsl #16
    78: 6b00003f cmp w1, w0
    7c: 54fffcad b.le 10 <cpu_workload_external_O0+0x10>
    80: b9400be0 ldr w0, [sp, #8]
    84: 910043ff add sp, sp, #0x10
    88: d65f03c0 ret
    -O3
    0000000000000000 <cpu_workload_external_O3>:
    0: 528e0f02 mov w2, #0x7078 // #28792
    4: 5292baa4 mov w4, #0x95d5 // #38357
    8: 529c4803 mov w3, #0xe240 // #57920
    c: 72a01782 movk w2, #0xbc, lsl #16
    10: 52800000 mov w0, #0x0 // #0
    14: 52802465 mov w5, #0x123 // #291
    18: 72a043e4 movk w4, #0x21f, lsl #16
    1c: 72a00023 movk w3, #0x1, lsl #16
    20: 4a050001 eor w1, w0, w5
    24: 0b000400 add w0, w0, w0, lsl #1
    28: 0b000021 add w1, w1, w0
    2c: 71000442 subs w2, w2, #0x1
    30: 53067c20 lsr w0, w1, #6
    34: 9ba47c00 umull x0, w0, w4
    38: d364fc00 lsr x0, x0, #36
    3c: 1b038400 msub w0, w0, w3, w1
    40: 54ffff01 b.ne 20 <cpu_workload_external_O3+0x20> // b.any
    44: d65f03c0 ret

    内核模块

    运行试验的代码如下。它在每次迭代之前/之后读取 PMCCNTR_EL0,将差异存储在一个数组中,并在最后打印出所有试验的最小/最大时间。函数 cpu_workload_external_O0cpu_workload_external_O3 位于外部对象文件中,它们分别编译,然后链接进来。

    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/kernel.h>

    #include "cpu.h"

    static DEFINE_SPINLOCK(lock);

    void runBenchmark(int (*benchmarkFunc)(void)){
    // Enable perf counters.
    u32 pmcr;
    asm volatile("mrs %0, pmcr_el0" : "=r" (pmcr));
    asm volatile("msr pmcr_el0, %0" : : "r" (pmcr|(1)));

    // Run trials, storing the time of each in `clockDiffs`.
    u32 result = 0;
    #define numtrials 10
    u32 clockDiffs[numtrials] = {0};
    u32 clockStart, clockEnd;
    for(int trial = 0; trial < numtrials; trial++){
    asm volatile("isb; mrs %0, PMCCNTR_EL0" : "=r" (clockStart));
    result += benchmarkFunc();
    asm volatile("isb; mrs %0, PMCCNTR_EL0" : "=r" (clockEnd));

    // Reset PMCCNTR_EL0.
    asm volatile("mrs %0, pmcr_el0" : "=r" (pmcr));
    asm volatile("msr pmcr_el0, %0" : : "r" (pmcr|(((uint32_t)1) << 2)));

    clockDiffs[trial] = clockEnd - clockStart;
    }

    // Compute the min and max times across all trials.
    u32 minTime = clockDiffs[0];
    u32 maxTime = clockDiffs[0];
    for(int ind = 1; ind < numtrials; ind++){
    u32 time = clockDiffs[ind];
    if(time < minTime){
    minTime = time;
    } else if(time > maxTime){
    maxTime = time;
    }
    }

    // Print the result so the benchmark function doesn't get optimized out.
    printk("result: %d\n", result);

    printk("diff: max %d - min %d = %d cycles\n", maxTime, minTime, maxTime - minTime);
    }

    int init_module(void) {
    printk("enter\n");
    unsigned long flags;
    spin_lock_irqsave(&lock, flags);

    printk("-O0\n");
    runBenchmark(cpu_workload_external_O0);

    printk("-O3\n");
    runBenchmark(cpu_workload_external_O3);

    spin_unlock_irqrestore(&lock, flags);
    return 0;
    }

    void cleanup_module(void) {
    printk("exit\n");
    }

    硬件
    $ lscpu
    Architecture: aarch64
    Byte Order: Little Endian
    CPU(s): 16
    On-line CPU(s) list: 0-15
    Thread(s) per core: 1
    Core(s) per socket: 4
    Socket(s): 4
    NUMA node(s): 1
    Vendor ID: ARM
    Model: 3
    Model name: Cortex-A72
    Stepping: r0p3
    BogoMIPS: 166.66
    L1d cache: 32K
    L1i cache: 48K
    L2 cache: 2048K
    NUMA node0 CPU(s): 0-15
    Flags: fp asimd evtstrm aes pmull sha1 sha2 crc32 cpuid
    $ lscpu --extended
    CPU NODE SOCKET CORE L1d:L1i:L2 ONLINE
    0 0 0 0 0:0:0 yes
    1 0 0 1 1:1:0 yes
    2 0 0 2 2:2:0 yes
    3 0 0 3 3:3:0 yes
    4 0 1 4 4:4:1 yes
    5 0 1 5 5:5:1 yes
    6 0 1 6 6:6:1 yes
    7 0 1 7 7:7:1 yes
    8 0 2 8 8:8:2 yes
    9 0 2 9 9:9:2 yes
    10 0 2 10 10:10:2 yes
    11 0 2 11 11:11:2 yes
    12 0 3 12 12:12:3 yes
    13 0 3 13 13:13:3 yes
    14 0 3 14 14:14:3 yes
    15 0 3 15 15:15:3 yes
    $ numactl --hardware
    available: 1 nodes (0)
    node 0 cpus: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
    node 0 size: 32159 MB
    node 0 free: 30661 MB
    node distances:
    node 0
    0: 10

    sample 测量

    下面是内核模块一次执行的一些输出:
    [902574.112692] kernel-module: running on cpu 15                                                                                                                                      
    [902576.403537] kernel-module: trial 00: 309983568 74097394 98796602 <-- max
    [902576.403539] kernel-module: trial 01: 309983562 74097397 98796597
    [902576.403540] kernel-module: trial 02: 309983562 74097397 98796597
    [902576.403541] kernel-module: trial 03: 309983562 74097397 98796597
    [902576.403543] kernel-module: trial 04: 309983562 74097397 98796597
    [902576.403544] kernel-module: trial 05: 309983562 74097397 98796597
    [902576.403545] kernel-module: trial 06: 309983562 74097397 98796597
    [902576.403547] kernel-module: trial 07: 309983562 74097397 98796597
    [902576.403548] kernel-module: trial 08: 309983562 74097397 98796597
    [902576.403550] kernel-module: trial 09: 309983562 74097397 98796597
    [902576.403551] kernel-module: trial 10: 309983562 74097397 98796597
    [902576.403552] kernel-module: trial 11: 309983562 74097397 98796597
    [902576.403554] kernel-module: trial 12: 309983562 74097397 98796597
    [902576.403555] kernel-module: trial 13: 309849076 74097403 98796630 <-- min
    [902576.403557] kernel-module: trial 14: 309983562 74097397 98796597
    [902576.403558] kernel-module: min time: 309849076
    [902576.403559] kernel-module: max time: 309983568
    [902576.403560] kernel-module: diff: 134492

    对于每个试验,报告的值是:循环数 (0x11)、L1D 访问数 (0x04)、L1I 访问数 (0x14)。我正在使用 this ARM PMU reference 的第 11.8 节)。

    最佳答案

    在最近的 Linux 内核中,自动 NUMA 页面迁移机制会定期删除 TLB 条目,以便它可以监视 NUMA 位置。即使数据保留在 L1DCache 中,TLB 重新加载也会减慢 O0 代码的速度。

    不应在内核页面上激活页面迁移机制。

    您检查是否启用了自动 NUMA 页面迁移

    $ cat /proc/sys/kernel/numa_balancing

    你可以禁用它
    $ echo 0 > /proc/sys/kernel/numa_balancing

    关于performance - 是什么导致在 Cortex-A72 上使用 -O0 而不是 -O3 的简单紧密循环的周期变化如此之大?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59349267/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com