我想应该从对问题的更高层次的描述开始。
但作为次要选项,我建议交换数组索引,以便更轻松地编写一个结合了四个(可能是不同的)向量的超快 SSE 内循环:
double times[N+1][nsims], *tend = times[N+1];
double *j,*k,*i,*l;
for (j=times[2];j<tend;j+=nsims)
for (k=j;k<tend;k+=nsims)
if (strcmp( )) ... /* One _can_ move this elsewhere, but why bother? */
for (i=times[2];i<tend;i+=nsims)
for (l=i;l<tend;l+=nsims) {
interm = efficient_sse_implementation(j,k,i,l, nsims);
...
}
对于少于 4 个不同数组的情况,也可以通过编写不同的内核来实现微小的优化。(在这种情况下,可以跳过每步一个内存操作。)
编辑
在这种情况下,模式的结构for(j=2;j<=N;j++) for (k=j;k<=N;k++)
重复了两次,仅此一项就暗示了更高级别优化的可能性——执行的操作是什么?虽然为此苦苦挣扎,但这种模式仍然提出了另一种方法:缓存 780 个(左右)子产品,但同时执行循环阻塞。这种方法不应该与我对先生的评论有同样的问题。格贝尼森
for (A=0;A<50000;A+=100) {
int k=0;
for (i=2;i<=N;i++)
for (j=i;j<=N;j++,k++)
for (a=0;a<100;a++) precalc[k][a]=times[i][A+a]*times[j][A+a];
for (i=0;i<k;i++) // Now i loops from 0..779 or so
for (j=0;j<k;j++) {
for (a=0;a<100;a++) partial_product+=precalc[i][a]*precalc[j][a];
// accumulate also partial_product to moment
}
}
免责声明:这是未经尝试的,但存在一些最佳的块大小(不一定是 100)(它可能比以前的情况更糟)。另请注意,这种方法会为预先计算的表使用大量内存。(选择 100 的块大小需要 624000 字节的内存,这听起来不错。要低于 256k,块长度只能是 42)。
编辑 2:
// 请注意,EDIT_1 中的循环同时计算P[2][a]*P[3][a]
和P[3][a]*P[2][a]
。
for (i=0;i<k;i++) // Now i loops from 0..779 or so, but... we can limit the
for (j=i;j<k;j++) { // calculation to the upper triangle of the 780^2 matrix
for (a=0;a<100;a++) partial_product+=precalc[i][a]*precalc[j][a];
moment[i]+=partial_product;
moment[lower_triangle(i)]+=partial_product; // <-- 50% speed increase
}
编辑3:这里有一些尝试:
gcc -O4 -DCACHELEVEL=2 -DPOPULATE=1 -DARRAY_OPT=1 && time ./a.out
POPULATE
初始化数组(假设非零内容很重要)
ARRAY_OPT=1
将数组索引切换到(也许)更好的顺序
CACHELEVEL=2
或 3 次切换缓存中间结果。
STRCMP
可以在源代码中找到测试 memcmp vs. strcmp vs '1'
NOT TODO 1 : LOOP_BLOCKING with cached values - 降低性能
TODO 2 : 只计算上三角形
TODO 3 : 找出changed_times[n]
and的含义moments[0][p]
- 正如它现在所表明的那样,没有任何计算被保存!
#include <stddef.h>
#define N 40
#define nsims 8000
#if ARRAY_OPT
#define TIMES(n,a) times[n][a]
double times[N+1][nsims]; // [nsims];
#else
#define TIMES(n,a) times[a][n]
double times[nsims][N+1];
#endif
#define STRCMP 1
// vs.
// #define STRCMP1 strcmp(mmethod, "emp")==0
void init()
{
#ifdef POPULATE
int i,a;
for (i=0;i<=N;i++)
for (a=0;a<nsims;a++)
TIMES(i,a) = (double)((i^a)&7) - 3.5;
#endif
}
double moments[4000] = { 0 };
double cache1[nsims];
double cache2[nsims];
int main()
{
int j,k,i,l,a, pcount=0;
init();
int changed_times[N+1]={0};
char *mmethod="emp";
double moment,interm;
for(j=2;j<=N;j++) {
for(k=j;k<=N;k++) {
#if CACHELEVEL == 2
for (a=0;a<nsims;a++) cache1[a]=TIMES(j,a)*TIMES(k,a);
#endif
moment=0;
for(i=2;i<=N;i++) {
#if CACHELEVEL == 3
for (a=0;a<nsims;a++) cache2[a]=TIMES(j,a)*TIMES(k,a)*TIMES(i,a);
#else
for (a=0;a<nsims;a++) cache2[a]=cache1[a]*TIMES(i,a);
#endif
for(l=i;l<=N;l++) {
if(STRCMP) {
for(a=0;a<nsims;a++) {
#if CACHELEVEL >= 2
interm += (double) cache2[a]*TIMES(l,a);
#else
interm=interm + (double) TIMES(k,a) * TIMES(j,a) * TIMES(i,a) * TIMES(l,a);
#endif
}
interm = (double) interm/(double)nsims;
moment = moment + (interm*i*l);
interm=0;
}
}
}
//if(!(changed_times[k]==0
// && changed_times[j]==0
// && changed_times[l]==0
// && changed_times[i]==0))
//{
// moments[0][pcount]=(double) moment;
// changed_times[k]++;changed_times[j]++; /* or what? */
// changed_times[l]++;changed_times[i]++;
//} else {
// moments[0][pcount]=moments[0][pcount];
//}
pcount++;
}
}
printf("%d %f\n",pcount, moment);
}