全局变量的意外性能

Z b*_*son 7 c gcc pointers global-variables

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

int ncols = 4096;
Run Code Online (Sandbox Code Playgroud)

static int ncols = 4096; 
Run Code Online (Sandbox Code Playgroud)

要么

const int ncols = 4096;
Run Code Online (Sandbox Code Playgroud)

代码运行得更快,组装更简单.

//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 %.16e\n", dtime/100);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

如果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 %.16e\n", dtime/100);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

如果我改为buff短指针,那么性能很快,并且不依赖于if ncols是静态还是常量if buff是自动的.但是,当我制作buff一个int*指针时,我会发现同样的效果char*.

我认为这可能是由于指针别名所以我也尝试过

void func(int * restrict pbuff, int * restrict _nrows, int * restirct _ncols)
Run Code Online (Sandbox Code Playgroud)

但它没有任何区别.

这是我的问题

  1. 何时buffchar*指针或int*全局指针,为什么在ncols具有文件范围或常量时代码更快?
  2. 为什么buff自动变量而不是全局变量或静态变量使代码更快?
  3. 为什么buff短指针时没有区别?
  4. 如果这是由于指针别名,为什么restrict没有明显的效果?

请注意,我使用的omp_get_wtime()原因仅仅是因为它便于计时.

edm*_*dmz 2

正如所写的,某些元素允许 GCC 在优化方面采取不同的行为;很可能,我们看到的最有影响力的优化是循环矢量化。所以,

为什么代码更快?

该代码速度更快,因为它的热门部分,即 中的循环func,已通过自动矢量化进行了优化。ncols事实上,在使用static/限定的情况下const,GCC 会发出:

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

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


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

在这种情况下,GCC 能够计算直观上应用矢量化所需的迭代次数。buf如果没有另外指定,这又是由于其存储是外部的。整个矢量化过程会立即被跳过,buff这与在局部矢量化过程中继续并成功不同。

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

为什么应该这样?func接受char*可以为任何别名的 a 。

  1. 如果这是由于指针别名造成的,为什么限制没有明显的效果?

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