c++ 用openMP实现最小元素索引的并行计算

knsnq2tg  于 2023-02-14  发布在  其他
关注(0)|答案(4)|浏览(220)

我试着写这个代码

float* theArray; // the array to find the minimum value
int   index, i;
float thisValue, min;

index    = 0;
min = theArray[0];
#pragma omp parallel for reduction(min:min_dist)
for (i=1; i<size; i++) {
    thisValue = theArray[i];
    if (thisValue < min)

    { /* find the min and its array index */

        min = thisValue;

        index    = i;
    }
}
return(index);

然而,这一个没有输出正确的答案。似乎最小值是可以的,但正确的索引已被线程破坏。
我也尝试了一些在互联网上和这里提供的方法(使用parallel for for外部循环,使用critical进行最终比较),但这会导致速度下降而不是加速。
我应该怎么做才能使最小值和它的索引都正确呢?谢谢!

uqdfh47h

uqdfh47h1#

我不知道有什么优雅的方法想要做一个最小值缩减并保存一个索引,我是通过找到每个线程的局部最小值和索引,然后在临界区找到全局最小值和索引来实现的。

index = 0;
min = theArray[0];
#pragma omp parallel
{
    int index_local = index;
    float min_local = min;  
    #pragma omp for nowait
    for (i = 1; i < size; i++) {        
        if (theArray[i] < min_local) {
            min_local = theArray[i];
            index_local = i;
        }
    }
    #pragma omp critical 
    {
        if (min_local < min) {
            min = min_local;
            index = index_local;
        }
    }
}

在OpenMP 4.0中,可以使用用户定义的约简。用户定义的最小约简可以如下定义

struct Compare { float val; sizt_t index; };    
#pragma omp declare reduction(minimum : struct Compare : omp_out = omp_in.val < omp_out.val ? omp_in : omp_out)

然后可以像这样进行归约

struct Compare min; 
min.val = theArray[0]; 
min.index = 0;
#pragma omp parallel for reduction(minimum:min)
for(int i = 1; i<size; i++) {
    if(theArray[i]<min.val) { 
        min.val = a[i];
        min.index = i;
    }
}

这适用于C和C++。用户定义的约简除了简化代码外还有其他优势。有多种算法可用于约简。例如,合并可以在O(number of threads)O(Log(number of threads)中完成。我给出的第一个解决方案在O(number of threads)中完成此操作,但使用用户定义的约简时,让OpenMP来选择算法。

elcex8rz

elcex8rz2#

    • 基本理念**

这可以通过创建custom reduction来完成,而无需任何对criticalatomic节的并行化破坏。基本上,定义一个同时存储索引和值的对象,然后创建一个函数,仅根据值而不是索引对其中两个对象进行排序。

    • 详细信息**

将索引和值存储在一起的对象:

typedef std::pair<unsigned int, float> IndexValuePair;

您可以通过访问first属性来访问索引,通过访问second属性来访问值,即:

IndexValuePair obj(0, 2.345);
unsigned int ix = obj.first;  // 0
float val = obj.second; // 2.345

定义一个函数以对两个IndexValuePair对象进行排序:

IndexValuePair myMin(IndexValuePair a, IndexValuePair b){
    return a.second < b.second ? a : b;
}

然后,按照OpenMP documentation中的指导原则构建自定义缩减:

#pragma omp declare reduction \
(minPair:IndexValuePair:omp_out=myMin(omp_out, omp_in)) \
initializer(omp_priv = IndexValuePair(0, 1000))

在本例中,我选择将索引初始化为0,将值初始化为1000。值应该初始化为大于您期望排序的最大值的某个数字。

    • 功能示例**

最后,用parallel for循环组合所有这些片段!

// Compile with g++ -std=c++11 -fopenmp demo.cpp
#include <iostream>
#include <utility>
#include <vector>

typedef std::pair<unsigned int, float> IndexValuePair;

IndexValuePair myMin(IndexValuePair a, IndexValuePair b){
    return a.second < b.second ? a : b;
}

int main(){

    std::vector<float> vals {10, 4, 6, 2, 8, 0, -1, 2, 3, 4, 4, 8};
    unsigned int i;

    IndexValuePair minValueIndex(0, 1000);

    #pragma omp declare reduction \
        (minPair:IndexValuePair:omp_out=myMin(omp_out, omp_in)) \
        initializer(omp_priv = IndexValuePair(0, 1000))

    #pragma omp parallel for reduction(minPair:minValueIndex)
    for(i = 0; i < vals.size(); i++){

        if(vals[i] < minValueIndex.second){
            minValueIndex.first = i;
            minValueIndex.second = vals[i];
        }
    }

    std::cout << "minimum value = " << minValueIndex.second << std::endl;   // Should be -1
    std::cout << "index = " << minValueIndex.first << std::endl;    // Should be 6

    return EXIT_SUCCESS;

}
k2fxgqgv

k2fxgqgv3#

因为你不仅要找到最小值(reduction(min:___)),但同时保留索引,则需要使检查成为关键检查。这会显著降低循环速度(如报告所述)一般而言,请确保有足够的工作量,这样您就不会遇到this问题中的开销。另一种方法是让每个线程找到最小值,并将其s索引并将它们保存到一个唯一的变量中,然后让主线程对它们进行最后的检查,如下面的程序所示。

#include <iostream>
#include <vector>
#include <ctime>
#include <random>
#include <omp.h>

using std::cout;
using std::vector;

void initializeVector(vector<double>& v)
{
    std::mt19937 generator(time(NULL));
    std::uniform_real_distribution<double> dis(0.0, 1.0);
    v.resize(100000000);
    for(int i = 0; i < v.size(); i++)
    {
        v[i] = dis(generator);
    }
}

int main()
{
    vector<double> vec;
    initializeVector(vec);

    float minVal = vec[0];
    int minInd = 0;

    int startTime = clock();

    for(int i = 1; i < vec.size(); i++)
    {
        if(vec[i] < minVal)
        {
            minVal = vec[i];
            minInd = i;
        }

    }

    int elapsedTime1 = clock() - startTime;

    // Change the number of threads accordingly
    vector<float> threadRes(4, std::numeric_limits<float>::max());
    vector<int>   threadInd(4);

    startTime = clock();
#pragma omp parallel for
    for(int i = 0; i < vec.size(); i++)
    {
        {
            if(vec[i] < threadRes[omp_get_thread_num()])
            {
                threadRes[omp_get_thread_num()] = vec[i];
                threadInd[omp_get_thread_num()] = i;
            }
        }

    }

    float minVal2 = threadRes[0];
    int minInd2 = threadInd[0];

    for(int i = 1; i < threadRes.size(); i++)
    {
        if(threadRes[i] < minVal2)
        {
            minVal2 = threadRes[i];
            minInd2 = threadInd[i];
        }
    }

    int elapsedTime2 = clock() - startTime;

    cout << "Min " << minVal << " at " << minInd << " took " << elapsedTime1 << std::endl;
    cout << "Min " << minVal2 << " at " << minInd2 << " took " << elapsedTime2 << std::endl;
}

请注意,如果优化处于打开状态,并且循环中没有其他操作,那么串行版本似乎仍然是王者。如果优化处于关闭状态,那么OMP将占据上风。
你写了reduction(min:min_dist),然后用min代替min_dist

cwtwac6a

cwtwac6a4#

实际上,我们可以使用omp critical指令,让一个线程同时运行临界区中的代码,这样只有一个线程可以运行它,并且索引值不会被其他线程破坏。
关于omp关键指令:
omp critical指令标识一段代码,一次必须由一个线程执行。
此代码可解决您的问题:

#include <stdio.h>
#include <omp.h>
int main() {
int i;
int arr[10] = {11,42,53,64,55,46,47, 68, 59, 510};

float* theArray; // the array to find the minimum value
int   index;
float thisValue, min;
index    = 0;
min = arr[0];
int size=10;
#pragma omp parallel for
for (i=1; i<size; i++) {
    thisValue = arr[i];
    #pragma omp critical
    if (thisValue < min)

    { /* find the min and its array index */

        min = thisValue;

        index    = i;
    }
}
printf("min:%d index:%d",min,index);
return 0;
}

相关问题