C+的巨大性能差异+;Mac和Linux下的程序(用GCC编译) 最近我在C++中编写了一个小程序(很诚实,它是C类的),并在MAC和Linux机器上测试了性能。

C+的巨大性能差异+;Mac和Linux下的程序(用GCC编译) 最近我在C++中编写了一个小程序(很诚实,它是C类的),并在MAC和Linux机器上测试了性能。,c++,performance,macos,gcc,operating-system,C++,Performance,Macos,Gcc,Operating System,即使硬件是可比的,性能是如此不同,我真的有一些奇怪的事情发生 首先是一些细节: 输入:约200MB压缩数据 程序的操作:它解压缩数据,然后将其加载到内存中,并执行许多数据访问以执行数据之间的连接。程序是连续的(没有额外的线程或进程) 输出:屏幕上显示的一些字符串 该代码在Linux机器上使用GCC4.8.1编译,在Mac机器上使用GCC4.8.2编译。在这两种情况下,都会使用以下参数调用编译器: gcc -c -O3 -fPIC -MD -MF $(patsubst %.o,%.d,$@) //

即使硬件是可比的,性能是如此不同,我真的有一些奇怪的事情发生

首先是一些细节:

输入:约200MB压缩数据

程序的操作:它解压缩数据,然后将其加载到内存中,并执行许多数据访问以执行数据之间的连接。程序是连续的(没有额外的线程或进程)

输出:屏幕上显示的一些字符串

该代码在Linux机器上使用GCC4.8.1编译,在Mac机器上使用GCC4.8.2编译。在这两种情况下,都会使用以下参数调用编译器:

gcc -c -O3 -fPIC -MD -MF $(patsubst %.o,%.d,$@) //The last three arguments are to create the dependencies between the files
Mac(OS=Mac mavericks 10.9)是一款macbook pro,配备2,3 GHz Intel core I7(四核)256KB二级缓存、6MB三级缓存、8GB DDR3 1600Mhz和256 GB SSD磁盘

Linux机器(内核2.6.32-358)具有Intel E5-2620 2.0 GHz(六核)16MB缓存、64GB DDR3 1600Mhz和256 GB SSD磁盘。这两台机器都应该使用Sandy Bridge架构(也许Mac是ivy Bridge,但无论如何这不会有太大区别)

现在,如果我在linux机器上启动程序,那么需要217毫秒才能完成,而如果我在Mac机器上启动它,则需要132毫秒:这使linux代码的速度慢了1.6倍

现在,我知道这两台机器有不同的操作系统和硬件,但我发现这样的减速太大了,不能用这些因素来解释,我觉得这背后一定有其他原因

请注意,此计时是在所有数据加载到内存后进行的,我确信在此期间程序不会交换到磁盘。因此,我可以排除SSD磁盘的问题

现在,我真的不知道是什么导致了这种减速?内存基本上是等效的,而CPU只是稍微慢一点

难道GCC在linux上产生的代码比mac上产生的代码更糟糕吗

难道Linux操作系统比Mac更糟糕吗

这两件事我都很难相信。有什么帮助吗

编辑:

我意识到我没有提到我是如何计时的:我使用boost chrono库,只测量调用主函数所需的时间。比如:

time = now();
function();
duration = now() - time;
print(duration);
EDIT2: 经过一些测试,我们用一个更简单(而且愚蠢)的程序再现了性能的差异:

在linux机器上,它是:

Joins 10000000 Cycles total 838198832
显然,linux版本需要更多的CPU周期,这可能是访问内存所需要的。现在的问题是:为什么内存访问速度较慢

一个原因可能是in1和in2不适合CPU缓存,这需要一些RAM访问。正如Roy Longboattom所指出的,linux中的内存实际上是ECC,这可能是性能降低的原因。如果我们将这一点与CPU速度稍低(sandy和ivy bridge之间的差异)结合起来,那么我们可能对这种差异有一个很好的解释


无论如何,谢谢大家的提示

从另一个角度看,运行时的差异只有85毫秒,这很小

你到底在测量什么?如果是整个程序运行时,包括启动和拆卸(例如,使用Unix
time
命令),那么差异可能很容易归因于所涉及的动态链接器:至少在Linux上,您的程序将在实际执行之前链接到系统
libstdc++
。如果MacOS动态链接器的速度稍微快一点(或者程序在Mac上静态链接?),这很容易解释差异


或者甚至可能是写入终端所需的时间。例如,在Linux上,
gnome终端
由于使用抗锯齿字体和完全的Unicode支持,通常被视为“缓慢”。如果改用
xterm
,程序是否运行得更快?如果将输出重定向到
/dev/null
,会发生什么情况?

两个系统都遵循System V AMD64 ABI,因此gcc不应该在这方面产生影响。不幸的是,如今系统性能中的随机效应相当普遍,因此有时通过重新排序链接顺序这样愚蠢的事情,您可能会获得显著的性能差异(参见Mytkowicz等人,“在不做任何明显错误的事情的情况下生成错误数据”。)

以下是一些关于如何分析这一点的建议:

  • 多跑一次。就我个人而言,我至少进行了11次跑步,并比较了中位数(以及各种四分位数,但这可能比你所关心的要多)。这避免了一些随机效应
  • 将所有输出导入一个文件,以最小化UI效果
  • 检查您的性能计数器。在Linux上,您可以使用
    perf'工具。检查是否存在
    major faults',这表明您存在需要转到磁盘的页面错误(当然,在多次运行时不太可能)。只有这样,您才能排除磁盘在这方面的影响。不幸的是,据我所知,OSX没有那么简单的方法来收集性能计数器
  • 您可以尝试使用“-mcpu”强制执行相同的目标指令集
  • 比较实际缓存大小`dmidecode-tcache'在Linux端执行此操作,但您必须是root用户。您的机器可能存在相关差异
  • 如果您的程序运行于多个阶段,请尝试分别对其进行基准测试

  • 祝你好运

    实际上,如果您考虑到不同的频率(如果您的程序是CPU限制的而不是内存限制的,这可能很关键,您没有告诉我们您的代码是做什么的),那么差异将减少到~1.43

    但是,如果其中一个CPU是IvyBridge
    Joins 10000000 Cycles total 589015641
    
    Joins 10000000 Cycles total 838198832
    
        for(j = 0; j < 10000000; ++j) {
                int el = in1[j];
                for(m = 0; m < 10000000; m++) {
                        count = count + 1;
                        if (in2[m] == el) 
                        {
                                joins++;
                                break;
                        }
                }
        }
    
      .L6:
            movzbl  in1(%ecx), %edx
            xorl    %eax, %eax
            jmp     .L5
            .p2align 4,,7
            .p2align 3
      .L3:
            addl    $1, %eax
            cmpl    $10000000, %eax
            je      .L4
      .L5:
            cmpb    in2(%eax), %dl
            fadd    %st, %st(1)
            jne     .L3
            addl    $1, %ebx
      .L4:
            addl    $1, %ecx
            cmpl    $10000000, %ecx
            jne     .L6
    
     Result 2400 MHz
     Count  320000000  Joins 10000000  0.4920310 seconds  20.32M  joins per second
     Result 1600 MHz
     Count  320000000  Joins 10000000  0.7400470 seconds  13.51M  joins per second