C OMP for 并行区域中的循环。不分工
C OMP for loop in parallel region. Not work-shared
我有一个函数想要并行化。这是连载版。
void parallelCSC_SpMV(float *x, float *b)
{
int i, j;
for(i = 0; i < numcols; i++)
{
for(j = colptrs[i] - 1; j < colptrs[i+1] - 1; j++)
{
b[irem[j] - 1] += xrem[j]*x[i];
}
}
}
我认为一个不错的方法是让每个线程在线程完成后写入 b 数组的私有副本(不需要是受保护的临界区,因为它是私有副本) ,然后它将其结果复制到实际的 b 数组。这是我的代码。
void parallelCSC_SpMV(float *x, float *b)
{
int i, j, k;
#pragma omp parallel private(i, j, k)
{
float* b_local = (float*)malloc(sizeof(b));
#pragma omp for nowait
for(i = 0; i < numcols; i++)
{
for(j = colptrs[i] - 1; j < colptrs[i+1] - 1; j++)
{
float current_add = xrem[j]*x[i];
int index = irem[j] - 1;
b_local[index] += current_add;
}
}
for (k = 0; k < sizeof(b) / sizeof(b[0]); k++)
{
// Separate question: Is this if statement allowed?
//if (b_local[k] == 0) { continue; }
#pragma omp atomic
b[k] += b_local[k];
}
}
}
但是,由于第二个 for 循环,我遇到了分段错误。我不需要在该循环上使用 "#pragma omp for"
,因为我希望每个线程都能完全执行它。如果我注释掉 for 循环中的内容,则没有分段错误。我不确定是什么问题。
您可能正在尝试访问动态数组中超出范围的位置 b_local。
看到 sizeof(b)
将 return float*
的字节大小(浮点指针的大小)。
如果您想知道传递给函数的数组的大小,我建议您将其添加到函数的参数中。
void parallelCSC_SpMV(float *x, float *b, int b_size){
...
float* b_local = (float*) malloc(sizeof(float)*b_size);
...
}
而且,如果 colptrs
的大小是 numcols
,我会小心 colptrs[i+1]
,因为 i=numcols-1
会出现另一个超出范围的问题。
首先,正如Jim Cownie所指出的:
In all of these answers, b_local is uninitialised, yet you are adding
to it. You need to use calloc instead of malloc
只是为了添加到已接受的答案中,我认为您可以尝试以下方法来避免并行调用 malloc
以及调用 #pragma omp atomic
.
的开销
void parallelCSC_SpMV(float *x, float *b, int b_size, int num_threads) {
float* b_local[num_threads];
for(int i = 0; i < num_threads; i++)
b_local[i] = calloc(b_size, sizeof(float));
#pragma omp parallel num_threads(num_threads)
{
int tid = omp_get_thread_num();
#pragma omp for
for(int i = 0; i < numcols; i++){
for(int j = colptrs[i] - 1; j < colptrs[i+1] - 1; j++){
float current_add = xrem[j]*x[i];
int index = irem[j] - 1;
b_local[tid][index] += current_add;
}
}
}
for(int id = 0; id < num_threads; id++)
{
#pragma omp for simd
for (int k = 0; k < b_size; k++)
{
b[k] += b_local[id][k];
}
free(b_local[id]);
}
}
我还没有测试过它的性能,所以请随时测试并提供反馈。
你可以进一步优化,而不是为主线程创建一个 local_b
只是重用原来的 b
,如下:
void parallelCSC_SpMV(float *x, float *b, int b_size, int num_threads) {
float* b_local[num_threads-1];
for(int i = 0; i < num_threads-1; i++)
b_local[i] = calloc(b_size, sizeof(float));
#pragma omp parallel num_threads(num_threads)
{
int tid = omp_get_thread_num();
float *thread_b = (tid == 0) ? b : b_local[tid-1];
#pragma omp for
for(int i = 0; i < numcols; i++){
for(int j = colptrs[i] - 1; j < colptrs[i+1] - 1; j++){
float current_add = xrem[j]*x[i];
int index = irem[j] - 1;
thread_b[index] += current_add;
}
}
}
for(int id = 0; id < num_threads-1; id++)
{
#pragma omp for simd
for (int k = 0; k < b_size; k++)
{
b[k] += b_local[id][k];
}
free(b_local[id]);
}
}
我有一个函数想要并行化。这是连载版。
void parallelCSC_SpMV(float *x, float *b)
{
int i, j;
for(i = 0; i < numcols; i++)
{
for(j = colptrs[i] - 1; j < colptrs[i+1] - 1; j++)
{
b[irem[j] - 1] += xrem[j]*x[i];
}
}
}
我认为一个不错的方法是让每个线程在线程完成后写入 b 数组的私有副本(不需要是受保护的临界区,因为它是私有副本) ,然后它将其结果复制到实际的 b 数组。这是我的代码。
void parallelCSC_SpMV(float *x, float *b)
{
int i, j, k;
#pragma omp parallel private(i, j, k)
{
float* b_local = (float*)malloc(sizeof(b));
#pragma omp for nowait
for(i = 0; i < numcols; i++)
{
for(j = colptrs[i] - 1; j < colptrs[i+1] - 1; j++)
{
float current_add = xrem[j]*x[i];
int index = irem[j] - 1;
b_local[index] += current_add;
}
}
for (k = 0; k < sizeof(b) / sizeof(b[0]); k++)
{
// Separate question: Is this if statement allowed?
//if (b_local[k] == 0) { continue; }
#pragma omp atomic
b[k] += b_local[k];
}
}
}
但是,由于第二个 for 循环,我遇到了分段错误。我不需要在该循环上使用 "#pragma omp for"
,因为我希望每个线程都能完全执行它。如果我注释掉 for 循环中的内容,则没有分段错误。我不确定是什么问题。
您可能正在尝试访问动态数组中超出范围的位置 b_local。
看到 sizeof(b)
将 return float*
的字节大小(浮点指针的大小)。
如果您想知道传递给函数的数组的大小,我建议您将其添加到函数的参数中。
void parallelCSC_SpMV(float *x, float *b, int b_size){
...
float* b_local = (float*) malloc(sizeof(float)*b_size);
...
}
而且,如果 colptrs
的大小是 numcols
,我会小心 colptrs[i+1]
,因为 i=numcols-1
会出现另一个超出范围的问题。
首先,正如Jim Cownie所指出的:
In all of these answers, b_local is uninitialised, yet you are adding to it. You need to use calloc instead of malloc
只是为了添加到已接受的答案中,我认为您可以尝试以下方法来避免并行调用 malloc
以及调用 #pragma omp atomic
.
void parallelCSC_SpMV(float *x, float *b, int b_size, int num_threads) {
float* b_local[num_threads];
for(int i = 0; i < num_threads; i++)
b_local[i] = calloc(b_size, sizeof(float));
#pragma omp parallel num_threads(num_threads)
{
int tid = omp_get_thread_num();
#pragma omp for
for(int i = 0; i < numcols; i++){
for(int j = colptrs[i] - 1; j < colptrs[i+1] - 1; j++){
float current_add = xrem[j]*x[i];
int index = irem[j] - 1;
b_local[tid][index] += current_add;
}
}
}
for(int id = 0; id < num_threads; id++)
{
#pragma omp for simd
for (int k = 0; k < b_size; k++)
{
b[k] += b_local[id][k];
}
free(b_local[id]);
}
}
我还没有测试过它的性能,所以请随时测试并提供反馈。
你可以进一步优化,而不是为主线程创建一个 local_b
只是重用原来的 b
,如下:
void parallelCSC_SpMV(float *x, float *b, int b_size, int num_threads) {
float* b_local[num_threads-1];
for(int i = 0; i < num_threads-1; i++)
b_local[i] = calloc(b_size, sizeof(float));
#pragma omp parallel num_threads(num_threads)
{
int tid = omp_get_thread_num();
float *thread_b = (tid == 0) ? b : b_local[tid-1];
#pragma omp for
for(int i = 0; i < numcols; i++){
for(int j = colptrs[i] - 1; j < colptrs[i+1] - 1; j++){
float current_add = xrem[j]*x[i];
int index = irem[j] - 1;
thread_b[index] += current_add;
}
}
}
for(int id = 0; id < num_threads-1; id++)
{
#pragma omp for simd
for (int k = 0; k < b_size; k++)
{
b[k] += b_local[id][k];
}
free(b_local[id]);
}
}