为什么malloc 7x比英特尔icc的新版慢?

我基于malloc与新分配一个浮点数组。 我的理解是,由malloc执行的操作是由new执行的操作的子集 - malloc只是分配,但是新的分配和构造,尽管我不确定这对基元是否有意义的区别。

使用gcc进行基准测试结果会给出预期的行为。 malloc()更快。 甚至有人提出与此相反的问题。

用icc malloc可以比新的慢7倍。 怎么可能?!

以下所有内容仅仅是基准程序的细节。

对于基准测试,我使用了最近由英特尔描述的协议。 这是我的结果。

用GNU的gcc分配一个4000浮点数组的时钟周期已经过去了:

new memory allocation, cycles            12168
malloc allocation, cycles                 5144

与英特尔的icc:

new    memory allocation clock cycles     7251
malloc memory allocation clock cycles    52372

我如何使用malloc:

volatile float* numbers = (float*)malloc(sizeof(float)*size);

我如何使用新的:

volatile float* numbers = new float[size];

因为在之前的基准测试中,我遇到了狡猾的编译器优化掉整个函数调用和生成存储常量的程序的问题。 (编译器选择以这种方式进行优化的函数实际上比它没有的更快!)我试图用volatile去除,只是为了确保结果是相同的。

我夹着我想要在两个宏之间进行基准测试的代码部分。

函数之前的宏:

#define CYCLE_COUNT_START 
asm volatile ("CPUIDnt" 
"RDTSCnt" 
"mov %%edx, %0nt" 
"mov %%eax, %1nt": "=r" (cycles_high), "=r" (cycles_low):: 
"%rax", "%rbx", "%rcx", "%rdx");

函数之后的宏:

#define CYCLE_COUNT_END 
asm volatile("RDTSCPnt" 
"mov %%edx, %0nt" 
"mov %%eax, %1nt" 
"CPUIDnt": "=r" (cycles_high1), "=r" (cycles_low1):: 
"%rax", "%rbx", "%rcx", "%rdx"); 
start = ( ((uint64_t)cycles_high << 32) | cycles_low ); 
end = ( ((uint64_t)cycles_high1 << 32) | cycles_low1 ); 
ellapsed_cycles = end - start;

因此,新增夹层宏的分配调用是这样的:

CYCLE_COUNT_START
volatile float* numbers = new float[size];
CYCLE_COUNT_END

之后,我检查了ellapsed_cycles的值,看看每件事情是如何发生的。

为了确保我没有做一些愚蠢的事情,下面是我用icc编译的方式:

icc -O3 -ipo -no-prec-div -std=c++11 heap_version3.cpp           -o heap_version3
icc -O3 -ipo -no-prec-div -std=c++11 malloc_heap_version3.cpp    -o malloc_heap_version3

并与海湾合作委员会:

g++-4.8 -Ofast -march=native -std=c++11 heap_version3.cpp        -o heap_version3
g++-4.8 -Ofast -march=native -std=c++11 malloc_heap_version3.cpp -o malloc_heap_version3

这是在2012年的MacBook Pro上提供了corei7-avx说明。 我有'as'二进制代码与脚本匹配,以便gcc可以使用AVX指令。

编辑1

要回答那些希望看到更多循环迭代的人,请浏览英特尔链接,然后发布。 另一方面,我可能会有相同的反应,所以这里是循环迭代。

数组大小仍然是4000,程序的每次运行仍然执行一次内存分配。 我不想通过分配一个不适合L1的更大阵列或重复分配和释放内存并提出有关内存的其他问题来改变正在进行基准测试的内容。 该程序由bash在循环中运行。 我为基准测试运行了4个独立的程序,每个循环迭代都运行4个程序,以减少由于其他正在运行的进程而导致的异构性。

for i in $(seq 1 10000); do
    echo gcc malloc $(./gcc_malloc_heap_version3 | head -n 1 | cut -d" " -f 4-)
    echo icc malloc $(./icc_malloc_heap_version3 | head -n 1 | cut -d" " -f 4-)
    echo gcc new $(./gcc_heap_version3 | head -n 1 | cut -d" " -f 4-)
    echo icc new $(./icc_heap_version3 | head -n 1 | cut -d" " -f 4-)
done

icc内存分配时间:

       malloc       new
Min.   : 3093      1150
1st Qu.: 3729      1367
Median : 3891      1496
Mean   : 4015      1571
3rd Qu.: 4099      1636
Max.   :33231    183377

    Welch Two Sample t-test
    p-value < 2.2e-16

观察到的差异不太可能偶然发生。

编译器和分配方法的密度估计值:

在存储器分配期间经过的时钟周期的概率密度估计

现在差别不大,但icc的顺序仍然与预期相反。

编辑2

结果对于char数组几乎是一样的。 因为sizeof(int)给我4和sizeof(char)给我1我增加了数组长度为16,000。

编辑3

源代码和脚本

编辑4

相同的数据重新绘制为前100个分配的时间进程。 时钟周期每个内存分配的时间进程


它不这样工作。 处理器和操作系统很复杂。 你不能只需要几微秒就打一次电话,并期望获得有意义的时间信息。 对于初学者来说,另一个应用程序可以使用你的CPU一点,RDTSC将继续计数。

链接地址: http://www.djcxy.com/p/6189.html

上一篇: Why is malloc 7x slower than new for Intel's icc?

下一篇: Which one is more optimized for accessing array?