我需要知道如何以某种方式编写一些可并行化问题的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,则检查一次,并且循环位于主仿函数周围.
假设我有一个数组:
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)
实现此目的的命令实质上是从非连续的一组存储器位置加载寄存器.我有一种痛苦的模糊记忆,看过这样一个命令的文档,但现在找不到它.它存在吗?在此先感谢您的帮助.
我有兴趣使用x86-64与gcc的SSE向量指令,并且不想为此使用任何内联汇编.有没有办法在C中做到这一点?如果是这样,有人可以举个例子吗?
如何在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) 我的问题是基于另一个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) 假设有必要计算打包浮点数据的倒数或倒数平方根.两者都可以轻松完成:
__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编译解决方案更快,哪个更慢.
以下是对答案的一些小限制,请:
欢迎任何性能评估,测量和讨论.
以下是具有一次NR迭代的单精度浮点数的版本:
__m128 recip_float4_single(__m128 x) {
__m128 res = _mm_rcp_ps(x);
__m128 muls …Run Code Online (Sandbox Code Playgroud) 由于某种原因,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) AVX512CD指令系列包括:VPCONFLICT,VPLZCNT和VPBROADCASTM.
AVX-512冲突检测(AVX-512CD)中的指令旨在帮助有效地计算通常无法安全矢量化的循环中元素的无冲突子集.
有哪些例子表明这些指令在向量化循环中有用?如果答案将包括标量循环及其矢量化对应物将会有所帮助.
谢谢!
让我们假设我们有一个函数,每个函数乘以两个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) 问题介绍
我正在尝试加快我正在编写的(2d)光线跟踪器的交集代码。我正在使用C#和System.Numerics库来提高SIMD指令的速度。
问题是我得到了奇怪的结果,屋顶加速和加速都很慢。我的问题是,为什么一个人过高而另一个人过低?
内容:
尝试次数
我已经尝试查找某些操作是否受到正确支持(请注意:这些操作适用于c ++。https://fgiesen.wordpress.com/2016/04/03/sse-mind-the-gap/或http://sci.tuomastonteri.fi/programming/sse),但似乎并非如此,因为我使用的笔记本电脑支持SSE 4.2。
在当前代码中,应用了以下更改:
代码...摘要?
对于大量代码,我们深表歉意,但是我不确定如果没有大量代码,我们如何才能具体讨论这一点。
雷代码-> 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)