使用 C 中的 openmp,如何并行化包含用于 qsort 的嵌套比较函数的 for 循环?
With openmp in C, how can I parallelize a for loop that contains a nested comparison function for qsort?
我想并行化 for
循环,其中包含 qsort 的嵌套比较函数:
#include <stdio.h>
#include <stdlib.h>
#include <omp.h>
int main(){
int i;
#pragma omp parallel for
for(i = 0; i < 100; i++){
int *index= (int *) malloc(sizeof(int)*10);
double *tmp_array = (double*) malloc(sizeof(double)*10);
int j;
for(j=0; j<10; j++){
tmp_array[j] = rand();
index[j] = j;
}
// QuickSort the index array based on tmp_array:
int simcmp(const void *a, const void *b){
int ia = *(int *)a;
int ib = *(int *)b;
if ((tmp_array[ia] - tmp_array[ib]) > 1e-12){
return -1;
}else{
return 1;
}
}
qsort(index, 10, sizeof(*index), simcmp);
free(index);
free(tmp_array);
}
return 0;
}
当我尝试编译它时,出现错误:
internal compiler error: in get_expr_operands, at tree-ssa-operands.c:881
}
据我所知,这个错误是由于嵌套比较函数引起的。有没有办法让 openmp 使用这个嵌套比较函数?如果不行,有没有不用嵌套比较函数也能达到类似结果的好方法?
编辑:
我正在使用允许嵌套函数的 GNU C 编译器。代码在没有 pragma 语句的情况下编译和运行良好。我不能在 for 循环之外定义 simcmp,因为 tmp_array 必须是一个全局变量,这会扰乱多线程。但是,如果有人建议在没有嵌套函数的情况下实现相同的结果,那将是最受欢迎的。
我用 qsort_r
解决了我的问题,它允许您将一个额外的指针传递给比较函数。
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <omp.h>
int simcmp(const void *a, const void *b, void *tmp_array){
int ia = *(int *)a;
int ib = *(int *)b;
double aa = ((double *)tmp_array)[ia];
double bb = ((double *)tmp_array)[ib];
if ((aa - bb) > 1e-12){
return -1;
}else{
return 1;
}
}
int main(){
int i;
#pragma omp parallel for
for(i = 0; i < 100; i++){
int *index= (int *) malloc(sizeof(int)*10);
double *tmp_array = (double*) malloc(sizeof(double)*10);
int j;
for(j=0; j<10; j++){
tmp_array[j] = rand();
index[j] = j;
}
// QuickSort the index array based on tmp_array:
qsort_r(index, 10, sizeof(*index), simcmp, tmp_array);
free(index);
free(tmp_array);
}
return 0;
}
编译和运行没有问题。但是,它并不完全理想,因为 qsort_r
依赖于平台和编译器。有一个 portable version of qsort_r here 作者很好地总结了我的问题:
If you want to qsort() an array with a comparison operator that takes
parameters you need to use global variables to pass those parameters
(not possible when writing multithreaded code), or use qsort_r/qsort_s
which are not portable (there are separate GNU/BSD/Windows versions
and they all take different arguments).
我知道这已经是自我回答,但这里有一些标准的 C 和 OpenMP 选项。 qsort_r
函数是一个很好的经典选择,但值得注意的是 qsort_s
是 c11 标准的一部分,因此在任何提供 c11 的地方都是可移植的(不包括 Windows,它们还不太提供 c99)。
在没有嵌套比较功能的OpenMP中,仍然使用原始的qsort,有两种方法可以想到。首先是结合 OpenMP 使用经典的全局变量 threadprivate
:
static int *index = NULL;
static double *tmp_array = NULL;
#pragma omp threadprivate(index, tmp_array)
int simcmp(const void *a, const void *b){
int ia = *(int *)a;
int ib = *(int *)b;
double aa = ((double *)tmp_array)[ia];
double bb = ((double *)tmp_array)[ib];
if ((aa - bb) > 1e-12){
return -1;
}else{
return 1;
}
}
int main(){
int i;
#pragma omp parallel for
for(i = 0; i < 100; i++){
index= (int *) malloc(sizeof(int)*10);
tmp_array = (double*) malloc(sizeof(double)*10);
int j;
for(j=0; j<10; j++){
tmp_array[j] = rand();
index[j] = j;
}
// QuickSort the index array based on tmp_array:
qsort_r(index, 10, sizeof(*index), simcmp, tmp_array);
free(index);
free(tmp_array);
}
return 0;
}
上面的版本导致并行区域中的每个线程都使用全局变量索引和 tmp_array 的私有副本,从而解决了这个问题。这可能是您可以用标准 C 和 OpenMP 编写的最便携的版本,唯一可能不兼容的平台是那些不实现线程本地内存的平台(某些微控制器等)。
如果您想避免使用全局变量并仍然具有可移植性并使用 OpenMP,那么我建议使用 C++11 和带有 lambda 的 std::sort
算法:
std::sort(index, index+10, [=](const int& a, const int& b){
if ((tmp_array[a] - tmp_array[b]) > 1e-12){
return -1;
}else{
return 1;
}
});
我想并行化 for
循环,其中包含 qsort 的嵌套比较函数:
#include <stdio.h>
#include <stdlib.h>
#include <omp.h>
int main(){
int i;
#pragma omp parallel for
for(i = 0; i < 100; i++){
int *index= (int *) malloc(sizeof(int)*10);
double *tmp_array = (double*) malloc(sizeof(double)*10);
int j;
for(j=0; j<10; j++){
tmp_array[j] = rand();
index[j] = j;
}
// QuickSort the index array based on tmp_array:
int simcmp(const void *a, const void *b){
int ia = *(int *)a;
int ib = *(int *)b;
if ((tmp_array[ia] - tmp_array[ib]) > 1e-12){
return -1;
}else{
return 1;
}
}
qsort(index, 10, sizeof(*index), simcmp);
free(index);
free(tmp_array);
}
return 0;
}
当我尝试编译它时,出现错误:
internal compiler error: in get_expr_operands, at tree-ssa-operands.c:881
}
据我所知,这个错误是由于嵌套比较函数引起的。有没有办法让 openmp 使用这个嵌套比较函数?如果不行,有没有不用嵌套比较函数也能达到类似结果的好方法?
编辑: 我正在使用允许嵌套函数的 GNU C 编译器。代码在没有 pragma 语句的情况下编译和运行良好。我不能在 for 循环之外定义 simcmp,因为 tmp_array 必须是一个全局变量,这会扰乱多线程。但是,如果有人建议在没有嵌套函数的情况下实现相同的结果,那将是最受欢迎的。
我用 qsort_r
解决了我的问题,它允许您将一个额外的指针传递给比较函数。
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <omp.h>
int simcmp(const void *a, const void *b, void *tmp_array){
int ia = *(int *)a;
int ib = *(int *)b;
double aa = ((double *)tmp_array)[ia];
double bb = ((double *)tmp_array)[ib];
if ((aa - bb) > 1e-12){
return -1;
}else{
return 1;
}
}
int main(){
int i;
#pragma omp parallel for
for(i = 0; i < 100; i++){
int *index= (int *) malloc(sizeof(int)*10);
double *tmp_array = (double*) malloc(sizeof(double)*10);
int j;
for(j=0; j<10; j++){
tmp_array[j] = rand();
index[j] = j;
}
// QuickSort the index array based on tmp_array:
qsort_r(index, 10, sizeof(*index), simcmp, tmp_array);
free(index);
free(tmp_array);
}
return 0;
}
编译和运行没有问题。但是,它并不完全理想,因为 qsort_r
依赖于平台和编译器。有一个 portable version of qsort_r here 作者很好地总结了我的问题:
If you want to qsort() an array with a comparison operator that takes parameters you need to use global variables to pass those parameters (not possible when writing multithreaded code), or use qsort_r/qsort_s which are not portable (there are separate GNU/BSD/Windows versions and they all take different arguments).
我知道这已经是自我回答,但这里有一些标准的 C 和 OpenMP 选项。 qsort_r
函数是一个很好的经典选择,但值得注意的是 qsort_s
是 c11 标准的一部分,因此在任何提供 c11 的地方都是可移植的(不包括 Windows,它们还不太提供 c99)。
在没有嵌套比较功能的OpenMP中,仍然使用原始的qsort,有两种方法可以想到。首先是结合 OpenMP 使用经典的全局变量 threadprivate
:
static int *index = NULL;
static double *tmp_array = NULL;
#pragma omp threadprivate(index, tmp_array)
int simcmp(const void *a, const void *b){
int ia = *(int *)a;
int ib = *(int *)b;
double aa = ((double *)tmp_array)[ia];
double bb = ((double *)tmp_array)[ib];
if ((aa - bb) > 1e-12){
return -1;
}else{
return 1;
}
}
int main(){
int i;
#pragma omp parallel for
for(i = 0; i < 100; i++){
index= (int *) malloc(sizeof(int)*10);
tmp_array = (double*) malloc(sizeof(double)*10);
int j;
for(j=0; j<10; j++){
tmp_array[j] = rand();
index[j] = j;
}
// QuickSort the index array based on tmp_array:
qsort_r(index, 10, sizeof(*index), simcmp, tmp_array);
free(index);
free(tmp_array);
}
return 0;
}
上面的版本导致并行区域中的每个线程都使用全局变量索引和 tmp_array 的私有副本,从而解决了这个问题。这可能是您可以用标准 C 和 OpenMP 编写的最便携的版本,唯一可能不兼容的平台是那些不实现线程本地内存的平台(某些微控制器等)。
如果您想避免使用全局变量并仍然具有可移植性并使用 OpenMP,那么我建议使用 C++11 和带有 lambda 的 std::sort
算法:
std::sort(index, index+10, [=](const int& a, const int& b){
if ((tmp_array[a] - tmp_array[b]) > 1e-12){
return -1;
}else{
return 1;
}
});