全局变量的意外性能
Unexpected performance with global variables
我使用全局变量得到了一个奇怪的结果。这个问题的灵感来自 。在下面的代码中,如果我更改
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 %.16e\n", dtime/100);
return 0;
}
如果 char* buff
是自动变量(即不是 global
或 static
),我也会得到相同的结果。我的意思是:
//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;
}
如果我将 buff
更改为短指针,那么性能会很快,并且不依赖于 ncols
是静态的还是常数,如果 buff
是自动的。但是,当我将 buff
设为 int*
指针时,我观察到与 char*
.
相同的效果
我认为这可能是由于指针别名造成的,所以我也尝试了
void func(int * restrict pbuff, int * restrict _nrows, int * restirct _ncols)
但这没什么区别。
这是我的问题
- 当
buff
是 char*
指针或 int*
全局指针时,为什么代码
当 ncols
具有文件范围或常量时更快?
- 为什么
buff
作为自动变量而不是全局变量或静态变量会使代码更快?
- 为什么
buff
是短指针时没有区别?
- 如果这是由于指针别名造成的,为什么
restrict
没有明显的效果?
请注意,我使用 omp_get_wtime()
只是因为它方便计时。
A const
很可能总是会产生比 read/write 变量更快或同样快的代码,因为编译器知道该变量不会被更改,这反过来又可以实现很多优化选项。
声明文件范围变量int
或static int
应该不会对性能产生太大影响,因为它仍将分配在同一个位置:.data
部分。
但是正如评论中提到的,如果变量是全局变量,编译器可能不得不假设其他一些文件(翻译单元)可能会修改它并因此阻止一些优化。我想这就是正在发生的事情。
但这无论如何都不是问题,因为从来没有理由在 C 中声明一个全局变量。始终将它们声明为 static
以防止变量被滥用于意大利面条式编码目的。
一般来说,我也会质疑您的基准测试结果。在 Windows 你应该使用 QueryPerformanceCounter 和类似的。
https://msdn.microsoft.com/en-us/library/windows/desktop/dn553408%28v=vs.85%29.aspx
正如所写的那样,某些元素允许 GCC 在优化方面采取不同的行为;我们看到的最有影响力的优化可能是循环矢量化。因此,
Why is the code faster?
代码速度更快,因为它的热部分,func
中的循环,已经通过自动矢量化进行了优化。在 ncols
和 static
/const
的情况下,确实,GCC 发出:
note: loop vectorized
note: loop peeled for vectorization to enhance alignment
如果您打开 -fopt-info-loop
、-fopt-info-vec
或它们与进一步 -optimized
的组合,则可见,因为它具有相同的效果。
- Why does buff being an automatic variable instead of global or static
make the code faster?
在这种情况下,GCC 能够计算应用矢量化直观所需的迭代次数。这又是由于 buf
的存储,如果没有另外指定,它是外部的。整个矢量化会立即被跳过,这与 buff
在本地进行并成功的情况不同。
- Why does it make no difference when buff is a short pointer?
为什么要这样做? func
接受一个 char*
可以别名任何东西。
- If this is due to pointer aliasing why does restrict have no noticeable effect?
我不认为,因为 GCC 可以看到它们在调用 func
时没有别名:不需要 restrict
。
我使用全局变量得到了一个奇怪的结果。这个问题的灵感来自
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 %.16e\n", dtime/100);
return 0;
}
如果 char* buff
是自动变量(即不是 global
或 static
),我也会得到相同的结果。我的意思是:
//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;
}
如果我将 buff
更改为短指针,那么性能会很快,并且不依赖于 ncols
是静态的还是常数,如果 buff
是自动的。但是,当我将 buff
设为 int*
指针时,我观察到与 char*
.
我认为这可能是由于指针别名造成的,所以我也尝试了
void func(int * restrict pbuff, int * restrict _nrows, int * restirct _ncols)
但这没什么区别。
这是我的问题
- 当
buff
是char*
指针或int*
全局指针时,为什么代码 当ncols
具有文件范围或常量时更快? - 为什么
buff
作为自动变量而不是全局变量或静态变量会使代码更快? - 为什么
buff
是短指针时没有区别? - 如果这是由于指针别名造成的,为什么
restrict
没有明显的效果?
请注意,我使用 omp_get_wtime()
只是因为它方便计时。
A const
很可能总是会产生比 read/write 变量更快或同样快的代码,因为编译器知道该变量不会被更改,这反过来又可以实现很多优化选项。
声明文件范围变量int
或static int
应该不会对性能产生太大影响,因为它仍将分配在同一个位置:.data
部分。
但是正如评论中提到的,如果变量是全局变量,编译器可能不得不假设其他一些文件(翻译单元)可能会修改它并因此阻止一些优化。我想这就是正在发生的事情。
但这无论如何都不是问题,因为从来没有理由在 C 中声明一个全局变量。始终将它们声明为 static
以防止变量被滥用于意大利面条式编码目的。
一般来说,我也会质疑您的基准测试结果。在 Windows 你应该使用 QueryPerformanceCounter 和类似的。 https://msdn.microsoft.com/en-us/library/windows/desktop/dn553408%28v=vs.85%29.aspx
正如所写的那样,某些元素允许 GCC 在优化方面采取不同的行为;我们看到的最有影响力的优化可能是循环矢量化。因此,
Why is the code faster?
代码速度更快,因为它的热部分,func
中的循环,已经通过自动矢量化进行了优化。在 ncols
和 static
/const
的情况下,确实,GCC 发出:
note: loop vectorized
note: loop peeled for vectorization to enhance alignment
如果您打开 -fopt-info-loop
、-fopt-info-vec
或它们与进一步 -optimized
的组合,则可见,因为它具有相同的效果。
- Why does buff being an automatic variable instead of global or static make the code faster?
在这种情况下,GCC 能够计算应用矢量化直观所需的迭代次数。这又是由于 buf
的存储,如果没有另外指定,它是外部的。整个矢量化会立即被跳过,这与 buff
在本地进行并成功的情况不同。
- Why does it make no difference when buff is a short pointer?
为什么要这样做? func
接受一个 char*
可以别名任何东西。
- If this is due to pointer aliasing why does restrict have no noticeable effect?
我不认为,因为 GCC 可以看到它们在调用 func
时没有别名:不需要 restrict
。