首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >未知SSE瓶颈

未知SSE瓶颈
EN

Stack Overflow用户
提问于 2014-09-16 00:17:50
回答 2查看 215关注 0票数 2

我有一个通用代码,我正试图移动到SSE,以加快它的速度,因为它经常被调用。所讨论的代码基本上如下所示:

代码语言:javascript
复制
for (int i = 1; i < mysize; ++i)
{
    buf[i] = myMin(buf[i], buf[i - 1] + offset);
}

myMin在哪里是简单的min函数(a < b)?B:(我看过拆卸,这里有跳转)

我的SSE代码(我已经经历了几次迭代以加快)现在是在这个表单上:

代码语言:javascript
复制
float tmpf = *(tmp - 1);
__m128 off = _mm_set_ss(offset);
for (int l = 0; l < mysize; l += 4)
{
    __m128 post = _mm_load_ps(tmp);
    __m128 pre = _mm_move_ss(post, _mm_set_ss(tmpf));
    pre = _mm_shuffle_ps(pre, pre, _MM_SHUFFLE(0, 3, 2, 1));
    pre = _mm_add_ss(pre, off);
    post = _mm_min_ss(post, pre);

    // reversed
    pre = _mm_shuffle_ps(post, post, _MM_SHUFFLE(2, 1, 0, 3));
    post = _mm_add_ss(post, off );
    pre = _mm_min_ss(pre, post);

    post = _mm_shuffle_ps(pre, pre, _MM_SHUFFLE(2, 1, 0, 3));
    pre = _mm_add_ss(pre, off);
    post = _mm_min_ss(post, pre);

    // reversed
    pre = _mm_shuffle_ps(post, post, _MM_SHUFFLE(2, 1, 0, 3));
    post = _mm_add_ss(post, off);
    pre = _mm_min_ss(pre, post);

    post = _mm_shuffle_ps(pre, pre, _MM_SHUFFLE(2, 1, 0, 3));
    _mm_store_ps(tmp, post);
    tmpf = tmp[3];
    tmp += 4;
}

忽略任何边缘案例场景(我处理得很好),而且由于buf/tmp的大小,这些方案的开销可以忽略不计,有人能解释为什么SSE版本比2x慢吗?VTune一直将其归因于L1的失误,但正如我所看到的,它应该减少4倍到L1的旅行,并且没有分支/跳转,所以应该更快一些,但它不是。我搞错什么了?

谢谢

编辑:所以我确实在一个单独的测试用例中找到了其他的东西。我原以为这不会有什么关系,但遗憾的是,事实确实如此。所以我上面的尺寸其实没那么大(大约30-50),但是有很多这样的东西,它们都是连续的。在这种情况下,三元表达式比SSE快。但是,如果以百万为单位,并且只有30-50次迭代,则SSE版本会更快。知道为什么吗?我认为记忆交互对两者都是一样的,包括先发制人的先发制人.

EN

回答 2

Stack Overflow用户

发布于 2014-09-16 11:25:53

如果此代码对性能至关重要,则必须查看所获得的数据。这是连环依赖正在杀死你,你需要摆脱它。

一个非常小的值-- buf I--将影响以下许多值。例如,如果偏移量= 1,buf = 0,以及所有其他值都大于100万,那么一个值将影响下一个100万。另一方面,这种事情可能很少发生。

如果很少见,他们会检查buf I> buf I+偏移量是否完全矢量化,如果是,则替换它,并跟踪所做的更改,而不考虑buf I值可能向上滚动。然后检查更改的位置,然后重新检查它们。

在极端情况下,假设buf i总是在0到1之间,偏移量> 0.5,您知道buf i根本无法影响buf i+2,所以您只需忽略串行依赖,并并行地、完全向量化地执行所有操作。

另一方面,如果缓冲区中有一些影响大量连续值的微小值,则从第一个值buf开始,并完全向量化地检查buf i< buf +I*偏移量,替换值,直到检查失败。

你说“价值可以是任何东西”。如果是这样的话,例如,如果buf i被随机选择在0到1,000,000之间,并且偏移量不是很大,那么就会有元素buf i,这会迫使大量的元素成为buf i+ (k - i) *偏移。例如,如果偏移量= 1,并且您发现buf i约为10,000,那么它将强制平均大约100个值等于buf i+ (k - i) *偏移量。

票数 1
EN

Stack Overflow用户

发布于 2014-09-16 09:19:18

这里有一个没有分支的解决方案,你可以试试

代码语言:javascript
复制
for (int i = 1; i < mysize; i++) {
    float a = buf[i];
    float b = buf[i-1] + offset;
    buf[i] = b + (a<b)*(a-b);
}

这是程序集:

代码语言:javascript
复制
.L6:
addss   xmm0, xmm4
movss   xmm1, DWORD PTR [rax]
movaps  xmm2, xmm1
add rax, 4
movaps  xmm3, xmm6
cmpltss xmm2, xmm0
subss   xmm1, xmm0
andps   xmm3, xmm2
andnps  xmm2, xmm5
orps    xmm2, xmm3
mulss   xmm1, xmm2
addss   xmm0, xmm1
movss   DWORD PTR [rax-4], xmm0
cmp rax, rdx
jne .L6

但是带有分支的版本可能已经更好了。

代码语言:javascript
复制
for (int i = 1; i < mysize; i++) {
     float a = buf[i];
     float b = buf[i-1] + offset;
     buf[i] = a<b ? a : b;
}

这是程序集

代码语言:javascript
复制
.L15:
addss   xmm0, xmm2
movss   xmm1, DWORD PTR [rax]
add rax, 4
minss   xmm1, xmm0
movss   DWORD PTR [rax-4], xmm1
cmp rax, rdx
movaps  xmm0, xmm1
jne .L15

这会产生使用minss (cmp rax, rdx应用于循环迭代器)的代码。

最后,下面是您可以与MSVC一起使用的代码,它生成与GCC相同的程序集,后者是无分支的。

代码语言:javascript
复制
__m128 offset4 = _mm_set1_ps(offset);
for (int i = 1; i < mysize; i++) {
    __m128 a = _mm_load_ss(&buf[i]);
    __m128 b = _mm_load_ss(&buf[i-1]);
    b = _mm_add_ss(b, offset4);
    a = _mm_min_ss(a,b);
    _mm_store_ss(&buf[i], a);
}

下面是另一种使用分支的表单

代码语言:javascript
复制
__m128 offset4 = _mm_set1_ps(offset);
for (int i = 1; i < mysize; i++) {
    __m128 a = _mm_load_ss(&buf[i]);
    __m128 b = _mm_load_ss(&buf[i-1]);
    b = _mm_add_ss(b, offset4);
    if(_mm_comige_ss(b,a))
        _mm_store_ss(&buf[i], b);
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/25858858

复制
相关文章

相似问题

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