[hellogcc] Re: [hellogcc] Re: [hellogcc] 请问下以下gcc -O0 和 -O1的代码运行时间不同的原因

  • From: 秦� <love.infly@xxxxxxxxx>
  • To: hellogcc@xxxxxxxxxxxxx, wuwei.gucas@xxxxxxxxx
  • Date: Tue, 30 Jul 2013 11:30:33 +0800

Hi, 吴伟

之前自己的问题是集中于两个汇编代码不同带来的性能不同的原因。

主要是O0代码在SIZE次循环和SIZE/2次循环的开销是2:1,但是O1代码的开销接近1:1
。自己手动改.s的对比结果可以看出来,O0性能差距的原因基本就是在多出来的几个movq上。但是自己不明白原因,所以提问。


对于你说的测试结果不一致,我又在自己电脑上做了一次。另外在别人的虚拟机上也简单测了下,和我的结果是一样的。以下是我在本机做的1000次的运行,这个貌似牵扯的比较多,从CPU型号到Gcc版本都会影响结果。

=====================================

我用的如下代码运行了1000遍,每次输出基本没有差距(正负1%左右)。所以整体也符合之前的情况,我的机器环境是Xubuntu13.04,
gcc 版本 4.7.3 (Ubuntu/Linaro 4.7.3-1ubuntu1) ,CPU 是Intel(R) Core(TM)
i3-2100 CPU @ 3.10GHz。

O0情况下依然是K=1,K=2和K=4的情况下都是1/2递减的。
O1情况下1/2递减现象消失了,K=1只是稍微多耗时一点, 这个应该确实是循环数不同计算量不同的原因。

代码如下:

#include <stdio.h>
#include <time.h>
#include <stdint.h>
#define SIZE 64*1024*1024
#define MAX_K 1024
int32_t arr [SIZE];
struct timespec ts;

int main(int argc, char *argv[])
{
    long i,j= 0;
    long start;
    long start_sec;
    int count = 1;
// init the arr;
    for (i = 0; i < SIZE; ++i){
        arr[i] = 0;
    }
    for (j = 1; j <= MAX_K; count++, j*= 2){
        clock_gettime(CLOCK_REALTIME, &ts);
        start = ts.tv_nsec;
        start_sec = ts.tv_sec;
        for (i = 0; i< SIZE; i+=j){
            arr[i] *=3;
        }
        clock_gettime(CLOCK_REALTIME, &ts);
        printf ("%d, %ld, %ld\n",
count,(ts.tv_sec-start_sec)*1000000000+(ts.tv_nsec -start), j);
    }
    return 0;
}

另,我还又用下面的相同循环数的代码分别用O0~3运行了几次,结果是K=128之前一直都是递增的,特别的是O1之后,明显的可以看出,初期的几个递增就是2倍递增,比O0的结果更加符合期望结果,数据如下,
**为什么K=128后的时间下降了,我也不清楚原因。**

1, 60962944, 1
2, 121961343, 2
3, 248836377, 4
4, 478276479, 8
5, 961089972, 16
6, 1371127716, 32
7, 1851929390, 64
8, 964310614, 128
9, 994438045, 256
10, 1030341734, 512
11, 1124689492, 1024

循环数相同的代码就是我贴在SO上的,如下:

#include <stdio.h>
#include <time.h>
#include <stdint.h>
#define SIZE 64*1024*1024
int32_t arr [SIZE];
struct timespec ts;

int main(int argc, char *argv[])
{
long i,j= 0;
long start;
long start_sec;
int count = 1;
int k = 0;

// init the arr;
for (i = 0; i< 64*1024*1024;++i){
    arr[i] = 0;
}

for (j = 1; j< 1025;){
    clock_gettime(CLOCK_REALTIME, &ts);
    start = ts.tv_nsec;
    start_sec = ts.tv_sec;
    for (i = 0, k = 0; i< 64*1024*1024; i++, k+=j){
        k = k & (SIZE -1);
        arr[k] *=3;
        arr[k] =1;
    }
    clock_gettime(CLOCK_REALTIME, &ts);
    printf ("%d, %ld, %ld\n",
count,(ts.tv_sec-start_sec)*1000000000+(ts.tv_nsec -start), j);
    count++;
    j *= 2;
}
return 0;
}


谢谢。


2013/7/29 Wei WU(吴伟) <wuwei.gucas@xxxxxxxxx>:
> Hi 秦��:
>
> 我对于 CPU Caching
> 不是很熟悉,没有看出来其中的原因。我自己运行了一下你贴在SO上的程序,得到的测试数据跟你的测试结果并不一致:在O1优化下j=2的时间是j=1的两倍,而在O0的情况下,两者的得分是类似的。
>
> 另外,一次运行的数据是不稳定的,确实有时候会出现j=2比j=1还快一点的情况。但是这可能是机器的噪音。建议运行多次(例如1000次)之后得到两者差值的平均,这样分析起来比较可靠。
>
> 现代CPU的性能分析是一件很困难的事情,如果你有了答案,强烈欢迎你贴在这个邮件列表中,大家共同学习 :-)
>
> 在 2013年7月29日下午4:51,秦�� <love.infly@xxxxxxxxx>写道:
>
>> c代码本身很简单
>> for (i = 0; i< SIZE; i+=j){
>>             arr[i] *=3;
>> }
>>
>> 就是以一个步进值j遍历整个数组,对成员做*3操作。
>>
>> O0的代码,步进的值在-24(%rbp)中:
>>
>>     movq    $0, -32(%rbp)
>>     jmp    .L5
>> .L6:
>>     movq    -32(%rbp), %rax
>>     movl    arr(,%rax,4), %edx
>>     movl    %edx, %eax
>>     addl    %eax, %eax
>>     addl    %eax, %edx
>>     movq    -32(%rbp), %rax
>>     movl    %edx, arr(,%rax,4)
>>     movq    -24(%rbp), %rax
>>     addq    %rax, -32(%rbp)
>> .L5:
>>     cmpq    $67108863, -32(%rbp)
>>
>> O1的代码,步进的值在 %rbx中:
>>
>>     movl    $0, %eax
>> .L3:
>>     movl    arr(,%rax,4), %ecx
>>     leal    (%rcx,%rcx,2), %edx
>>     movl    %edx, arr(,%rax,4)
>>     addq    %rbx, %rax
>>     cmpq    $67108863, %rax
>>     jle    .L3
>>
>>
>> 想问的问题是,在O0情况下,j = 1的时间是j=2时间的两倍,O1下,两者就基本接近了,甚至步进为1的还快点,为什么?
>>
>>
>> 问题来源是:http://stackoverflow.com/questions/17914782/why-cache-doesnt-work-as-it-supposed-to-be/17915086#17915086
>>
>> 我自己改改O0的代码,变成如下:
>>
>>     movq    $0, -32(%rbp)
>>     movl    $0, %eax
>>     movq    -24(%rbp), %rbx
>>     jmp .L5
>> .L6:
>>     movl    arr(,%rax,4), %edx
>>     movl    %edx, %ecx
>>     addl    %ecx, %ecx
>>     addl    %ecx, %edx
>>     movl    %edx, arr(,%rax,4)
>>     addq    %rbx, %rax
>> .L5:
>>     cmpq    $67108863, %rax
>>     jle .L6
>>
>> 就和O1差不多了。
>>
>> 但是不明白,循环里改掉了几个movq,怎么j = 1的速度就和j = 2的差不多了?原因是什么?
>>
>> 谢谢了。
>
>
>
>
> --
> 吴伟/Wei Wu
> wuwei.gucas@xxxxxxxxx

Other related posts: