为啥 gcc 的输出比 Visual Studio 慢得多(对于此代码)?

Posted

技术标签:

【中文标题】为啥 gcc 的输出比 Visual Studio 慢得多(对于此代码)?【英文标题】:Why gcc's output much slower than Visual Studio's (for this code)?为什么 gcc 的输出比 Visual Studio 慢得多(对于此代码)? 【发布时间】:2017-08-05 20:09:19 【问题描述】:

当我使用 gcc 在定期更新的 Ubuntu 16.04 64bit 上编译以下代码时

gcc source.c -O3 --fast-math

可执行文件需要大约 45 秒的 CPU 时间才能运行。但是在同一台机器上,在 Windows 7 64bit 中,使用 Visual Studio 2012 发布模式,运行 CPU 时间不到 10 秒。造成这种差异的主要原因是什么?我没有使用足够多的 gcc 优化选项吗? Visual Studio 的编译器是否更好?还是别的什么?

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

#define Nx 1000

int main()

    double d = 0.015e-2;        // meter
    double V0 = 400;            // volt
    double De = 1800e-4;        // m^2 per sec
    double mu_e = 2.9e1 / 760;  // m^2 per volt sec
    double n0 = 1e19;           // per m^3
    double e_eps = 1.602e-19 / 8.854e-12;
    double ne[Nx], je[Nx], E[Nx];
    double dx = d / (Nx - 1);
    double dt = 1e-14;          // s
    const int Nt = 500000;
    int i, k;
    double sum;
    FILE *fp_ne, *fp_E;
    double alpha, exp_alpha, R;
    int ESign = -1;
    clock_t start_t, end_t;

    start_t = clock();
    // initialization
    for (i = 1; i < Nx; i++)
        ne[i] = n0;
    ne[0] = 1e-4 * n0;

    for (i = 0; i < Nx; i++)
        E[i] = -V0 / d;

    // time loop
    for (k = 0; k < Nt; k++)
    
        if (k%1000==0) printf("k = %d\n", k);
        for (i = 0; i < (Nx-1); i++)
        
            alpha = mu_e*dx*E[i]/De;
            exp_alpha = exp(alpha);
            R = (exp_alpha-1)/alpha;
            je[i] = (De/(dx*R))*(ne[i]-exp_alpha*ne[i+1]);
        

        for (i = 1; i < (Nx - 1); i++)
            ne[i] += -dt/dx*(je[i] - je[i-1]);
        ne[Nx - 1] = ne[Nx - 2];

        sum = 0;
        for (i = 0; i < (Nx - 1); i++)
            sum += dx*je[i];
        for (i = 0; i < (Nx - 1); i++)
        
            E[i] += -dt*e_eps*(sum / d - je[i]);
            if (E[i]>=0) ESign=+1;
        
        if (ESign==1) break;
    

    // output
    printf("time=%e\n",k*dt);
    fp_ne = fopen("ne.txt", "w");
    fp_E = fopen("E.txt", "w");
    fprintf(fp_ne, "# x (cm)\tne(per cm^3)\n");
    fprintf(fp_E,  "# x (cm)\tE(V/cm)\n");
    for (i = 0; i < Nx; i++)
        fprintf(fp_ne, "%f\t%e\n", i*dx*100,ne[i]/1e6);
    for (i = 0; i < Nx-1; i++)
        fprintf(fp_E, "%f\t%e\n", i*dx*100, fabs(E[i])/1e2);
    fclose(fp_ne);
    fclose(fp_E);
    end_t = clock();
    printf("CPU time = %f\n", (double)(end_t - start_t) / CLOCKS_PER_SEC);

【问题讨论】:

对两者进行概要分析并进行比较?我不知道这段代码做了什么,也没有答案。它有“米”、“伏特”和电荷,所以它是一些物理、电磁学的东西,我不想把它拆开来找出答案。你希望我们用它做什么,究竟是什么? 哦 - 它读取包含一些内容的数据文件,同样,我们应该如何处理这个?应用 I/O 是否绑定?文件有多大?你知道所有这些东西,所以你应该做[whatever] 比较生成的代码(仅限数学部分)I/O 和其他库函数可以完全不同。 最大的耗时是输出到终端(在 Ubuntu linux 16.04 中)对我来说,在我的机器上,21+ 秒输出所有数据 您是直接在机器/计算机上运行每个操作系统还是其中一个在 VM 中运行? 【参考方案1】:

我做的第一件事就是注释掉循环 I/O。

//if (k%1000==0) printf("k = %d\n", k);

我得到了下面的时间,只有那个变化。最后的fprintf 调用确实会显着影响时间,但不会影响它们的相对差异,所以我不会再次测量所有这些。

我在我的 Arch Linux 第一代 Core i5 上得到了这些时间(全部使用标准 -O2 编译):

GCC 7.1:

CPU time = 23.459520

Clang 4.0.1:

CPU time = 22.936315

英特尔 17.0.4:

CPU time = 7.830828

在同一台机器上的 Windows 10 的 Qemu/libvirt 虚拟机上,我得到了这些时间:

MinGW-w64 GCC 6.3:

CPU time = 76.122000

VS 2015.3:

CPU time = 13.497000

对比 2017:

CPU time = 49.306000

在 WINE 上(本机 Linux,但 Win32 API 仿真,仍应与本机 Linux 代码执行相当)

MinGW-w64 GCC 6.3:

CPU time = 56.074000

VS 2015.3:

CPU time = 12.048000

对比 2017:

CPU time = 34.541000

长话短说:似乎这些为这个特定问题输出了最好的代码:

    Linux 上的 Intel(可能也在 Windows 上) VS 2015.3 Linux 上的 GCC/Clang VS 2017 MinGW-w64 GCC。

查看程序集将是了解这一点的唯一方法,但正确分析这超出了我的范围。

【讨论】:

比较 32 位 mingw-w64 和 64 位 谢谢。 time=5.000000e-09 与执行时间无关。所以你可以省略它。 @M.M.在这个问题的背景下,我看不出这有什么意义。 @rubenvb 我认为这将是一个有用的数据点,例如,如果结果证明 32 位很慢而 64 位匹配更快的编译器 @rubenvb 我看不出这在这个问题的上下文中有何意义。 32-bit x86 has 8 32-bit general purpose registers. 64-bit x86 has 16 64-bit general-purpose registers. 将可用寄存器的数量加倍,同时将可用寄存器的数量加倍每个寄存器可以保存的数据肯定会对性能产生巨大影响。

以上是关于为啥 gcc 的输出比 Visual Studio 慢得多(对于此代码)?的主要内容,如果未能解决你的问题,请参考以下文章

为啥使用 Visual Studio 而不是 GCC 编译时没有错误?

为啥 cygwin 使用 Visual Studio BuildTools 而不是 gcc?

使用 Visual Studio 9 构建的命令行应用程序不会将 *.NEF 作为输入,而使用 gcc 构建的相同应用程序会 - 为啥?

为啥 clang 生成的二进制文件比 Visual Studio 生成的大

为啥 Visual Studio Community 2015 RC 只有4G?比VS2013小这么多。

visual studio code终端输出为啥会乱码