C语言 全局变量的意外性能



我使用全局变量得到一个奇怪的结果。这个问题的灵感来自另一个问题。在下面的代码中,如果我更改

int ncols = 4096;

static int ncols = 4096; 

const int ncols = 4096;

代码运行速度更快,程序集也简单得多。

//c99 -O3 -Wall -fopenmp foo.c
#include <stdlib.h>
#include <stdio.h>
#include <omp.h>
int nrows = 4096;
int ncols = 4096;
//static int ncols = 4096;
char* buff;
void func(char* pbuff, int * _nrows, int * _ncols) {
    for (int i=0; i<*_nrows; i++) {
        for (int j=0; j<*_ncols; j++) {
            *pbuff += 1;
            pbuff++;
        }
    }
}
int main(void) {
    buff = calloc(ncols*nrows, sizeof*buff);
    double dtime = -omp_get_wtime();
    for(int k=0; k<100; k++) func(buff, &nrows, &ncols);
    dtime += omp_get_wtime();
    printf("time %.16en", dtime/100);
    return 0;
}

如果char* buff是自动变量(即不是globalstatic(,我也得到相同的结果。我的意思是:

//c99 -O3 -Wall -fopenmp foo.c
#include <stdlib.h>
#include <stdio.h>
#include <omp.h>
int nrows = 4096;
int ncols = 4096;
void func(char* pbuff, int * _nrows, int * _ncols) {
    for (int i=0; i<*_nrows; i++) {
        for (int j=0; j<*_ncols; j++) {
            *pbuff += 1;
            pbuff++;
        }
    }
}
int main(void) {
    char* buff = calloc(ncols*nrows, sizeof*buff);
    double dtime = -omp_get_wtime();
    for(int k=0; k<100; k++) func(buff, &nrows, &ncols);
    dtime += omp_get_wtime();
    printf("time %.16en", dtime/100);
    return 0;
}

如果我将buff更改为短指针,则性能很快,并且不取决于ncols是静态的还是恒定的,或者buff是否是自动的。但是,当我buff int*指针时,我观察到与char*相同的效果。

我认为这可能是由于指针混叠,所以我也尝试了

void func(int * restrict pbuff, int * restrict _nrows, int * restirct _ncols)

但这没有什么区别。

这是我的问题

  1. buffchar*指针或int*全局指针时,为什么代码当ncols具有文件范围或恒定时更快?
  2. 为什么buff是自动变量而不是全局变量或静态变量会使代码更快?
  3. 为什么当buff是一个短指针时没有区别?
  4. 如果这是由于指针混叠造成的,为什么restrict没有明显的影响?

请注意,我使用omp_get_wtime()只是因为它方便计时。

一些元素允许GCC在优化方面采取不同的行为; 很可能,我们看到的最具影响力的优化是循环矢量化。因此

为什么代码更快?

代码更快,因为它的热门部分,即func中的循环,已经通过自动矢量化进行了优化。在具有static/const的合格ncols的情况下,确实,GCC会发出:

注意:循环矢量化
注意:环剥离以进行矢量化以增强对齐

如果您打开 -fopt-info-loop-fopt-info-vec或具有进一步-optimized的组合,这是可见的,因为它具有相同的效果。


  1. 为什么 buff 是自动变量而不是全局变量或静态变量 使代码更快?

在这种情况下,GCC 能够计算应用矢量化的直观必要的迭代次数。这同样是由于存储buf如果没有另行说明,则存储是外部的。整个矢量化立即被跳过,这与buff在本地进行并成功时不同。

  1. 为什么当buff是一个短指针时没有区别?

为什么呢? func接受可能为任何内容添加别名的char*

  1. 如果这是由于指针混叠,为什么限制没有明显效果?

我不认为,因为 GCC 可以看到它们在调用 func 时不会别名:不需要restrict

const很可能

总是产生更快或同样快的代码作为读/写变量,因为编译器知道变量不会被更改,这反过来又启用了大量的优化选项。

声明文件范围变量intstatic int应该不会对性能产生太大影响,因为它仍将分配给同一位置:.data部分。

但如注释中所述,如果变量是全局变量,编译器可能必须假设其他文件(翻译单元(可能会修改它,从而阻止某些优化。我想这就是正在发生的事情。

但无论如何,这不应该引起任何关注,因为永远没有理由在 C, period 中声明全局变量。始终将它们声明为 static,以防止变量被滥用于意大利面条编码目的。

总的来说,我也会质疑你的基准测试结果。在Windows中,您应该使用QueryPerformanceCounter和类似功能。https://msdn.microsoft.com/en-us/library/windows/desktop/dn553408%28v=vs.85%29.aspx

相关内容

  • 没有找到相关文章

最新更新