从四个 __m128i 变量的 64 个高位或低位初始化 __m256i

Posted

技术标签:

【中文标题】从四个 __m128i 变量的 64 个高位或低位初始化 __m256i【英文标题】:Initialize __m256i from 64 high or low bits of four __m128i variables 【发布时间】:2014-07-06 08:26:37 【问题描述】:

假设我有四个 __m128i 变量,其中包含一些计算产生的数据。例如,让我们说:

__m128i a = _mm_set_epi64x(1, 11);
__m128i b = _mm_set_epi64x(2, 22);
__m128i c = _mm_set_epi64x(3, 33);
__m128i d = _mm_set_epi64x(4, 44);

我想初始化两个__m256i 变量,其中第一个包含四个变量的所有高 64 位,第二个包含每个变量的低 64 位。所以我想拥有:

__m256i x = ...; // x =  4, 3, 2, 1 ;
__m256i y = ...; // y =  44, 33, 22, 11 ;

这样做的明显方法是使用_mm256_set_epi64x_mm_extract_epi64。但是,它可能不是特别快。有没有更快的方法呢?特别是,对于访问 64 位高位,我没有看到合适的负载(SSE2 中有低 64 位的负载)或随机播放指令(似乎没有“64 位随机播放”)。

【问题讨论】:

你看到的加载低 64 位的指令是什么? @JohnZwinck _mm_loadl_epi64,但它适用于 128 位寄存器。 您提到没有 64 位随机播放,但谁需要呢?我们有 _mm256_shuffle_epi32,如果我们想要 shuffle 64 位块,我们可以声明我们正在做 32 位块并进行 shuffle,以便相邻的 32 位块一起移动。 【参考方案1】:

如果我正确理解您的问题,这是一个简单的 4x2 转置(或 2x4 转置?)。

这是对我有用的代码:

#include <iostream>
#include <immintrin.h>

using namespace std;
int main() 
    __m128i a = _mm_set_epi64x(1, 11);
    __m128i b = _mm_set_epi64x(2, 22);
    __m128i c = _mm_set_epi64x(3, 33);
    __m128i d = _mm_set_epi64x(4, 44);

    __m256i ac = _mm256_castsi128_si256(a);
    ac = _mm256_inserti128_si256(ac, c, 1); // 3, 33, 1, 11

    __m256i bd = _mm256_castsi128_si256(b);
    bd = _mm256_inserti128_si256(bd, d, 1); // 4, 44, 2, 22

    __m256i high = _mm256_unpackhi_epi64(ac, bd);
    __m256i low = _mm256_unpacklo_epi64(ac, bd);

    uint64_t t[4];

    _mm256_storeu_si256((__m256i*) t, high);

    for (int i = 0; i < 4; ++i) 
        cout << t[i] << endl;
    

    _mm256_storeu_si256((__m256i*) t, low);

    for (int i = 0; i < 4; ++i) 
        cout << t[i] << endl;
    

    return 0;

这应该编译成 4 条指令。

【讨论】:

聪明。我的尝试看起来像六个指令。

以上是关于从四个 __m128i 变量的 64 个高位或低位初始化 __m256i的主要内容,如果未能解决你的问题,请参考以下文章

将 __m128i 值转换为 std::tuple

转移 __m128i 的最佳方式?

用内在函数初始化 __m128i 常量的最快方法?

如何获得英特尔架构 SIMD __m128 的标志

两个 __m128i 的两个位到一个 __m128i 的四个位 -SSE

如何将 16 字节的内存加载到 Rust __m128i 中?