gpt4 book ai didi

x86 - 与 CPU 微架构相关的奇怪现象

转载 作者:行者123 更新时间:2023-12-01 00:18:43 25 4
gpt4 key购买 nike

我正在测试通过指针测量一个函数调用的成本,这是我的 code .但是,我发现了一些非常奇怪的事情并寻求您的帮助。

代码在VS2017的Release模式下编译,使用默认配置。

有 4 个测试平台,它们的所有操作系统都是 Win10。以下是一些详细信息:

  • M1:CPU:i7-7700,微架构:Kaby Lake
  • M2:CPU:i7-7700,微架构:Kaby Lake
  • M3:CPU:i7-4790,微架构:Haswell
  • M4:CPU:E5-2698 v3,微架构:Haswell

  • 在下图中,图例的格式为 machine parameter_order alias . machine上面列出了。 parameter_order描述 LOOP 的顺序在单次运行期间传递给程序。 alias指示时间是哪个部分。 no-exec也就是没有函数调用部分。第 98-108 行。 exec表示调用函数部分,又名。第 115-125 行。 per-exec是函数调用的成本。所有时间单位都是毫秒。 per-exec指左 y 轴,而其他指右 y 轴。

    比较图 1-图 4,您可以看到该图可能与 CPU 的微架构有关(M1 和 M2 相似,M3 和 M4 相似)。

    我的问题 :
  • 为什么所有机器都有两个阶段( LOOP < 25LOOP > 100 )?
  • 为什么 32 <= LOOP <= 41 时所有的非执行时间都有一个奇怪的峰值?
  • 为什么在 72 <= LOOP <= 94 时 Kaby Lake 机器(M1 和 M2)的 no-exec 时间和 exec 时间有不连续的间隔?
  • 为什么 M4(服务器处理器)与 M3(桌面处理器)相比差异更大?


  • 这是我的测试结果:

    Fig. 1

    Fig. 2

    Fig. 3

    Fig. 4

    为方便起见,我还在这里粘贴代码:
    #include <cstdio>
    #include <cstdlib>
    #include <ctime>
    #include <cassert>
    #include <algorithm>

    #include <windows.h>

    using namespace std;

    const int PMAX = 11000000, ITER = 60000, RULE = 10000;
    //const int LOOP = 10;

    int func1(int a, int b, int c, int d, int e)
    {
    return 0;
    }

    int func2(int a, int b, int c, int d, int e)
    {
    return 0;
    }

    int func3(int a, int b, int c, int d, int e)
    {
    return 0;
    }

    int func4(int a, int b, int c, int d, int e)
    {
    return 0;
    }

    int func5(int a, int b, int c, int d, int e)
    {
    return 0;
    }

    int func6(int a, int b, int c, int d, int e)
    {
    return 0;
    }

    int (*init[6])(int, int, int, int, int) = {
    func1,
    func2,
    func3,
    func4,
    func5,
    func6
    };
    int (*pool[PMAX])(int, int, int, int, int);

    LARGE_INTEGER freq;

    void getTime(LARGE_INTEGER *res)
    {
    QueryPerformanceCounter(res);
    }

    double delta(LARGE_INTEGER begin_time, LARGE_INTEGER end_time)
    {
    return (end_time.QuadPart - begin_time.QuadPart) * 1000.0 / freq.QuadPart;
    }

    int main()
    {
    char path[100], tmp[100];

    FILE *fin, *fout;

    int cnt = 0;

    int i, j, t, r;
    int ans;

    int LOOP;

    LARGE_INTEGER begin_time, end_time;
    double d1, d2, res;

    for(i = 0;i < PMAX;i += 1)
    pool[i] = init[i % 6];

    QueryPerformanceFrequency(&freq);

    printf("file path:");
    scanf("%s", path);

    fin = fopen(path, "r");

    start:
    if (fscanf(fin, "%d", &LOOP) == EOF)
    goto end;

    ans = 0;
    getTime(&begin_time);
    for(r = 0;r < RULE;r += 1)
    {
    for(t = 0;t < ITER;t += 1)
    {
    //ans ^= (pool[t])(0, 0, 0, 0, 0);
    ans ^= pool[0](0, 0, 0, 0, 0);
    ans = 0;
    for(j = 0;j < LOOP;j += 1)
    ans ^= j;
    }
    }
    getTime(&end_time);
    printf("%.10f\n", d1 = delta(begin_time, end_time));
    printf("ans:%d\n", ans);

    ans = 0;
    getTime(&begin_time);
    for(r = 0;r < RULE;r += 1)
    {
    for(t = 0;t < ITER;t += 1)
    {
    ans ^= (pool[t])(0, 0, 0, 0, 0);
    ans ^= pool[0](0, 0, 0, 0, 0);
    ans = 0;
    for(j = 0;j < LOOP;j += 1)
    ans ^= j;
    }
    }
    getTime(&end_time);
    printf("%.10f\n", d2 = delta(begin_time, end_time));
    printf("ans:%d\n", ans);

    printf("%.10f\n", res = (d2 - d1) / (1.0 * RULE * ITER));

    sprintf(tmp, "%d.txt", cnt++);

    fout = fopen(tmp, "w");
    fprintf(fout, "%d,%.10f,%.10f,%.10f\n", LOOP, d1, d2, res);
    fclose(fout);

    goto start;
    end:

    fclose(fin);

    system("pause");

    exit(0);
    }

    最佳答案

    Why do all machines have two stage (LOOP < 25 and LOOP > 100)?



    第一个不连续发生在最内层循环 for(j = 0;j < LOOP;j += 1) 时。 ,停止正确预测其退出。在我的机器上它发生在 LOOP达到 24 次迭代。

    你可以用 perf stat -I3000 看得很清楚。将基准输出与性能统计信息交错:
    BenchWithFixture/RandomTarget/21     727779 ns     727224 ns       3851   78.6834M items/s
    45.003283831 2998.636997 task-clock (msec)
    45.003283831 118 context-switches # 0.039 K/sec
    45.003283831 0 cpu-migrations # 0.000 K/sec
    45.003283831 0 page-faults # 0.000 K/sec
    45.003283831 7,777,209,518 cycles # 2.595 GHz
    45.003283831 26,846,680,371 instructions # 3.45 insn per cycle
    45.003283831 6,711,087,432 branches # 2238.882 M/sec
    45.003283831 1,962,643 branch-misses # 0.03% of all branches
    BenchWithFixture/RandomTarget/22 751421 ns 750758 ns 3731 76.2169M items/s
    48.003487573 2998.943341 task-clock (msec)
    48.003487573 111 context-switches # 0.037 K/sec
    48.003487573 0 cpu-migrations # 0.000 K/sec
    48.003487573 0 page-faults # 0.000 K/sec
    48.003487573 7,778,285,186 cycles # 2.595 GHz
    48.003487573 26,956,175,646 instructions # 3.47 insn per cycle
    48.003487573 6,738,461,171 branches # 2247.947 M/sec
    48.003487573 1,973,024 branch-misses # 0.03% of all branches
    BenchWithFixture/RandomTarget/23 774490 ns 773955 ns 3620 73.9325M items/s
    51.003697814 2999.024360 task-clock (msec)
    51.003697814 105 context-switches # 0.035 K/sec
    51.003697814 0 cpu-migrations # 0.000 K/sec
    51.003697814 0 page-faults # 0.000 K/sec
    51.003697814 7,778,570,598 cycles # 2.595 GHz
    51.003697814 21,547,027,451 instructions # 2.77 insn per cycle
    51.003697814 5,386,175,806 branches # 1796.776 M/sec
    51.003697814 72,207,066 branch-misses # 1.12% of all branches
    BenchWithFixture/RandomTarget/24 1138919 ns 1138088 ns 2461 50.2777M items/s
    57.004129981 2999.003582 task-clock (msec)
    57.004129981 108 context-switches # 0.036 K/sec
    57.004129981 0 cpu-migrations # 0.000 K/sec
    57.004129981 0 page-faults # 0.000 K/sec
    57.004129981 7,778,509,575 cycles # 2.595 GHz
    57.004129981 19,061,717,197 instructions # 2.45 insn per cycle
    57.004129981 4,765,017,648 branches # 1589.492 M/sec
    57.004129981 103,398,285 branch-misses # 1.65% of all branches
    BenchWithFixture/RandomTarget/25 1171572 ns 1170748 ns 2391 48.8751M items/s
    60.004325775 2998.547350 task-clock (msec)
    60.004325775 111 context-switches # 0.037 K/sec
    60.004325775 0 cpu-migrations # 0.000 K/sec
    60.004325775 0 page-faults # 0.000 K/sec
    60.004325775 7,777,298,382 cycles # 2.594 GHz
    60.004325775 17,008,954,992 instructions # 2.19 insn per cycle
    60.004325775 4,251,656,734 branches # 1418.230 M/sec
    60.004325775 131,311,948 branch-misses # 2.13% of all branches

    过渡前,分支误判率约为 0.03%,然后在基准放缓时跃升至 2.13% 左右,或增加两个数量级。错误预测率实际上比您预期的要低一点:有 25 个分支(外循环还有几个),您预期在 1 / 25 == 4% 左右。错误预测,但我们没有看到,不知道为什么。

    在我的机器上,第一个循环(只有 pool[0](0,0,0,0,0) 调用)和你的一样,在 ~24 LOOP 处没有转换。迭代,但为什么我不清楚。我的经验是,TAGE 计数器通常无法处理超过 24 个周期的恒定迭代循环,但这里可能与间接分支预测器有一些交互。这很有趣。

    Why do all no-exec time have a weird peak when 32 <= LOOP <= 41?



    我在本地也经历过。在我的测试中,这也是由于分支错误预测造成的:当时间激增时,错误预测也随之增加。同样,这里的预测如何工作(如此好)尚不清楚,但显然在这些值下,算法会出现预测失败。

    Why do no-exec time and exec time of Kaby Lake machines (M1 and M2) have a discontinuous interval when 72 <= LOOP <= 94?



    我经历了同样的事情:迭代 72 以 28M 循环/秒的速度运行,而迭代 73 仅以 20M 运行(随后的迭代也很慢)。同样,差异可以放在分支错误预测的脚下:从第 72 次迭代到第 73 次迭代,它们从 0.01% 增加到 1.35%。这几乎正是每次执行外部循环时发生的一次错误预测,因此很可能是退出时的错误预测。

    Why does M4 (Server processor) have a larger variance compared to M3 (Desktop processor)?



    你的测试很长,所以会有很多变化来体验各种差异来源,比如中断、上下文切换、核心频率变化等等。

    由于这是一个完全不同的硬件配置,也许是软件配置,所以看到不相等的差异也就不足为奇了。您可以减少外部迭代的数量以保持基准更短,并查看异常值的数量是否减少(但它们的大小会增加)。您还可以在外循环内移动计时,因此您正在计时一个较小的部分,并查看直方图以了解各种系统的结果如何分布在这个较小的时间间隔内。

    要更深入地了解方差来源以及如何诊断它们, have a look at this answer .

    关于x86 - 与 CPU 微架构相关的奇怪现象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50492071/

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