Intel Xeon Phi 提供使用“IMCI”指令集,
我用它来做 "c = a*b",像这样:
float* x = (float*) _mm_malloc(N*sizeof(float), ALIGNMENT) ;
float* y = (float*) _mm_malloc(N*sizeof(float), ALIGNMENT) ;
float z[N];
_Cilk_for(size_t i = 0; i < N; i+=16)
{
__m512 x_1Vec = _mm512_load_ps(x+i);
__m512 y_1Vec = _mm512_load_ps(y+i);
__m512 ans = _mm512_mul_ps(x_1Vec, y_1Vec);
_mm512_store_pd(z+i,ans);
}
然后测试它的性能,当N SIZE为1048576时,
它需要成本 0.083317 Sec ,我想比较自动矢量化的性能
所以其他版本的代码是这样的:
_Cilk_for(size_t i = 0; i < N; i++)
z[i] = x[i] * y[i];
这个版本花费 0.025475 秒(但有时花费 0.002285 或更少,我不知道为什么?)
如果我将 _Cilk_for 更改为 #pragma omp parallel for,性能会很差。
所以,如果答案是这样的,为什么我们需要使用内部函数?
我有没有哪里出错了?
有人可以给我一些好的建议来优化代码吗?
由于各种错误,测量结果意义不大。
- 代码将 16 个 float 存储为 8 个 double 。
_mm512_store_pd
应该是 _mm512_store_ps
。
- 代码在地址为 z+i 的未对齐位置上使用 _mm512_store_...,这可能会导致段错误。使用
__declspec(align(64))
来解决这个问题。
- 数组 x 和 y 没有初始化。这有引入随机数的非正规值的风险,这可能会影响性能。 (我不确定这是否是 Intel Xeon Phi 的问题)。
- 没有证据表明使用了 z,因此优化器可能会删除计算。我认为这里不是这种情况,但是像这样的微不足道的基准测试是有风险的。此外,在堆栈上分配大型数组有堆栈溢出的风险。
- 示例的单次运行可能是一个糟糕的基准测试,因为时间可能主要由
_Cilk_for
的 fork/join 开销支配。假设有 120 个 Cilk worker(60 个 4 路线程内核的默认值),每个 worker 只有大约 1048576/120/16 = ~546 次迭代。时钟速率超过 1 GHz,这不会花很长时间。事实上,循环中的工作是如此之小,以至于一些 worker 很可能永远没有机会窃取工作。这可能解释了为什么 _Cilk_for 跑得比 OpenMP 快。在 OpenMP 中,所有线程都必须参与 fork/join 才能完成并行区域。
如果编写测试是为了纠正所有错误,那么它本质上就是在一个大数组上计算 z[:] = x[:]*y[:]。由于 Intel(R) Xeon Phi(TM) 上的宽 vector 单元,这成为对内存/高速缓存带宽的测试,而不是 ALU 速度,因为 ALU 完全有能力超过内存带宽。
内在函数对于不能表示为并行/simd 循环的事物很有用,通常是需要奇特排列的事物。例如,我使用内在函数来做一个 16 元素的 prefix-sum operation在 MIC 上(如果我没记错的话只有 6 条指令)。
我是一名优秀的程序员,十分优秀!