首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >SSE2矢量移位

SSE2矢量移位
EN

Stack Overflow用户
提问于 2016-07-27 06:38:22
回答 1查看 1.1K关注 0票数 1

我一直试图在SSE2本质上实现按向量转换,但是从实验和情报本征指南来看,它似乎只使用了向量中最不重要的部分。

给出向量{v1,v2,.,vn}和一组移位{s1,s2,.,sn}来重述我的问题,我如何计算结果{r1,r2,.,rn}以便:

代码语言:javascript
复制
r1 = v1 << s1
r2 = v2 << s2
...
rn = vn << sn

因为_mm_sll_epi*似乎执行以下操作:

代码语言:javascript
复制
r1 = v1 << s1
r2 = v2 << s1
...
rn = vn << s1

提前谢谢。

编辑:

下面是我的代码:

代码语言:javascript
复制
#include <iostream>

#include <cstdint>

#include <mmintrin.h>
#include <emmintrin.h>

namespace SIMD {

    using namespace std;

    class SSE2 {
    public:
        // flipped operands due to function arguments
        SSE2(uint64_t a, uint64_t b, uint64_t c, uint64_t d) { low = _mm_set_epi64x(b, a); high = _mm_set_epi64x(d, c); }

        uint64_t& operator[](int idx)
        {
            switch (idx) {
            case 0:
                _mm_storel_epi64((__m128i*)result, low);
                return result[0];
            case 1:
                _mm_store_si128((__m128i*)result, low);
                return result[1];
            case 2:
                _mm_storel_epi64((__m128i*)result, high);
                return result[0];
            case 3:
                _mm_store_si128((__m128i*)result, high);
                return result[1];
            }

            /* Undefined behaviour */
            return 0;
        }

        SSE2& operator<<=(const SSE2& rhs)
        {
            low  = _mm_sll_epi64(low,  rhs.getlow());
            high = _mm_sll_epi64(high, rhs.gethigh());

            return *this;
        }

        void print()
        {
            uint64_t a[2];
            _mm_store_si128((__m128i*)a, low);

            cout << hex;
            cout << a[0] << ' ' << a[1] << ' ';

            _mm_store_si128((__m128i*)a, high);

            cout << a[0] << ' ' << a[1] << ' ';
            cout << dec;
        }

        __m128i getlow() const
        {
            return low;
        }

        __m128i gethigh() const
        {
            return high;
        }
    private:
        __m128i low, high;
        uint64_t result[2];
    };
}

int main()
{
    cout << "operator<<= test: vector << vector: ";
    {
        auto x = SIMD::SSE2(7, 8, 15, 10);
        auto y = SIMD::SSE2(4, 5,  6,  7);

        x.print();
        y.print();

        x <<= y;

        if (x[0] != 112 || x[1] != 256 || x[2] != 960 || x[3] != 1280) {
            cout << "FAILED: ";
            x.print();
            cout << endl;
        } else {
            cout << "PASSED" << endl;
        }
    }

    return 0;
}

应该发生的事情得到了{7 << 4= 112,8 << 5= 256,15 << 6= 960,10 << 7= 1280}的结果。结果似乎是{7 << 4= 112,8 << 4= 128,15 << 6= 960,15 << 6= 640},这不是我想要的。

希望这能帮上忙延斯。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2016-07-27 09:04:41

如果AVX2可用,并且您的元素是32位或64位,则操作需要一个可变移位指令:vpsrlvq,(__m128i _mm_srlv_epi64 (__m128i a, __m128i count) )。

有关带有SSE4.1的32位元素,请参见用不同的SIMD值右移4个整数。根据延迟与吞吐量的需求,您可以进行单独的移位,然后混合,或者使用乘法(由特殊构造的幂向量2)得到可变计数的左移位,然后对所有元素进行相同的右移位。

对于您的情况,具有运行时变量移位的64位元素很重要:

每个SSE向量只有两个元素,所以我们只需要两次移位,然后组合结果(我们可以使用pblendw,或者用浮点movsd (这可能会在某些CPU上造成额外的旁路延迟延迟),或者我们可以使用两次洗牌,或者我们可以做两个数字和一个OR。

代码语言:javascript
复制
__m128i SSE2_emulated_srlv_epi64(__m128i a, __m128i count)
{
    __m128i shift_low = _mm_srl_epi64(a, count);          // high 64 is garbage
    __m128i count_high = _mm_unpackhi_epi64(count,count); // broadcast the high element
    __m128i shift_high = _mm_srl_epi64(a, count_high);    // low 64 is garbage
    // SSE4.1:
    // return _mm_blend_epi16(shift_low, shift_high, 0x0F);

#if 1   // use movsd to blend
    __m128d blended = _mm_move_sd( _mm_castsi128_pd(shift_high), _mm_castsi128_pd(shift_low) );  // use movsd as a blend.  Faster than multiple instructions on most CPUs, but probably bad on Nehalem.
    return _mm_castpd_si128(blended);
#else  // SSE2 without using FP instructions:
    // if we're going to do it this way, we could have shuffled the input before shifting.  Probably not helpful though.
    shift_high = _mm_unpackhi_epi64(shift_high, shift_high);       // broadcast the high64
    return       _mm_unpacklo_epi64(shift_high, shift_low);        // combine
#endif
}

其他的洗牌,如pshufd或psrldq可以工作,但是潘普克可以在不需要立即字节的情况下完成工作,所以它比以前短一个字节。SSSE3 palignr可以将一个寄存器中的高元素和另一个寄存器中的低元素转换到一个向量中,但是它们会被反转(因此我们需要一个pshufd来交换高和低的一半)。shufpd可以混合使用,但与movsd相比没有优势。

有关在两个整数指令之间使用FP指令的潜在旁路延迟延迟的详细信息,请参见阿格纳雾微拱导轨。英特尔的SnB家族CPU可能没问题,因为其他的FP洗牌都是这样。(没错,movsd xmm1, xmm0在port5的洗牌部门运行。如果不需要合并行为,使用movapsmovapd进行reg移动(即使是标量)。

这将编译(在哥德波特上使用gcc5.3 -O3)到

代码语言:javascript
复制
    movdqa  xmm2, xmm0  # tmp97, a
    psrlq   xmm2, xmm1    # tmp97, count
    punpckhqdq      xmm1, xmm1  # tmp99, count
    psrlq   xmm0, xmm1    # tmp100, tmp99
    movsd   xmm0, xmm2    # tmp102, tmp97
    ret
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/38605451

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档