标签: simd

SIMD或不SIMD - 跨平台

我需要知道如何以某种方式编写一些可并行化问题的C++跨平台实现,以便我可以利用SIMD(SSE,SPU等)(如果可用).同时我希望能够在运行时在SIMD之间切换而不是SIMD.

你会如何建议我解决这个问题? (当然我不想为所有可能的选项多次实现该问题)

我可以看到这对C++来说可能不是一件容易的事,但我相信我错过了一些东西.到目前为止,我的想法看起来像这样......类cStream将是单个字段的数组.使用多个cStream我可以实现SoA(阵列结构).然后使用一些函数我可以伪造我需要在整个cStream上执行的Lambda函数.

// just for example I'm not expecting this code to compile
cStream a; // something like float[1024]
cStream b;
cStream c;

void Foo()
{
    for_each(
        AssignSIMD(c, MulSIMD(AddSIMD(a, b), a)));
}
Run Code Online (Sandbox Code Playgroud)

其中for_each将负责增加流的当前指针以及使用SIMD和没有SIMD内联仿函数的主体.

像这样的事情:

// just for example I'm not expecting this code to compile
for_each(functor<T> f)
{
#ifdef USE_SIMD
    if (simdEnabled)
        real_for_each(f<true>()); // true means use SIMD
    else
#endif
        real_for_each(f<false>());
}
Run Code Online (Sandbox Code Playgroud)

请注意,如果启用SIMD,则检查一次,并且循环位于主仿函数周围.

c++ metaprogramming simd functor

12
推荐指数
1
解决办法
2823
查看次数

使用SSE索引到数组

假设我有一个数组:

uint8_t arr[256];
Run Code Online (Sandbox Code Playgroud)

和一个元素

__m128i x
Run Code Online (Sandbox Code Playgroud)

包含16个字节,

x_1, x_2, ... x_16
Run Code Online (Sandbox Code Playgroud)

我想有效地填补一个新__m128i元素

__m128i y
Run Code Online (Sandbox Code Playgroud)

使用arr取决于值的值x,以便:

y_1  = arr[x_1]
y_2  = arr[x_2]
   .
   .
   .
y_16 = arr[x_16]
Run Code Online (Sandbox Code Playgroud)

实现此目的的命令实质上是从非连续的一组存储器位置加载寄存器.我有一种痛苦的模糊记忆,看过这样一个命令的文档,但现在找不到它.它存在吗?在此先感谢您的帮助.

c sse simd

12
推荐指数
1
解决办法
2161
查看次数

使用带有gcc的SSE指令而不使用内联汇编

我有兴趣使用x86-64与gcc的SSE向量指令,并且不想为此使用任何内联汇编.有没有办法在C中做到这一点?如果是这样,有人可以举个例子吗?

c gcc sse x86-64 simd

12
推荐指数
3
解决办法
8834
查看次数

SIMD以下代码

如何在C中简化以下代码(当然使用SIMD内在函数)?我无法理解SIMD内在函数,这会有很大帮助:

int sum_naive( int n, int *a )
{
    int sum = 0;
    for( int i = 0; i < n; i++ )
        sum += a[i];
    return sum;
}
Run Code Online (Sandbox Code Playgroud)

c x86 sse simd

12
推荐指数
1
解决办法
6187
查看次数

击败或满足OS X memset(和memset_pattern4)

我的问题是基于另一个SO问题:为什么_mm_stream_ps会产生L1/LL缓存未命中?

在阅读并被它吸引之后,我试图复制结果并亲眼看看哪个更快:天真循环,展开的幼稚循环,_mm_stream_ps(展开),_mm_store_ps(展开)以及最后但并非最不重要memset_pattern4.(最后一个采用4字节模式,例如浮点数,并在目标数组上填充它,这应该与所有其他函数相同,但它可能是OS X独有的).

我已确保将数组的开头对齐在高速缓存行(64字节,我检查过),并在参数中传递数组以及上一个问题中提到的任何其他性能调整.

有人想在gamedev上知道同样的事情:http://www.gamedev.net/topic/532112-fast-memset/

该线程的结论反映了我自己:当目标数组小于最大(L3)缓存时,_mm_store_ps速度快于_mm_stream_ps.目标数组越大,_mm_stream_ps速度越快.我不完全确定为什么__mm_store_ps在第一种情况下速度更快,因为我从不在缓存中使用这些值,但我知道为什么_mm_stream_ps在后一种情况下胜出.它适用于这种情况:将字节写入内存,您不需要立即(或永远).

以下是使用gcc 4.8编译的目标数组比L3缓存大256倍(在我的情况下为1.5GB)的一些结果:

gcc-4.8 stream.c -o stream -std=c11 -O3 -g3 -ftree-vectorize -march=native -minline-all-stringops && ./stream

bench L3-MASS, array 1610612736 bytes (402653184 floats, 0 remainder, 0x104803040 pointer)
warm up round...
      6% (  20.81148 ms) : MEMSET CHEAT
      8% (  28.49419 ms) : MEMSET PATTER
    100% ( 371.40385 ms) : NAIVE  NORMAL
     54% ( 202.01147 ms) …
Run Code Online (Sandbox Code Playgroud)

c optimization performance assembly simd

12
推荐指数
1
解决办法
1497
查看次数

快速矢量化rsqrt和SSE/AVX的倒数取决于精度

假设有必要计算打包浮点数据的倒数或倒数平方根.两者都可以轻松完成:

__m128 recip_float4_ieee(__m128 x) { return _mm_div_ps(_mm_set1_ps(1.0f), x); }
__m128 rsqrt_float4_ieee(__m128 x) { return _mm_div_ps(_mm_set1_ps(1.0f), _mm_sqrt_ps(x)); }
Run Code Online (Sandbox Code Playgroud)

这种方法效果很好但很慢:根据指南,它们在Sandy Bridge上进行了14次和28次循环(吞吐量).对应的AVX版本在Haswell上几乎占用相同的时间.

另一方面,可以使用以下版本:

__m128 recip_float4_half(__m128 x) { return _mm_rcp_ps(x); }
__m128 rsqrt_float4_half(__m128 x) { return _mm_rsqrt_ps(x); }
Run Code Online (Sandbox Code Playgroud)

它们只需要一到两个时间周期(吞吐量),从而大大提升了性能.但是,它们非常接近:它们产生的结果相对误差小于1.5*2 ^ -12.鉴于单精度浮点数的机器epsilon是2 ^?24,我们可以说这种近似具有大约一半的精度.

似乎可以添加Newton-Raphson迭代以产生具有精度的结果(可能不像IEEE标准所要求的那样精确),参见GCC,ICC,LLVM上的讨论.理论上,相同的方法可用于双精度值,产生精度或精度或精度.

我有兴趣为float和double数据类型以及所有(half,single,double)精度实现此方法的实现.处理特殊情况(除以零,sqrt(-1),inf/nan等)不是必需的.此外,我不清楚这些例程中的哪一个比普通的IEEE编译解决方案更快,哪个更慢.

以下是对答案的一些小限制,请:

  1. 在代码示例中使用内在函数.程序集依赖于编译器,因此不太有用.
  2. 对函数使用类似的命名约定.
  3. 实现例程,将单个SSE/AVX寄存器包含密集打包的float/double值作为输入.如果有相当大的性能提升,你也可以发布几个寄存器作为输入的例程(两个reg可能是可行的).
  4. 如果两个SSE/AVX版本绝对等于将_mm更改为_mm256,则不要发布它们,反之亦然.

欢迎任何性能评估,测量和讨论.

摘要

以下是具有一次NR迭代的单精度浮点数的版本:

__m128 recip_float4_single(__m128 x) {
  __m128 res = _mm_rcp_ps(x);
  __m128 muls …
Run Code Online (Sandbox Code Playgroud)

performance sse simd avx

12
推荐指数
1
解决办法
4202
查看次数

为什么我在GCC 5和cilk-plus中遇到此编译错误?

由于某种原因,cilk_spawn不适用于x86内在函数.每当我尝试将两者组合在同一函数的主体中时,我都会收到错误.(注意cilk_for工作正常).如果我删除所有SIMD指令,它编译并运行正常.

#include <stdio.h>
#include <x86intrin.h>
#include <math.h>
#include <cilk/cilk.h>

int main()
{
    int w = cilk_spawn sqrt(10);
    __m128i x = _mm_set_epi64x(1, 1);
    x = _mm_add_epi64(x, x);
    cilk_sync;
    printf("%d\n", w);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

这是gcc输出:

gcc-4.9 -std=c99 -march=native -fcilkplus -O0 -g3 -Wall -c -fmessage-length=0 -MMD -MP -MF"main.d" -MT"main.d" -o "main.o" "../main.c"
In file included from /usr/lib/gcc/x86_64-linux-gnu/4.9/include/xmmintrin.h:1258:0,
                 from /usr/lib/gcc/x86_64-linux-gnu/4.9/include/x86intrin.h:31,
                 from ../main.c:2:
../main.c: In function ‘main’:
/usr/lib/gcc/x86_64-linux-gnu/4.9/include/emmintrin.h:581:1: error: inlining failed in call to always_inline ‘_mm_set_epi64x’: function not inlinable
 _mm_set_epi64x (long long __q1, long long …
Run Code Online (Sandbox Code Playgroud)

c gcc simd cilk-plus

12
推荐指数
1
解决办法
1054
查看次数

冲突检测指令如何使循环矢量化变得更容易?

AVX512CD指令系列包括:VPCONFLICT,VPLZCNT和VPBROADCASTM.

关于这些指令的维基百科部分说:

AVX-512冲突检测(AVX-512CD)中的指令旨在帮助有效地计算通常无法安全矢量化的循环中元素的无冲突子集.

有哪些例子表明这些指令在向量化循环中有用?如果答案将包括标量循环及其矢量化对应物将会有所帮助.

谢谢!

x86 simd vectorization intel-mic avx512

12
推荐指数
1
解决办法
1071
查看次数

为什么这个SIMD乘法不比非SIMD乘法快?

让我们假设我们有一个函数,每个函数乘以两个1000000双精度数组.在C/C++中,函数如下所示:

void mul_c(double* a, double* b)
{
    for (int i = 0; i != 1000000; ++i)
    {
        a[i] = a[i] * b[i];
    }
}
Run Code Online (Sandbox Code Playgroud)

编译器生成以下程序集-O2:

mul_c(double*, double*):
        xor     eax, eax
.L2:
        movsd   xmm0, QWORD PTR [rdi+rax]
        mulsd   xmm0, QWORD PTR [rsi+rax]
        movsd   QWORD PTR [rdi+rax], xmm0
        add     rax, 8
        cmp     rax, 8000000
        jne     .L2
        rep ret
Run Code Online (Sandbox Code Playgroud)

从上面的程序集看来,编译器似乎使用了SIMD指令,但每次迭代只会增加一倍.所以我决定在内联汇编中编写相同的函数,在那里我充分利用xmm0寄存器并一次乘以两个双精度:

void mul_asm(double* a, double* b)
{
    asm volatile
    (
        ".intel_syntax noprefix             \n\t"
        "xor    rax, rax                    \n\t"
        "0:                                 \n\t"
        "movupd …
Run Code Online (Sandbox Code Playgroud)

c++ performance assembly simd

12
推荐指数
2
解决办法
1823
查看次数

C#和SIMD:高和低加速。怎么了?

问题介绍

我正在尝试加快我正在编写的(2d)光线跟踪器的交集代码。我正在使用C#和System.Numerics库来提高SIMD指令的速度。

问题是我得到了奇怪的结果,屋顶加速和加速都很慢。我的问题是,为什么一个人过高而另一个人过低?

内容:

  • RayPack结构是一系列(不同的)射线,包装在System.Numerics的Vector中。
  • BoundingBoxPack和CirclePack结构是单个bb /圆圈,包装在System.Numerics的向量中。
  • 使用的CPU是i7-4710HQ(Haswell),带有SSE 4.2,AVX(2)和FMA(3)指令。
  • 在发布模式(64位)下运行。该项目在.Net Framework 472中运行。未设置其他选项。

尝试次数

我已经尝试查找某些操作是否受到正确支持(请注意:这些操作适用于c ++。https://fgiesen.wordpress.com/2016/04/03/sse-mind-the-gap/http://sci.tuomastonteri.fi/programming/sse),但似乎并非如此,因为我使用的笔记本电脑支持SSE 4.2。

在当前代码中,应用了以下更改:

  • 使用更正确的说明(例如,最小包装)。
  • 不使用float *向量指令(导致大量其他操作,请参见原始程序集)。

代码...摘要?

对于大量代码,我们深表歉意,但是我不确定如果没有大量代码,我们如何才能具体讨论这一点。

雷代码-> BoundingBox

public bool CollidesWith(Ray ray, out float t)
{
    // https://gamedev.stackexchange.com/questions/18436/most-efficient-aabb-vs-ray-collision-algorithms
    // r.dir is unit direction vector of ray
    float dirfracx = 1.0f / ray.direction.X;
    float dirfracy = 1.0f / ray.direction.Y;
    // lb is the corner of AABB with minimal coordinates - left bottom, rt is maximal corner
    // r.org is origin of …
Run Code Online (Sandbox Code Playgroud)

c# performance x86-64 simd avx

12
推荐指数
1
解决办法
217
查看次数