繁体   English   中英

从 C++ 中的距离矩阵创建索引向量的最快方法

[英]Fastest way to create a vector of indices from distance matrix in C++

我有一个大小为n x n的距离矩阵D和一个常数L作为输入。 我需要创建一个向量v包含D中的所有条目,使其值最多为L 这里v必须按特定顺序v = [v1 v2.. vn]其中vi包含D的第i行中的条目,其值最多为L 每个vi中的条目顺序并不重要。

我想知道有没有一种使用向量、数组或任何数据结构 + 并行化创建v的快速方法。 我所做的是使用 for 循环,对于大n来说非常慢。

vector<int> v;
for (int i=0; i < n; ++i){
    for (int j=0; j < n; ++j){
        if (D(i,j) <= L) v.push_back(j);
    }
}

考虑到评论,我做了适当的更正(强调)。

您是否搜索过编写性能代码、线程、asm 指令(如果您的程序集不是您想要的)和用于并行处理的 OpenCL 的技巧? 如果没有,我强烈推荐!

某些情况下,在 for 循环之外声明所有 for 循环变量(以避免多次声明它们)会使其更快,但在这种情况下不会(来自我们的朋友 Paddy 的评论)

此外,使用new插入的vector可以更快,正如我们在此处看到的: 在 C++ 中使用 arrays 或 std::vectors,性能差距是什么? -我测试了,使用vector比使用new慢 6 秒,只需要 1 秒。 我猜想当有人在搜索性能时,不需要 std::vector 带来的安全性和易于管理的保证,即使因为使用new并不是那么困难,只要避免计算的堆溢出并记住使用delete[]

user4581301 在这里是正确的,下面的说法是不正确的:最后,如果你在一个数组而不是矩阵中构建D (或者如果你将D复制到一个常量数组中,也许......),它将会更加缓存友好并将保存一个 for 循环语句。

最好的方法主要取决于上下文。 如果您正在寻找 GPU 并行化,您应该看看 OpenCL。

对于基于 CPU 的并行化,C++ 标准#include <thread>库可能是您最好的选择,但您需要小心:

  • 创建线程需要时间,因此如果 n 相对较小(<1000 左右),它会减慢您的速度
  • D(i,j) 必须同时被多个线程读取
  • v 必须可由多个线程写入,标准向量不会削减它

v 可能是以 vi 作为其子向量的 2d 向量,但必须在并行化之前对其进行初始化:

std::vector<std::vector<int>> v; 
v.reserve(n);                    
for(size_t i = 0; i < n; i++)
{
    v.push_back(std::vector<int>());
}

您需要决定要使用多少线程。 如果这仅适用于一台机器,硬编码是一个有效的选项。 线程库中有一个 function 可以获取支持的线程数量,但它更多的是提示而不是值得信赖。

size_t threadAmount = std::thread::hardware_concurrency(); //How many threads should run hardware_concurrency() gives you a hint, but its not optimal
std::vector<std::thread> t;                                //to store the threads in
t.reserve(threadAmount-1);                                 //you need threadAmount-1 extra threads (we already have the main-thread)

要启动一个线程,您需要一个可以执行的 function。 在这种情况下,这是通读矩阵的一部分。

void CheckPart(size_t start, size_t amount, int L, std::vector<std::vector<int>>& vec)
{
    for(size_t i = start; i < amount+start; i++)
    {
        for(size_t j = 0; j < n; j++)
        {
            if(D(i,j) <= L)
            {
                vec[i].push_back(j);
            }
        }
    }
}

现在您需要将矩阵拆分为大约 n/threadAmount 行的部分并启动线程。 线程构造函数需要一个 function 及其参数,但它总是会尝试复制参数,即使 function 需要一个引用。 为了防止这种情况,您需要强制使用带有std::ref()的引用

int i = 0;
int rows;
for(size_t a = 0; a < threadAmount-1; a++)
{
    rows = n/threadAmount + ((n%threadAmount>a)?1:0);
    t.push_back(std::thread(CheckPart, i, rows, L, std::ref(v)));
    i += rows;
}

线程现在正在运行,所要做的就是运行主 function 上的最后一个块:

SortPart(i, n/threadAmount, L, v);

之后,您需要等待线程完成并清理它们:

for(unsigned int a = 0; a < threadAmount-1; a++)
{
    if(t[a].joinable())
    {
        t[a].join();
    }
}

请注意,这只是一个快速而肮脏的例子。 不同的问题可能需要不同的实现,而且由于我无法猜测上下文,因此我可以提供的帮助非常有限。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM