在MATLAB中通过对32位整数的运算对固定大小的整数组合进行矢量化哈希/排序

在MATLAB中通过对32位整数的运算对固定大小的整数组合进行矢量化哈希/排序,matlab,hash,vectorization,gpgpu,ranking,Matlab,Hash,Vectorization,Gpgpu,Ranking,我在MATLAB中动态创建了大量表/矩阵,它们的第一维度不同,其行表示(排序)6阶1-50范围内的整数组合 我想为每个组合分配一个唯一的值(散列、排名),以便检查相同的组合是否出现在不同的表中。不同的组合不允许分配相同的值,即没有冲突。我必须在很多这样的表格之间做很多这样的比较。因此,出于性能方面的考虑,我希望通过对uint32操作进行矢量化来实现这一点,以使其适合于MATLAB中的GPU加速 到目前为止我想到的事情: 词典排序:不知道如何很好地将标准快速递归算法矢量化,唯一的选择似乎是parf

我在MATLAB中动态创建了大量表/矩阵,它们的第一维度不同,其行表示(排序)6阶1-50范围内的整数组合

我想为每个组合分配一个唯一的值(散列、排名),以便检查相同的组合是否出现在不同的表中。不同的组合不允许分配相同的值,即没有冲突。我必须在很多这样的表格之间做很多这样的比较。因此,出于性能方面的考虑,我希望通过对
uint32
操作进行矢量化来实现这一点,以使其适合于MATLAB中的GPU加速

到目前为止我想到的事情:

  • 词典排序:不知道如何很好地将标准快速递归算法矢量化,唯一的选择似乎是
    parfor
    it通过行,这比其他选项慢。IIRC,直接显式公式,虽然可以矢量化,但需要计算二项式,而二项式又需要
    log Gamma
    函数,以避免巨大的阶乘+
    double
    类型,以避免在我没有弄错的情况下发生冲突,即速度较慢,因为它是“非常数值化的”
  • Cantor配对函数:可以连续应用Cantor配对,这很好,因为它是一个多项式表达式,但它产生的巨大数字远远超过了
    uint32
    ,并且肯定比其他选项慢
  • 基51(无双关)整数:将组合/行向量
    (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