【发布时间】:2013-12-09 20:53:07
【问题描述】:
最近我用 C++ 编写了一个小程序(好吧,说实话,它更多的是 C 加类),并在 Mac 和 Linux 机器上测试了性能。
尽管硬件相当,但性能与我的实际差别如此之大,发生了一些奇怪的事情。
首先是一些细节:
输入:约200MB压缩数据
程序的操作:对数据进行解压,然后加载到内存中,进行多次数据访问,实现数据之间的连接。该程序是顺序的(没有额外的线程或进程)。
输出:一些要显示在屏幕上的字符串
代码在 Linux 机器上使用 GCC 4.8.1 编译,在 Mac 机器上使用 GCC 4.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)机器是配备 2.3 GHz Intel core I7(四核)256KB L2 缓存、6MB L3 缓存、8GB DDR3 1600Mhz 和 256 GB SSD 的 macbook pro磁盘。
Linux 机器(内核 2.6.32-358)具有 Intel E5-2620 2.0 GHz(六核)16MB 缓存、64GB DDR3 1600Mhz 和 256GB SSD 磁盘。两台机器都应该使用 Sandy Bridge 架构(也许 Mac 是 ivy bridge,但无论如何这应该不会有太大区别)。
现在,如果我在 linux 机器上启动程序,则需要 217 毫秒才能完成,而如果我在 Mac 机器上启动它需要 132 毫秒:这会使 linux 代码慢 1.6 倍!
现在,我了解到两台机器的操作系统和硬件不同,但我发现这样的减速太大了,这些因素无法证明是合理的,我觉得这背后一定有其他原因。
请注意,此时间是在所有数据加载到内存后进行的,我确信程序在此期间不会交换到磁盘。因此,我可以排除SSD磁盘的问题。
现在,我真的不知道是什么导致了这种放缓?内存基本等价,CPU只是慢了一点。
难道 GCC 在 linux 上生成的代码比在 mac 上更糟糕吗?
会不会是 Linux 操作系统明显比 Mac 差?
我觉得这两件事都难以置信。有什么帮助吗?
编辑:
我意识到我没有提到我是如何进行计时的:嗯,我使用 boost chrono 库,并且我只测量调用 main 函数所需的时间。比如:
time = now();
function();
duration = now() - time;
print(duration);
EDIT2: 经过一些测试,我们设法用一个更简单(也很愚蠢)的程序重现了性能差异:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
char in1[10000000];
char in2[10000000];
static inline uint64_t rdtscp (void) {
uint64_t low, high;
uint64_t aux;
__asm__ __volatile__ (
".byte 0x0f,0x01,0xf9"
: "=a" (low), "=d" (high), "=c" (aux)
);
return low | (high << 32);
}
int main(int argc, char** argv) {
uint64_t counter = rdtscp();
for(int i = 0; i < 10000000; ++i) {
in1[i] = (char)i * 200;
in2[i] = (char)i * 100;
}
int joins = 0;
for(int j = 0; j < 10000000; ++j) {
int el = in1[j];
for(int m = 0; m < 10000000; m++) {
if (in2[m] == el) {
joins++;
break;
}
}
}
printf("Joins %d Cycles total %ld\n", joins, (rdtscp() - counter));
return 0;
}
请不要看程序的操作。他们没有什么意义。我们试图重现的是一系列对内存的访问和对它们的简单操作。
我们在 Mac 上启动了这个程序,输出结果是:
Joins 10000000 Cycles total 589015641
在 linux 机器上是:
Joins 10000000 Cycles total 838198832
显然,linux 版本需要更多的 CPU 周期,这可能是访问内存所必需的。现在的问题是:为什么内存访问变慢了?
一个原因可能是 in1 和 in2 不适合 CPU 缓存,这需要一些 RAM 访问。正如 Roy Longbottom 所指出的,linux 中的内存确实是 ECC,这可能是性能较低的原因。如果我们将其与略低的 CPU 速度、沙桥和常春藤桥之间的差异结合起来,那么我们可能对这种差异有一个很好的解释。
无论如何,谢谢大家的提示!
【问题讨论】:
-
你是如何安排程序的?启动/完成或进入/退出
main? (应该是后者) -
“some”有多少个字符串?如果它不止几个,您可以尝试将输出重定向到一个文件,以防输出减慢速度。不过老实说,一个比另一个少花一点点时间可能不值得太担心。如果您需要扩展到更大的数据集或更长的工作时间,那么可能值得对两者进行分析,看看是否可以缩小范围。
-
我的代码几乎有 2 倍的差异(有利于在 Mac 上编译和运行的代码)。我尝试在非常不同的硬件上在 linux(Ubuntu、CentOS)下编译和运行我的 c++ 代码,并且在所有情况下,Linux 的速度都慢了大约 2 倍。使用完全相同的优化标志和 g++ 编译器
标签: c++ performance macos gcc operating-system