【问题标题】:Processor behavior explanation处理器行为说明
【发布时间】:2018-12-04 21:26:22
【问题描述】:

在询问this question 之后,我很困惑,决定为 C 编译器程序构建类似的测试。这是我的代码:

#include <stdio.h>
#include <time.h>
#include <math.h>
#include <stdlib.h>

#define SUMMATIONS 20000000

int main() {

    static int speedups[2101] = { 0 };

    srand((unsigned)time(NULL));

    while (1) {

        unsigned int t1, t2, t3, t4;
        signed int tmp, i, n1, n2;

        // Slow version
        t1 = clock();
        for (n1 = rand() % 50, i = 0; i < SUMMATIONS; i++) {
            n1 += 3 * i * i;
        }
        t2 = clock();

        // Optimized version
        t3 = clock();
        for (n2 = rand() % 50, i = 0; i < SUMMATIONS; i++) {
            n2 += i * i;
        }
        n2 *= 3;
        t4 = clock();

        // gather speedup statistics
        if ((int)(t2 - t1) != 0) {
            tmp = (int)(100.0f * ((float)(t2 - t1) - (float)(t4 - t3)) / (float)(t2 - t1));
            tmp = tmp < -100 ? -100 : tmp > 100 ? 100 : tmp;
            tmp = (tmp >= 0 ? 1000 : 2000) + abs(tmp);
            speedups[tmp]++;
        }

        // output statistics
        for (i = 0; i < 2101; i++) {
            if (speedups[i] != 0) {
                char s = i / 1000 == 1 ? '+' : i / 1000 == 2 ? '-' : '?';
                printf("%c%i : %i\n", s, i % 1000, speedups[i]);
            }
        }
        printf("error %i ******************\n", abs(n2-n1));

    }

    return 0;
}

在 GCC 下编译,带有选项 -O3 -march=native

编辑

测试代码已更改,因此只能在运行时(而不是在编译时)知道错误值,因此 GCC 优化器无法删除 for 循环的代码。

结果

运行时 - 将 CPU 命中计数器重新计算为特定加速值并输出计数器表。如果我们绘制 CPU hits Vs Speedup 值,我们将 得到这样的图表:

所以 GCC 制作的程序平均产生了 ~ 20% 的加速。

问题

我们应该期望 CPU 加速吗? (根据GCC编译程序的预测)

【问题讨论】:

  • 目前还不清楚所有这些代码的作用,以及它与您引用的输出的关系。你能补充一些解释吗?
  • 有趣的事实:gcc -O3 愉快地生成了以下序列(由于此评论中缺少格式而进行了少量编辑)作为翻译的一部分:call clock; movq %rax, %r14; call clock; movq %rax, %rbx; call clock; movq %rax, %r13; call clock 我邀请您思考时间的意义有多大有。
  • 测量慢的和优化的代码块的执行速度。计算“快速”块的相对加速并增加数组中该加速值的计数器,以查看有多少处理器达到该加速值。以“百分比加速:命中量”格式打印数据
  • 如果您使用godbolt.org 分析生成的汇编代码,您会发现带有给定命令参数的clang 能够优化掉完整的循环(即使它们为空)。修改您的代码以禁用优化器以丢弃计算结果。
  • @AgniusVasiliauskas 不,我的建议是对循环中聚合的变量做一些事情。在您的代码中,这些变量不再使用。因此,优化器很乐意将它们扔掉。因此,“禁用”我的意思是“阻止”而不是关闭。

标签: c optimization cpu


【解决方案1】:

我们应该期待 CPU 的加速吗?

没有。通过选择使用高级语言,您选择放弃期望与性能相关的任何事情的权利。

您可以假设(但不期望)第一个版本在循环内有一个额外的乘法(额外的3*),因此可能会有与该乘法相关的额外成本。

您还可以假设(但不期望)编译器可能会将两个版本都优化为一个常量,并且可能会在运行时生成 printf("error %i ******************\n", CONSTANT_CALCULATED_AT_COMPILE_TIME); 的等价物,而无需任何代码来计算 n1n2

请注意,这些随机假设是相互排斥的。

【讨论】:

  • 你的第一段有问题;人们确实在使用 C(甚至 C++)时期望性能。
  • @M.M:当然,人们总是期待他们无权期待的东西。然后他们创建假设,并进行测量,以查看在为一个特定 CPU 生成代码时,对于一个特定编译器的一个特定版本和一个特定的命令行选项/优化的假设有多错误。
  • 这个人在做一个糟糕的基准测试并不意味着写得好的 C 程序就不能期望性能
  • @M.M:我认为您将“可以”与“应该”混淆了。例如。 “人们可以有期望,即使他们无权期待任何东西,即使他们不应该期待任何东西,因为他们无权期待任何东西”。
  • @M.M 如果你能写出你的非蹩脚基准作为答案,我会更多高兴。非常欢迎你
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多