我正在为考试而学习,并试图解决以下问题: 我使用以下C代码进行一些数组初始化:
int i, n = 61440;
double x[n];
for(i=0; i < n; i++) {
x[i] = 1;
}
但是以下代码运行得更快(1000次迭代之间相差0.5秒):
int i, n = 61440;
double x[n];
for(i=n-1; i >= 0; i--) {
x[i] = 1;
}
我首先认为这是由于循环访问了n变量,因此不得不进行更多的读取(例如,此处建议使用Why is iterating through an array backwards faster than forwards)。但是,即使我将第一个循环中的n更改为硬编码值,反之亦然,也可以将底部循环中的0更改为变量,但性能仍保持不变。我还尝试将循环更改为仅做一半的工作(从0到<30720,或者从n-1到> = 30720),以消除对0值的任何特殊处理,但是底部循环仍然更快。 / p>
我认为是因为某些编译器优化了吗?但是我查找生成的机器代码的所有内容都表明,<和> =应该相等。
感谢您的任何提示或建议!谢谢!
编辑:Makefile,以获取编译器详细信息(这是多线程练习的一部分,因此是OpenMP,尽管在这种情况下,它们都在1个内核上运行,而代码中没有任何OpenMP指令)
#CC = gcc
CC = /opt/rh/devtoolset-2/root/usr/bin/gcc
OMP_FLAG = -fopenmp
CFLAGS = -std=c99 -O2 -c ${OMP_FLAG}
LFLAGS = -lm
.SUFFIXES : .o .c
.c.o:
${CC} ${CFLAGS} -o $@ $*.c
sblas:sblas.o
${CC} ${OMP_FLAG} -o $@ $@.o ${LFLAGS}
Edit2:我用n * 100重做实验,得到了相同的结果: 前进:〜170s 后退:〜120s 与之前的1.7s和1.2s相似,只是100倍
Edit3:最小示例-上面描述的更改,所有更改都局限于矢量更新方法。这是默认的前向版本,比后向版本for(i = limit - 1; i >= 0; i--)
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <omp.h>
void vector_update(double a[], double b[], double x[], int limit);
/* SBLAS code */
void *main() {
int n = 1024*60;
int nsteps = 1000;
int k;
double a[n], b[n], x[n];
double vec_update_start;
double vec_update_time = 0;
for(k = 0; k < nsteps; k++) {
// Loop over whole program to get reasonable execution time
// (simulates a time-steping code)
vec_update_start = omp_get_wtime();
vector_update(a, b, x, n);
vec_update_time = vec_update_time + (omp_get_wtime() - vec_update_start);
}
printf( "vector update time = %f seconds \n \n", vec_update_time);
}
void vector_update(double a[], double b[], double x[] ,int limit) {
int i;
for (i = 0; i < limit; i++ ) {
x[i] = 0.0;
a[i] = 3.142;
b[i] = 3.142;
}
}
Edit4:CPU是AMD四核Opteron8378。该计算机使用其中的4个,但我在主处理器上仅使用一个(AMD架构中的内核ID 0)
答案 0 :(得分:1)
不是向后迭代,而是与零的比较导致第二种情况下的循环运行得更快。
for(i=n-1; i >= 0; i--) {
与零的比较可以通过一个汇编指令完成,而与任何其他数字的比较则需要多个指令。
答案 1 :(得分:0)
主要原因是您的编译器不太擅长优化。从理论上讲,没有理由认为一个更好的编译器不能将您的两个版本的代码都转换为完全相同的机器代码,而不是让它变慢。
除此以外的一切取决于生成的机器代码及其运行的方式。这可能包括RAM和/或CPU速度的差异,缓存行为的差异,硬件预取的差异(以及预取器的数量),指令成本和指令流水线的差异,推测的差异等。请注意(理论上)不能排除(在大多数计算机上,但不是在您的计算机上)编译器为正向循环生成的机器代码比其为向后循环生成的机器代码更快的可能性(您的样本大小不足以具有统计意义,除非您在所有运行该代码的计算机都相同的嵌入式系统或游戏机上工作。