并行计算的C ++代码优化示例

时间:2016-01-13 17:50:25

标签: c++ multithreading optimization

我正在尝试理解优化例程。我专注于我的代码中最关键的部分(代码有一些长度为“nc”的循环和一个长度为“np”的循环,其中数字“np”比“nc”大得多)。我在这里介绍部分代码。剩下的代码在计算时间的百分比上不是很重要,所以我更喜欢在算法的其余部分进行代码净化。但是,具有“np”长度的关键循环是一段非常简单的代码,并且可以并行化。因此,如果我将这部分重写为一些更有效和更不清晰的版本(可能是SSE指令),也不会受到伤害。我正在使用gcc编译器,c ++代码和OpenMP并行化。

此代码是众所周知的particle-in-cell算法的一部分(这也是基本算法)。我正在尝试学习这个版本的代码优化(所以我的目标不是只有有效的PIC算法,因为它已经写成千种变体,但我想为代码优化带来一些示范性的例子)。我正在尝试做一些工作,但我不太确定我是否正确地解决了所有优化属性。

const int NT = ...; // number of threads (in two versions: about 6 or about 30)
const int np = 10000000; // np is about 1000-10000 times larger than nc commonly
const int nc = 10000;
const int step = 1000;

float u[np], x[np];
float a[nc], a_lin[nc], rho_full[NT][nc], rho_diff[NT][nc] , weight[nc];

int p,num;

for ( i = 0 ; i<step ;  i++) {

// *** 
// *** some not very time consuming code for calculation 
// *** a, a_lin from values of rho_full and rho_diff

#pragma omp for private(p,num)
for ( k = np ; --k ; ) {

    num = omp_get_thread_num();

    p = (int) x[k];
    u[k] += a[p] + a_lin[p] * (x[k] - p);
    x[k] += u[k];

    if (x[k]<0 ) {x[k]+=nc;} else
    if (x[k]>nc) {x[k]-=nc;};

    p = (int) x[k];
    rho_full[num][p] += weight[k];
    rho_diff[num][p] += weight[k] * (x[k] - p);

    }
};

我意识到这有问题:

1)(主要问题)我使用数组rho_full[num][p],其中num是每个线程的索引。在计算之后,我只是总结了这个数组(rho_full[0][p] + rho_full[1][p] + rho_full[2][p] ...)。原因是避免使用两个不同的线程写入数组的相同部分。我不太确定这种方式是否是一种有效的解决方案(注意数字“nc”相对较小,因此“np”的操作次数仍然是最重要的)

2)(同样重要的问题)我需要多次阅读x[k]并且它也会多次更改。也许最好将这个值读入某个寄存器,然后忘记整个x数组或修复一些指针。在所有计算之后,我可以再次调用x[k]数组并存储获得的值。我相信编译器这对我有用,但我不太确定,因为我在算法的中心使用了x[k]的修改。因此,编译器可能会自己做一些有效的工作,但也许在这个版本中,它会调用更多次,因为我会更多地调用并存储该值。

3)(可能不相关)代码使用整数部分和小数点部分以下的余数。它需要这两个值。我将整数部分标识为p = (int) x,将余数标识为x - p。我在开始时和循环内部结束时计算这个例程。可以看出,这种拆分可以存储在某处并在下一步使用(我的意思是步骤i索引)。你认为以下版本更好吗?我将积分和余数部分存储在x的数组而不是整数值x。

int x_int[np];
float x_rem[np];
//...

for ( k = np ; --k ; ) {

    num = omp_get_thread_num();

    u[k] += a[x_int[k]] + a_lin[x_int[k]] * x_rem[k];
    x_rem[k] += u[k];

    p = (int) x_rem[k];   // *** This part is added into code for simplify the rest.
    x_int[k] += p;        // *** And maybe there is a better way how to realize
    x_rem[k] -= p;        // *** this "pushing correction".

    if (x_int[k]<0 ) {x_int[k]+=nc;} else
    if (x_int[k]>nc) {x_int[k]-=nc;};

    rho_full[num][x_int[k]] += weight[k];
    rho_diff[num][x_int[k]] += weight[k] * x_rem[k];

    }
};

1 个答案:

答案 0 :(得分:0)

您可以使用OMP reduction作为for循环:

int result = 0;

#pragma omp for nowait reduction(+:result) 
for ( k = np ; --k ; ) {

    num = omp_get_thread_num();

    p = (int) x[k];
    u[k] += a[p] + a_lin[p] * (x[k] - p);
    x[k] += u[k];

    if (x[k]<0 ) {x[k]+=nc;} else
    if (x[k]>nc) {x[k]-=nc;};

    p = (int) x[k];
    result += weight[k] + weight[k] * (x[k] - p);

}