在MATLAB中通过对32位整数的运算对固定大小的整数组合进行矢量化哈希/排序
我在MATLAB中动态创建了大量表/矩阵,它们的第一维度不同,其行表示(排序)6阶1-50范围内的整数组合 我想为每个组合分配一个唯一的值(散列、排名),以便检查相同的组合是否出现在不同的表中。不同的组合不允许分配相同的值,即没有冲突。我必须在很多这样的表格之间做很多这样的比较。因此,出于性能方面的考虑,我希望通过对在MATLAB中通过对32位整数的运算对固定大小的整数组合进行矢量化哈希/排序,matlab,hash,vectorization,gpgpu,ranking,Matlab,Hash,Vectorization,Gpgpu,Ranking,我在MATLAB中动态创建了大量表/矩阵,它们的第一维度不同,其行表示(排序)6阶1-50范围内的整数组合 我想为每个组合分配一个唯一的值(散列、排名),以便检查相同的组合是否出现在不同的表中。不同的组合不允许分配相同的值,即没有冲突。我必须在很多这样的表格之间做很多这样的比较。因此,出于性能方面的考虑,我希望通过对uint32操作进行矢量化来实现这一点,以使其适合于MATLAB中的GPU加速 到目前为止我想到的事情: 词典排序:不知道如何很好地将标准快速递归算法矢量化,唯一的选择似乎是parf
uint32
操作进行矢量化来实现这一点,以使其适合于MATLAB中的GPU加速
到目前为止我想到的事情:
parfor
it通过行,这比其他选项慢。IIRC,直接显式公式,虽然可以矢量化,但需要计算二项式,而二项式又需要log Gamma
函数,以避免巨大的阶乘+double
类型,以避免在我没有弄错的情况下发生冲突,即速度较慢,因为它是“非常数值化的”uint32
,并且肯定比其他选项慢(x_1,…,x_6)
发送到x_1+x_2*51+…+x_6*51^5
。这是我目前最快的。它很容易矢量化,但不幸的是,对于50个元素的秩6组合,仍然需要uint64
或double
,这比uint32
或单一类型操作要慢
uint32
范围内进行计算的“聪明的”内射函数,它也可以很好地矢量化(在MATLAB中)
任何帮助都将不胜感激
编辑:这里有一个例程,它可以对uint32
、单次
和双次
中的排序和搜索进行基准测试。我使用了MATLAB的gputimeit
来生成准确的结果
% testing parameters:
N = 26;
ord = 5;
% base-(N+1):
basevec_uint32 = gpuArray(repmat(uint32(N+1),1,ord).^cast(0:ord-1,'uint32'));
basevec_single = cast(basevec_uint32,'single').';
basevec_double = cast(basevec_uint32,'double').';
% highest hash value:
max_hash_value = gpuArray(cast(N-ord+1:N,'single'))*basevec_single
% benchmark GPU-accelerated base-(N+1) ranking for uint32, single, and double:
X_uint16 = randi(N,15000000,ord,'uint16','gpuArray');
X_uint32 = cast(X_uint16,'uint32');
X_single = cast(X_uint16,'single');
X_double = cast(X_uint16,'double');
Y_uint16 = randi(N,5000000,ord,'uint16','gpuArray');
Y_uint32 = cast(Y_uint16,'uint32');
Y_single = cast(Y_uint16,'single');
Y_double = cast(Y_uint16,'double');
ranking_uint32 = @() sum(bsxfun(@times,X_uint32,basevec_uint32),2,'native');
ranking_single = @() X_single*basevec_single;
ranking_double = @() X_double*basevec_double;
disp('ranking in uint32:'); gputimeit(ranking_uint32,1)
disp('ranking in single:'); gputimeit(ranking_single,1)
disp('ranking in double:'); gputimeit(ranking_double,1)
% benchmark GPU-accelerated searching in uint32, single, and double matrices:
X_uint32_ranks = myfun_uint32(); Y_uint32_ranks = sum(bsxfun(@times,Y_uint32,basevec_uint32),2,'native');
X_single_ranks = myfun_single(); Y_single_ranks = Y_single*basevec_single;
X_double_hash = myfun_double(); Y_double_ranks = Y_double*basevec_double;
search_uint32 = @() ismember(X_uint32_ranks,Y_uint32_ranks);
search_single = @() ismember(X_single_ranks,Y_single_ranks);
search_double = @() ismember(X_double_ranks,Y_double_ranks);
disp('searching in uint32:'); gputimeit(search_uint32,1)
disp('searching in single:'); gputimeit(search_single,1)
disp('searching in double:'); gputimeit(search_double,1)
我使用的是nVidia GTX 1060,它在Windows下为我提供了以下基准测试结果(但是,我不知道WDDM对gputimeit
的影响有多大):
结论:类型的选择不会影响通过ismember(预期)进行的GPU加速搜索,但是矩阵乘法的排序速度存在明显差异。在single
中的排名几乎是在double
中排名的两倍,而在uint32
中的排名几乎与在double
中的排名相同。MATLAB目前不支持GPU加速的uint32
矩阵乘法,因此我使用了一个替代函数。因此,尝试将50个6阶元素的组合编码为single
值更为合理,如果我没有弄错的话,这些值的位刚好足以容纳所有15890700个组合为single
整数
你的最后一个想法几乎有了足够的点子,所以你只需要挤出一些点子就可以了,这是因为你的订单太多了。因为整个序列都是排序的,所以每一对也都是排序的。因此,使用一个50乘50的查找表将排序后的(1,2)、(3,4)、(5,6)对映射为0-1274之间的数字
或者,如果您不想要一个表,那么可以使用相当简单的显式函数将j>=i的对(i,j)映射到线性索引。查找上三角矩阵索引或下三角矩阵索引,了解这些索引的详细信息。(这将是类似于
n*(n+1)/2-(n-i)*(n-i-1)/2+j
根据base-0或base-1索引,输入+/-1,在您的情况下,n=50,但我确信我会在即兴写入时出错。)
无论如何,一旦你有了三个数字0-1274,base-1275的想法将适合uint32
你想要什么样的性能?到目前为止,您所取得的最好成绩是什么?我提出这个问题的原因是,使用double,基本转换以每行几纳秒的速度运行,并且我不确定在没有mex文件的情况下,您如何获得比这快得多的速度。我的意思是,我可以变得“聪明”,并使用LUT进行联合字典排序,以将值保持在uint32范围内,但这要慢一个数量级(以倍频程为单位,因此MATLAB计时可能会有点不同)。@Biker:感谢您的评论,并对延迟表示歉意!最近几天一直忙于考试之类的事情。我记不清我测量的准确时间了,我必须检查一下,然后返回给你。@bicker:我添加了一个测试例程和相应的基准测试结果。我确实使用了gputimeit
来确保GPU执行时间的测量是准确的。我没有发现在uint32
中排名与在double
中排名之间有任何区别,但这也可能是因为函数不同,因为MATLAB不支持GPU上的uint32
矩阵乘法。然而,在single
和double
中的排名有着明显的区别-single
的排名速度几乎是前者的两倍。因此,寻找在单个
空间中运行的排序函数是有意义的。我以前忘记了MATLAB不支持GPU加速的uint32
矩阵乘法。非常感谢您的酷主意,并对延迟表示歉意!我需要一点时间来消化它。如果我有一些后续问题,希望你不会介意。
>> test1
max_rank_value =
gpuArray single
14327680
ranking in uint32:
ans =
0.0129
ranking in single:
ans =
0.0063
ranking in double:
ans =
0.0108
searching in uint32:
ans =
0.1572
searching in single:
ans =
0.1577
searching in double:
ans =
0.1599