Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/143.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/asp.net-core/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 使用SIMD指令的并行二项式系数_C++_Simd_Intrinsics_Avx_Binomial Coefficients - Fatal编程技术网

C++ 使用SIMD指令的并行二项式系数

C++ 使用SIMD指令的并行二项式系数,c++,simd,intrinsics,avx,binomial-coefficients,C++,Simd,Intrinsics,Avx,Binomial Coefficients,背景 我最近一直在使用一些旧代码(~1998年)并重新编写一些代码以提高性能。以前在状态的基本数据结构中,我将元素存储在几个数组中,现在我使用原始位(对于需要少于64位的情况)。也就是说,以前我有一个b元素数组,现在我在一个64位整数中设置了b位,以指示该值是否是我状态的一部分 使用诸如\u pext\u u64和\u pdep\u u64之类的内部函数,我成功地将所有操作的速度提高了5-10倍。我正在做最后一个操作,它与计算一个完美的散列函数有关 哈希函数的确切细节并不太重要,但它可以归结为计

背景

我最近一直在使用一些旧代码(~1998年)并重新编写一些代码以提高性能。以前在状态的基本数据结构中,我将元素存储在几个数组中,现在我使用原始位(对于需要少于64位的情况)。也就是说,以前我有一个
b
元素数组,现在我在一个64位整数中设置了
b
位,以指示该值是否是我状态的一部分

使用诸如
\u pext\u u64
\u pdep\u u64
之类的内部函数,我成功地将所有操作的速度提高了5-10倍。我正在做最后一个操作,它与计算一个完美的散列函数有关

哈希函数的确切细节并不太重要,但它可以归结为计算二项式系数(
n选择k
-
n!/((n-k)!k!)
用于各种
n
k
。我当前的代码为此使用了一个大的查找表,它本身可能很难显著加快速度(表中可能存在的缓存未命中除外,我尚未测量)

但是,我在想,使用SIMD指令,我可能能够直接并行计算多个状态的这些值,从而看到整体性能的提升

一些制约因素:

  • 在每个64位状态(表示小数字)中,始终精确地设置了
    b

  • 二项式系数中的
    k
    值与
    b
    相关,并且在计算中变化均匀。这些值很小(大多数情况下这里有一种可能的解决方案,一次使用一种状态从查找表进行计算。在多个状态上并行进行计算可能比使用单一状态更有效。注意:这是硬编码的,用于获得6个元素组合的固定情况

    int64_t GetPerfectHash2(State &s)
    {
        // 6 values will be used
        __m256i offsetsm1 = _mm256_setr_epi32(6*boardSize-1,5*boardSize-1,
                                              4*boardSize-1,3*boardSize-1,
                                              2*boardSize-1,1*boardSize-1,0,0);
        __m256i offsetsm2 = _mm256_setr_epi32(6*boardSize-2,5*boardSize-2,
                                              4*boardSize-2,3*boardSize-2,
                                              2*boardSize-2,1*boardSize-2,0,0);
        int32_t index[9];
        uint64_t value = _pext_u64(s.index2, ~s.index1);
        index[0] = boardSize-numItemsSet+1;
        for (int x = 1; x < 7; x++)
        {
            index[x] = boardSize-numItemsSet-_tzcnt_u64(value);
            value = _blsr_u64(value);
        }
        index[8] = index[7] = 0;
    
        // Load values and get index in table
        __m256i firstLookup = _mm256_add_epi32(_mm256_loadu_si256((const __m256i*)&index[0]), offsetsm2);
        __m256i secondLookup = _mm256_add_epi32(_mm256_loadu_si256((const __m256i*)&index[1]), offsetsm1);
        // Lookup in table
        __m256i values1 = _mm256_i32gather_epi32(combinations, firstLookup, 4);
        __m256i values2 = _mm256_i32gather_epi32(combinations, secondLookup, 4);
        // Subtract the terms
        __m256i finalValues = _mm256_sub_epi32(values1, values2);
        _mm256_storeu_si256((__m256i*)index, finalValues);
    
        // Extract out final sum
        int64_t result = 0;
        for (int x = 0; x < 6; x++)
        {
            result += index[x];
        }
        return result;  
    }
    
    int64\t GetPerfectHash2(州和州)
    {
    //将使用6个值
    __m256i偏移量M1=_mm256_setr_epi32(6*boardSize-1,5*boardSize-1,
    4*boardSize-1,3*boardSize-1,
    2*boardSize-1,1*boardSize-1,0,0);
    __m256i偏移量M2=_mm256_setr_epi32(6*boardSize-2,5*boardSize-2,
    4*boardSize-2,3*boardSize-2,
    2*boardSize-2,1*boardSize-2,0,0);
    int32_t指数[9];
    uint64_t值=_pext_u64(s.index2,~s.index1);
    索引[0]=boardSize NumitemSet+1;
    对于(int x=1;x<7;x++)
    {
    索引[x]=板尺寸NUMITEMSET-tzcnt_64(值);
    值=_blsr_u64(值);
    }
    指数[8]=指数[7]=0;
    //在表中加载值并获取索引
    __m256i firstLookup=\u mm256\u add\u epi32(\u mm256\u loadu\u si256((常数m256i*)和索引[0]),偏移量m2);
    __m256i secondLookup=\u mm256\u add\u epi32(\u mm256\u loadu\u si256((const\u m256i*)和index[1]),偏移量为1;
    //在表中查找
    __m256i值1=_mm256_i32gather_epi32(组合,第一次查找,4);
    __m256i值2=_mm256_i32gather_epi32(组合,二次查找,4);
    //减去术语
    __m256i最终值=_mm256_sub_epi32(值1,值2);
    _mm256存储si256((uu m256i*)索引,最终值;
    //提取最终金额
    int64_t结果=0;
    对于(int x=0;x<6;x++)
    {
    结果+=指数[x];
    }
    返回结果;
    }
    

    请注意,我实际上有两种类似的情况。在第一种情况下,我不需要
    \u pext\u u64
    ,并且此代码比我现有的代码慢约3倍。在第二种情况下,我需要它,速度快25%。

    我们可以假设AVX2(以及收集负载的可用性)吗?使用不同的哈希函数是一种选项吗?x86上不提供SIMD整数除法,除非通过乘法逆(对常数除数有效)或浮点或双精度的转换。将位提取为适合SIMD指令的值。这是看待SIMD的错误方式。当您将64位整数加载到SIMD向量中时,它已经是8x 8位整数和4x 16位整数的向量,依此类推。您可以在
    \u m128i变量。如果您需要更宽的中间精度,那么是的,第一步通常是类似于
    pmovzxbd
    或类似()如果
    k
    总是很小,那么除数实际上是常量。或者你是说这些值是可变长度的比特组,你需要迭代解析,以找出一个结束点和下一个开始点?那么是的,你可能需要一个标量循环。我想至少有一些(伪)至少一个标量版本的代码会有帮助;我真的不想知道需要加速哪些操作。可能对16位或32位整数SIMD除以小常量会有帮助。(方法与)