C 如何使用OpenMP从0-1数组中提取所有非零元素的索引?

C 如何使用OpenMP从0-1数组中提取所有非零元素的索引?,c,parallel-processing,openmp,C,Parallel Processing,Openmp,我是一个OpenMP初学者。我遇到了这样一个问题 我有一个长度为N的掩码数组M,其元素为0或1。我希望提取满足M[I]=1的所有索引I,并将它们存储到一个新数组T OpenMP可以加速此问题吗 我尝试了以下代码。但它不是有效的性能 int count = 0; #pragma omp parallel for for(int i = 0; i < N; ++i) { if(M[i] == hashtag) { int pos = 0; #pragma

我是一个OpenMP初学者。我遇到了这样一个问题

我有一个长度为
N
的掩码数组
M
,其元素为
0或1
。我希望提取满足
M[I]=1
的所有索引
I
,并将它们存储到一个新数组
T

OpenMP可以加速此问题吗

我尝试了以下代码。但它不是有效的性能

int count = 0;
#pragma omp parallel for
for(int i = 0; i < N; ++i) {
    if(M[i] == hashtag) {
        int pos = 0;
        #pragma omp critical (c1)
        pos = count++;
        T[pos] = i;
}
int count=0;
#pragma-omp并行
对于(int i=0;i
我不能100%肯定这会好得多,但您可以尝试以下方法:

int count = 0;
#pragma omp parallel for
for(int i = 0; i < N; ++i) {
    if(M[i]) {
        #pragma omp atomic
        T[count++] = i;
    }
}
通过改变稀疏矩阵
M
的稀疏程度,可以看出这一点——当我将
M
创建为一个随机数组,并测试
M[I]<0.01*RAND_MAX
(0.1%密集矩阵)时,事情的运行速度要比我将其设置为10%密集时快得多——这表明
临界
部分中的部分确实在减慢我们的速度

在这种情况下,我认为在OMP中没有办法加快这项任务的速度——在最后将所有线程的输出合并到一个列表中的工作只会消耗掉您可能拥有的任何速度优势,因为内部循环中发生的事情很少。因此,与其使用多个线程,我建议您重写他尽可能高效地循环-例如:

for( i = 0; i < N; i++) {
  T[count] = i;
  count += M[i];
}
(i=0;i{ T[count]=i; 计数+=M[i]; }
在我的快速基准测试中,这比OMP解决方案快-与
threads=1
解决方案相当。同样,这是因为这里访问内存的方式。请注意,我避免使用
if
语句-这将尽可能快地保存代码。相反,我利用了
M[I]
始终为零或一。在循环结束时,您必须丢弃元素
T[count]
,因为它将无效……好元素是
T[0]…T[count-1]
。在我的机器上,一个包含10M个元素的数组
M
在大约0.02秒的时间内被这个循环处理。对于大多数目的来说应该足够了吗?

关键操作应该是
原子的
,而不是
关键的
;实际上,在您的情况下,您必须使用
原子捕获
子句:

int pos, count = 0;                     // pos declared outside the loop
#pragma omp parallel for private(pos)   // and privatized, count is implicitly
for(int i = 0; i < N; ++i) {            // shared by all the threads
    if(M[i]) {
        #pragma omp atomic capture
            pos = count++;
        T[pos] = i;
    }
}
int pos,count=0;//在循环外部声明的pos
#pragma omp parallel for private(pos)//和privative,count是隐式的
对于所有线程共享的(inti=0;i

看一看,全面了解使用OpenMP进行
原子操作的所有可能性。

基于Floris的快速功能,我试图看看是否可以找到一种使用OpenMP的更快解决方案。我提出了两个功能
foo_v2
foo_v3
,它们对于更大的阵列更快,
foo_v2
与密度无关,速度更快,而
foo_v3
对于稀疏数组速度更快。函数
foo_v2
基本上创建了一个具有宽度
N*nthreads
的2D数组以及一个包含每个线程计数的数组
countsa
。这可以用代码更好地解释。下面的代码将d循环所有写入T的元素

for(int ithread=0; ithread<nthreads; ithread++) {
    for(int i=0; i<counta[ithread]; i++) {
        T[ithread*N/nthread + i]
    } 
}

我尝试了你的代码,因为它看起来像是一个很好的同步示例,但它没有使用VS'12和GCC 4.7编译,编译器错误消息是:“#pragma omp atomic”后面的
表达式的形式不正确
!你有什么想法吗?是的-
原子
需要使用GCC 4.7进行
+=
。当我开始运行代码时,我发现myself。请参阅更新的答案。+1,还发现在MSDN上,实际上允许的操作调用了一点Win32 API(InterlockedIncrement等)。谢谢,我认为原子的问题在于(每次运行的计数不同)是因为原子只是赋值,而不是计数的增量。根据OpenMP规范,您应该使用
#pragma omp atomic capture\n{结构化块} @ eSeSuCu谢谢指针。我担心它不会解决内存访问冲突的问题,但至少它应该给出正确答案…@ VLADIMIF:“代码>捕获/<代码>子句在OpenMP 3.1中被引入;您需要一个兼容编译器:V4.7以来的GCC,Vx1以来的英特尔C++编译器,Oracle Solaris Studioo从v12.3开始的编译器,从v12.1开始的IBM XL C/C++(或更早版本,找不到任何更早的文档),可能是除MSVC编译器(包括2013预览版)之外的大多数其他编译器,它们不支持高于2.0的OpenMP版本(2000年发布)…酷-我要玩这个!
for(int ithread=0; ithread<nthreads; ithread++) {
    for(int i=0; i<counta[ithread]; i++) {
        T[ithread*N/nthread + i]
    } 
}
#include <stdio.h>
#include <stdlib.h>
#include <omp.h>

int foo_v1(int *M, int *T, const int N) {   
    int count = 0;
    for(int i = 0; i<N; i++) {
        T[count] = i;
        count += M[i];
    }
    return count;
}

int foo_v2(int *M, int *T, int *&counta, const int N) {
    int nthreads;
    #pragma omp parallel
    {
        nthreads = omp_get_num_threads();
        const int ithread = omp_get_thread_num();   
        #pragma omp single
        counta = new int[nthreads];
        int count_private = 0;      
        #pragma omp for
        for(int i = 0; i<N; i++) {
            T[ithread*N/nthreads + count_private] = i;
            count_private += M[i];
        }
        counta[ithread] = count_private;
    }
    return nthreads;
}

int foo_v3(int *M, int *T, const int N) {
    int count = 0;

    int *counta = 0;    
    #pragma omp parallel reduction(+:count)
    {
        const int nthreads = omp_get_num_threads();
        const int ithread = omp_get_thread_num();   
        #pragma omp single
        {
            counta = new int[nthreads+1];
            counta[0] = 0;
        }
        int *Tprivate = new int[N/nthreads];

        int count_private = 0;      
        #pragma omp for nowait
        for(int i = 0; i<N; i++) {
            Tprivate[count_private] = i;
            count_private += M[i];
        }
        counta[ithread+1] = count_private;
        count += count_private;

        #pragma omp barrier
        int offset = 0;
        for(int i=0; i<(ithread+1); i++) {
            offset += counta[i];
        }

        for(int i=0; i<count_private; i++) {
            T[offset + i] = Tprivate[i];
        }

        delete[] Tprivate;
    }
    delete[] counta;
    return count;
}

void compare(const int *T1, const int *T2, const int N, const int count, const int *counta, const int nthreads) {
    int diff = 0;
    int n = 0;
    for(int ithread=0; ithread<nthreads; ithread++) {
        for(int i=0; i<counta[ithread]; i++) {
            int i2 = N*ithread/nthreads+i;
            //printf("%d %d\n", T1[n], T2[i2]);
            int tmp = T1[n++] - T2[i2];
            if(tmp<0) tmp*=-1;
            diff += tmp;
        }
    }
    printf("diff %d\n", diff);
}

void compare_v2(const int *T1, const int *T2, const int count) {
    int diff = 0;
    int n = 0;
    for(int i=0; i<count; i++) {
        int tmp = T1[i] - T2[i];
        //if(tmp!=0) printf("%i %d %d\n", i, T1[i], T2[i]);
        if(tmp<0) tmp*=-1;
        diff += tmp;
    }
    printf("diff %d\n", diff);
}

int main() {
    const int N = 1 << 26;

    printf("%f MB\n", 4.0*N/1024/1024);
    int *M = new int[N];
    int *T1 = new int[N];
    int *T2 = new int[N];
    int *T3 = new int[N];

    int *counta;
    double dtime;
    for(int i=0; i<N; i++) {
        M[i] = ((rand()%10)==0);
    }

    //int repeat = 10000;
    int repeat = 1;
    int count1, count2;
    int nthreads;
    dtime = omp_get_wtime();
    for(int i=0; i<repeat; i++) count1 = foo_v1(M, T1, N);
    dtime = omp_get_wtime() - dtime;
    printf("time v1 %f\n", dtime);

    dtime = omp_get_wtime();
    for(int i=0; i<repeat; i++) nthreads = foo_v2(M, T2, counta, N);
    dtime = omp_get_wtime() - dtime;
    printf("time v2 %f\n", dtime);
    compare(T1, T2, N, count1, counta, nthreads);

    dtime = omp_get_wtime();
    for(int i=0; i<repeat; i++) count2 = foo_v3(M, T3, N);
    dtime = omp_get_wtime() - dtime;
    printf("time v2 %f\n", dtime);
    printf("count1 %d, count2 %d\n", count1, count2);
    compare_v2(T1, T3, count1); 

}