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

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

本文关键字:向量 索引 方法 创建 C++ 距离      更新时间:2023-10-16

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

我想知道有一种快速的方法可以使用向量、数组或任何数据结构 + parallization 创建v。我所做的是用于循环,对于大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);
}
}

最好的方法主要取决于上下文。如果您正在寻找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>());
}

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

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)

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

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 行的部分并启动线程。线程构造函数需要一个函数及其参数,但它将始终尝试复制参数,即使函数需要引用也是如此。为了防止这种情况,您需要强制使用带有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;
}

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

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

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

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

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

考虑到这些评论,我作了适当的更正(强调(。

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

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

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

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