将 256 位 AVX 向量存储为无符号长整数的最佳方法

Posted

技术标签:

【中文标题】将 256 位 AVX 向量存储为无符号长整数的最佳方法【英文标题】:Best way to store 256 bit AVX vectors into unsigned long integers 【发布时间】:2017-03-13 12:18:30 【问题描述】:

我想知道将 256 位长 AVX 向量存储为 4 个 64 位无符号长整数的最佳方法是什么。根据https://software.intel.com/sites/landingpage/IntrinsicsGuide/ 网站上写的功能,我只能想办法使用 maskstore(下面的代码)来做到这一点。但这是最好的方法吗?还是有其他方法可以做到这一点?

#include <immintrin.h>
#include <stdio.h>

int main() 

    unsigned long long int i,j;
    unsigned long long int bit[32][4];//256 bit random numbers
    unsigned long long int bit_out[32][4];//256 bit random numbers for test

    for(i=0;i<32;i++) //load with 64 bit random integers
        for(j=0;j<4;j++)
            bit[i][j]=rand();
            bit[i][j]=bit[i][j]<<32 | rand();
        
    

//--------------------load masking-------------------------
    __m256i v_bit[32];
    __m256i mask;
    unsigned long long int mask_ar[4];
    mask_ar[0]=~(0UL);mask_ar[1]=~(0UL);mask_ar[2]=~(0UL);mask_ar[3]=~(0UL);
    mask = _mm256_loadu_si256 ((__m256i const *)mask_ar);
//--------------------load masking ends-------------------------

//--------------------------load the vectors-------------------
    for(i=0;i<32;i++)

        v_bit[i]=_mm256_loadu_si256 ((__m256i const *)bit[i]);

    
//--------------------------load the vectors ends-------------------

//--------------------------extract from the vectors-------------------
    for(i=0;i<32;i++)

        _mm256_maskstore_epi64 (bit_out[i], mask, v_bit[i]);
    
//--------------------------extract from the vectors end-------------------

    for(i=0;i<32;i++) //load with 64 bit random integers
        for(j=0;j<4;j++)
            if(bit[i][j]!=bit_out[i][j])
                printf("----ERROR----\n");
        
    

  return 0;

【问题讨论】:

最好的办法是不要。 unsigned long 不保证有 64 位。如果您需要特定的位宽(和编码),请使用来自stdint.h 的固定宽度类型。 也许你应该看看 extractsetinsert 内在函数。我不知道你想做什么。 @Christoph 我只想在 4、64 位整数中提取 256 个向量。我没有找到您在上述页面中提到的内在函数。 如果目标 64 位整数是连续的,则只需使用 _mm256_storeu_si256 在 C11 中,使用 _Alignas(32) unsigned long long int bit[32][4]; 让编译器为您的数组对齐堆栈内存。即使您仍然使用_mm256_storeu_ps,这也有助于提高性能。 【参考方案1】:

正如其他人在 cmets 中所说,在这种情况下您不需要使用掩码存储。以下循环在您的程序中没有错误

for(i=0;i<32;i++)
   _mm256_storeu_si256 ((__m256i const *) bit_out[i], v_bit[i]);


因此,您正在寻找的最佳指令是_mm256_storeu_si256,如果您的数据已对齐,则此指令将__m256i 向量存储到未对齐的地址,您可以使用_mm256_store_si256。要查看您的向量值,您可以使用此函数:

#include <stdalign.h>
alignas(32) unsigned long long int tempu64[4];
void printVecu64(__m256i vec)

    _mm256_store_si256((__m256i *)&tempu64[0], vec);
    printf("[0]= %u, [1]=%u, [2]=%u, [3]=%u \n\n", tempu64[0],tempu64[1],tempu64[2],tempu64[3]) ;


_mm256_maskstore_epi64 让您选择要存储到内存中的元素。当您想要存储具有更多选项的向量以将元素存储到内存或不更改内存值时,此指令很有用。

我正在阅读 Intel 64 和 IA-32 架构优化参考手册 (248966-032),2016 年,第 410 页。 有趣地发现未对齐的存储仍然是性能杀手。 p>

11.6.3 优先使用对齐存储而不是对齐加载

在某些情况下,可以仅对齐 处理的数据缓冲区。在这些情况下,对齐用于 存储操作通常比对齐数据产生更好的性能 用于加载操作的缓冲区。未对齐的商店可能会导致 比未对齐的负载更大的性能下降,因为有 对跨页面的拆分缓存行的存储非常高的惩罚。 该惩罚估计为 150 个周期。跨页面的加载 边界在退休时执行。在示例 11-12 中,未对齐存储 地址会影响 3 个未对齐地址的 SAXPY 性能 大约四分之一的对齐大小写。

我在这里分享是因为有人说对齐/非对齐存储之间除了调试之外没有区别!

【讨论】:

_mm256_store_si256_mm256_storeu_si256 相比没有任何优势,除非您希望内存对齐并且可能用于调试,而_mm256_store_si256 不会使您的代码崩溃。 @Zboson 遗憾的是,即使您使用 _mm256_store_si256,ICC 也会发出未对齐的负载。当代码突然与其他编译器崩溃时,这让我有些白发。 @Christoph,我应该说指令而不是内在函数,但使用另一个编译器也可以是一种有助于调试的方法。 有趣的事实:Skylake 极大地减少了 4k 分割惩罚,降至仅略高于缓存行分割惩罚。 (我认为这对于商店和负载都是正确的)。 是的,我从未因 SKL 中未对齐的负载而受到处罚。但是,处理器供应商是一些杀手......

以上是关于将 256 位 AVX 向量存储为无符号长整数的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章

两个 16 位整数向量与 C++ 中的 AVX2 的内积

有符号 32 位元素的 AVX __m256i 整数除法

使用 AVX512 将压缩 64 位整数转换为带符号饱和的压缩 8 位整数

使用 intel 内在函数将压缩的 8 位整数乘以浮点向量

AVX2 64位无符号整数比较

将 4 个字节转换为无符号 32 位整数并将其存储在 long