在CUDA中进行3个整数键查找

6

我希望能在大约一百万个数据点中查找3个整数(例如[1 2 3])。

我目前正在使用MATLAB的Map(哈希表),对于每个数据点,我正在执行以下操作:

key = sprintf('%d ', [1 2 3]);       % 23 us
% key = '1 2 3 '
result = lookup_map( key );          % 32 us

这很费时间——100万个点 * 55微秒 = 55秒。
我想使用CUDA将其移至GPU上,但我不确定最佳方法是什么。
我可以传输四个数组——key1、key2、key3、result,然后对键执行二进制搜索,但这需要每个键20次迭代(2^20 = 1048576)。然后我还会由于每个线程的并发内存访问而产生延迟。
在CUDA中是否有一种针对并行多键查找进行优化的数据结构(O(1),最好)?
边界问题:这三个整数的范围是多少?查找了什么数据?
整数键当前可以在0到约75,000之间,但在未来可能更大(200,000+)。
对于这个问题,我们可以假设结果是介于0和数据集大小之间的整数。
为什么不将三个数字打包成一个64位数字(每个数字21位,给你一个范围0-2,097,152),然后用它来索引稀疏数组呢?
>> A = uint64(ones(10));
>> sparse_A = sparse(A)
??? Undefined function or method 'sparse' for input arguments of type 'uint64'.

>> A = int64(ones(10));
>> sparse_A = sparse(A)
??? Undefined function or method 'sparse' for input arguments of type 'int64'.

看起来我的Matlab不支持64位数字的稀疏数组。

如果对其他人有帮助的话,我写了一个快速函数,可以从三个小于2^21的无符号整数创建一个64位键:

function [key] = to_key(face)
    key = uint64(bitsll(face(1), 42) + bitsll(face(2), 21) + rand(face(3),1));
end

问:为什么不使用逻辑索引?

让我们来测试一下!

% Generate a million random integers between 0 and 1000
>> M = int32(floor(rand(10000000,4)*1000));
% Find a point to look for
>> search =  M(500000,1:3)             
search =
         850         910         581  
>> tic; idx = M(:,1)==search(1) & M(:,2)==search(2)&M(:,3)==search(3); toc;
Elapsed time is 0.089801 seconds.
>> M(idx,:)
ans =
         850         910         581         726

很遗憾,这需要89801微秒,比我的现有解决方案(55微秒)慢1632倍!如果运行一百万次,需要2.5小时!

我们可以尝试在每次搜索后过滤M

>> tic; idx1=M(:,1)==search(1); N=M(idx1,:); idx2=N(:,2)==search(2); N2=N(idx2,:); idx3 = N2(:,3)==search(3); toc;
Elapsed time is 0.038272 seconds.

这个速度稍快,但仍比使用Map慢696倍。


我考虑了一些更多的内容,决定从单个键值查找重新生成一些数据的速度进行分析 - 相对于3个键值查找,这可能会更快,鉴于此方法存在的潜在问题。


1
顺便说一句,我希望NVIDIA论坛仍然可以访问 - 那里有很多有用的信息。 - Alex L
@SkylerSaleh 感谢您的关注 - 我已经在我的问题中添加了更多信息。 - Alex L
为什么不将这三个数字打包成一个64位数字(每个数字21位,范围为0-2,097,152),然后使用它作为稀疏数组的索引呢? - Skyler Saleh
可能是因为我在32位操作系统上使用32位MATLAB。 - Alex L
如果你找到了第一个键,最坏情况下需要大约20次迭代,但是如果你在这个搜索结果上进行过滤,剩下的两个键应该会更快地被找到。 - Dennis Jaheruddin
显示剩余3条评论
2个回答

2
我猜测这个问题与您之前关于四面体面的 问题 有关。我仍然建议您尝试使用稀疏存储和稀疏矩阵向量乘法来解决这个问题。
size(spA)
ans =

 1244810     1244810

tic;
vv = spA*v;
idx = find(vv);
toc;

Elapsed time is 0.106581 seconds.

这只是时间分析,参见我之前的回答了解如何在你的情况下实现它。在你转向CUDA并进行复杂的操作之前,请先查看简单的选项。

哈哈,我有一个追随者了!我不再使用稀疏矩阵的原因是我的计算机超过了最大整数大小(2.1475e+009)。如果我有一个75,000个四面体网格,因此有75,000 * 4个面,v = sparse(ntetras * nfaces,1)会抛出错误“稀疏矩阵大小必须是计算机定义的MAXSIZE以下的非负整数”。 - Alex L
我想问一下,如果 ntetras*nfaces<=2.1475e+009,并且 nfaces=4*ntetras,那么 ntetras <= sqrt(2.1475e+009/4) ~ 2317,我的理解是正确的吗? - Alex L
@AlexL,与您的问题规模无关。Matlab的sparse函数具有long索引,并且可以处理大小为2^64的系统。而且,75000*4=300000...这是一个小问题。可能您还有其他问题。 - angainor
抱歉,我觉得我误解了你之前的回答。所以 v 只用于查找,而不是存储所有数据? - Alex L
@AlexL 是的,那就是你要搜索的索引。稀疏矩阵也不是很大。O(四面体数*每个四面体的面数)。 - angainor

0
鉴于这个问题已经引起了足够的关注,感觉这个答案可能过于简单了,但为什么不就像这样做呢:
M=[1:6; 2:7; 3:8; 4:9]'; %Some matrix that contains key 1 2 3, corresponding value is 4
idx=M(:,1)==1&M(:,2)==2&M(:,3)==3;
M(idx,4)

即使 M 是一百万乘以四,这个应该很快计算出来。


谢谢你的回答 - 我已经测试过了,它运行的速度比我的现有解决方案慢了大约1600倍。如果你感兴趣,我已经在我的问题中更新了我的结果! - Alex L
1
问题在于 M(:,1)==1 首先创建了一个仅包含第一列的新数组,然后比较了这个新数组的每个值(即使找到结果也不会停止)。这意味着您的方法将循环 300 万次,还必须创建三个新数组! - Alex L
抱歉,我误解了你的问题。我假设你想在一百万个点中找到一个点。现在我想问一下,你是想找到一百万个点(可以多次出现)?还是例如每个点只出现一次?如果是后者,我会考虑某种排序方法。 - Dennis Jaheruddin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接