You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用OpenMP并行化含qsort嵌套比较函数的C语言for循环?

问题:OpenMP并行化含嵌套qsort比较函数的for循环时出现编译内部错误

我想要并行化一个包含qsort嵌套比较函数的for循环,代码如下:

#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;
        }
        // 根据tmp_array对index数组进行快速排序:
        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适配该嵌套比较函数?若不能,有无无需嵌套函数的替代实现方案?

补充说明:我使用支持嵌套函数的GCC编译器,移除#pragma语句后代码可正常编译运行。无法将simcmp定义在for循环外,否则tmp_array需设为全局变量,会破坏多线程逻辑,希望得到无需嵌套函数的实现建议。


解决方案

嘿,我来帮你解决这个问题!首先得明确:GCC的嵌套函数和OpenMP并行区域目前是没法兼容的,这就是你碰到内部编译器错误的根源。嵌套函数依赖GCC特有的栈上trampoline实现,而OpenMP的并行线程栈管理和这个机制冲突,编译器处理不了这种情况,所以没法让OpenMP适配嵌套函数,得换个思路实现。

下面给你两种无需嵌套函数的可行方案,都能避免全局变量,同时保证多线程安全:

方案1:用GCC扩展的qsort_r传递上下文

GCC提供了qsort_r(注意和POSIX版本的参数顺序不一样),它允许你把需要的上下文(比如你的tmp_array)作为额外参数传给比较函数,这样就不用嵌套函数也能访问到当前线程的数组了。

修改后的代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <omp.h>

// 比较函数,最后一个参数是我们传递的上下文(这里就是tmp_array)
int simcmp(const void *a, const void *b, void *context) {
    double *tmp_array = (double *)context;
    int ia = *(int *)a;
    int ib = *(int *)b;
    if ((tmp_array[ia] - tmp_array[ib]) > 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;
        // 注意:rand()不是线程安全的,这里换成线程安全的rand_r示例
        unsigned int seed = omp_get_thread_num();
        for(j=0; j<10; j++){
            tmp_array[j] = rand_r(&seed);
            index[j] = j;
        }
        // 调用qsort_r,把tmp_array作为上下文传入
        qsort_r(index, 10, sizeof(*index), simcmp, tmp_array);
        free(index);
        free(tmp_array);
    }
    return 0;
}

方案2:用C11标准的qsort_s(如果编译器支持)

如果你想用更标准的方式,C11引入了qsort_s,这是标准库的安全排序函数,同样支持传递上下文。它的参数顺序和GCC的qsort_r不同,上下文是第一个参数:

#include <stdio.h>
#include <stdlib.h>
#include <omp.h>
#include <stddef.h>

// 比较函数,第一个参数是上下文
int simcmp(void *context, const void *a, const void *b) {
    double *tmp_array = (double *)context;
    int ia = *(int *)a;
    int ib = *(int *)b;
    if ((tmp_array[ia] - tmp_array[ib]) > 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;
        unsigned int seed = omp_get_thread_num();
        for(j=0; j<10; j++){
            tmp_array[j] = rand_r(&seed);
            index[j] = j;
        }
        // 调用qsort_s,传入上下文
        qsort_s(index, 10, sizeof(*index), simcmp, tmp_array);
        free(index);
        free(tmp_array);
    }
    return 0;
}

注意:qsort_s是C11标准,但不是所有编译器都支持(比如旧版本GCC可能需要加-std=c11编译选项),如果你的环境支持,这是更推荐的标准方案。

额外提醒

你的原代码里用了rand(),但rand()不是线程安全的——多个线程同时调用会导致随机数重复或者异常。所以上面的示例里换成了rand_r(),每个线程用自己的种子(这里用线程ID作为种子,你也可以用其他更合适的种子)。如果是C11及以上,也可以用<stdatomic.h>或者线程本地存储的mt19937随机数生成器,安全性更好。

内容的提问来源于stack exchange,提问作者Agargara

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 09:04:15