温馨提示:本文翻译自stackoverflow.com,查看原文请点击:c++ - How to parallelize nearest neighbour search using OpenMP
c++ multithreading openmp optimization nearest-neighbor

c++ - 如何使用OpenMP并行化最近邻居搜索

发布于 2020-04-09 10:50:47

基本上,我有一个集合std::vector<std::pair<std::vector<float>, unsigned int>>,其中包含std::vector<float>大小为512(2048 bytes的模板及其相应的identifier unsigned int

我正在编写一个提供模板的函数,我需要返回集合中最相似模板的标识符。我正在使用点积计算相似度。

我的幼稚实现如下所示:

// Should return false if no match is found (ie. similarity is 0 for all templates in collection)
bool identify(const float* data, unsigned int length, unsigned int& label, float& similarity) {
    bool found = false;
    similarity = 0.f;

    for (size_t i = 0; i < collection.size(); ++i) {
        const float* candidateTemplate = collection[i].first.data();
        float consinSimilarity = getSimilarity(data, candidateTemplate, length); // computes cosin sim between two vectors, implementation depends on architecture. 

        if (consinSimilarity > similarity) {
            found = true;
            similarity = consinSimilarity;
            label = collection[i].second;
        }
    }

    return found;
}

我如何使用并行化来加快速度。我的收藏可能包含数百万个模板。我读过您可以添加,#pragma omp parallel for reduction但是我不确定如何使用它(即使这是最好的选择)。

另请注意:对于我的点产品实现,如果基本体系结构支持AVX&FMA,则我正在使用实现。由于SIMD寄存器数量有限,在并行化时这会影响性能吗?

查看更多

提问者
cyrusbehr
被浏览
62
Qubit 2020-04-06 21:51

由于我们无法访问实际编译的示例(本来不错),因此我实际上没有尝试编译以下示例。尽管如此,除了一些小的错别字外,总体思路应该很清楚。

我们的任务是找到相似度的最大值和相应的标签,为此,我们确实可以使用reduction,但是由于我们需要找到一个最大值,然后存储相应的标签,因此我们使用一对来存储两个值立即将其实现为reductionOpenMP中的一个。

我已经稍微重写了您的代码,temp使用变量的原始命名(可能会使阅读起来有些困难基本上,我们并行执行搜索,因此每个线程都找到一个最佳值,然后要求OpenMP在线程(reduction之间找到最佳解决方案,然后完成。

//Reduce by finding the maximum and also storing the corresponding label, this is why we use a std::pair. 
void reduce_custom (std::pair<float, unsigned int>& output, std::pair<float, unsigned int>& input) {
    if (input.first > output.first) output = input;
}
//Declare an OpenMP reduction with our pair and our custom reduction function. 
#pragma omp declare reduction(custom_reduction : \
    std::pair<float, unsigned int>: \
    reduce_custom(omp_out, omp_in)) \
    initializer(omp_priv(omp_orig))

bool identify(const float* data, unsigned int length, unsigned int& label, float& similarity) {
    std::pair<float, unsigned int> temp(0.0, label); //Stores thread local similarity and corresponding best label. 

#pragma omp parallel for reduction(custom_reduction:temp)
    for (size_t i = 0; i < collection.size(); ++i) {
        const float* candidateTemplate = collection[i].first.data(); 
        float consinSimilarity = getSimilarity(data, candidateTemplate, length);

        if (consinSimilarity > temp.first) {
            temp.first = consinSimilarity;
            temp.second = collection[i].second;
        }
    }

    if (temp.first > 0.f) {
        similarity = temp.first;
        label = temp.second;
        return true;
    }

    return false;
}

关于SIMD寄存器数量有限的问题,它们的数量取决于您使用的特定CPU。据我所知,每个内核都有一定数量的向量寄存器可用,因此,只要您使用的寄存器不多于现在可用的数量,就可以了,此外,例如AVX512还提供32个向量寄存器和2个向量寄存器。用于每个核的向量运算的算术单位,因此用尽计算资源并非易事,由于内存局部性较差,您更有可能遭受苦难(尤其是在向量被各处保存的情况下)。我当然可能是错的,如果是这样,请随时在评论中纠正我。