【问题标题】:Finding the instances of the number in a vector array in KNC (Xeon Phi)在 KNC (Xeon Phi) 中查找向量数组中的数字实例
【发布时间】:2014-03-14 15:55:23
【问题描述】:

我正在尝试利用 knc (Xeon Phi) 提供的 SIMD 512 来使用 intel 内部函数来提高以下 C 代码的性能。但是,我的内在嵌入式代码运行速度比自动矢量化代码慢

C 代码

int64_t match=0;
int *myArray __attribute__((align(64)));
myArray = (int*) malloc (sizeof(int)*SIZE); //SIZE is array size taken from user
radomize(myArray); //to fill some random data
int searchVal=24;
#pragma vector always
for(int i=0;i<SIZE;i++) {
   if (myArray[i]==searchVal) match++;
return match;

内在嵌入代码: 在下面的代码中,我首先加载数组并将其与搜索键进行比较。内部函数返回使用 _mm512_mask_reduce_add_epi32() 减少的 16 位掩码值。

register int64_t match=0;
int *myArray __attribute__((align(64)));
myArray = (int*) malloc (sizeof(int)*SIZE); //SIZE is array size taken from user
const int values[16]=\
                {   1,1,1,1,\
                    1,1,1,1,\
                    1,1,1,1,\
                    1,1,1,1,\
                };
__m512i const flag = _mm512_load_epi32((void*) values);
__mmask16 countMask;

__m512i searchVal = _mm512_set1_epi32(16);
__m512i kV = _mm512_setzero_epi32();


for (int i=0;i<SIZE;i+=16)
{
   // kV = _mm512_setzero_epi32();
    kV = _mm512_loadunpacklo_epi32(kV,(void* )(&myArray[i]));
    kV = _mm512_loadunpackhi_epi32(kV,(void* )(&myArray[i + 16]));

    countMask = _mm512_cmpeq_epi32_mask(kV, searchVal);
    match += _mm512_mask_reduce_add_epi32(countMask,flag);
}
return match;

我相信我知道如何在此代码中引入额外的循环,因此与自动矢量化代码相比,它运行缓慢。与直接返回 128 位寄存器中比较值的 SIMD128 不同,SIMD512 返回掩码寄存器中的值,这增加了我的代码的复杂性。我是否在这里遗漏了什么,必须有一种方法可以直接比较并记录成功搜索的次数,而不是使用 XOR 操作等掩码。

最后,请向我推荐使用内在函数提高此代码性能的方法。我相信我可以使用内在函数来提高性能。至少 SIMD128 是这样,使用内部函数可以让我获得 25% 的性能。

【问题讨论】:

  • 你为什么用两个负载来填充kV?为什么不只是一个 _mm512_load_epi32

标签: c simd intrinsics xeon-phi


【解决方案1】:

我建议以下优化:

  • 使用预取。您的代码执行的计算量很少,而且几乎肯定会受到带宽限制。 Xeon Phi 仅对 L2 缓存进行硬件预取,因此为了获得最佳性能,您需要手动插入预取指令。
  • 按照@PaulR 的提示使用对齐读取_mm512_load_epi32。使用 memalign 函数而不是 malloc 来保证数组真正对齐在 64 字节上。如果您需要未对齐的指令,请使用 _mm512_undefined_epi32() 作为第一个未对齐加载的源,因为它打破了对 kV(在您当前的代码中)的依赖,并让编译器进行额外的优化。
  • 将数组展开 2 或使用至少两个线程来隐藏指令延迟。
  • 避免使用int 变量作为索引。 unsigned intsize_tssize_t 是更好的选择。

【讨论】:

  • 我在使用 malloc 和 _mm512_load_epi32 时遇到分段错误。但是,它现在可以使用 memalign。关于减少我的代码中比较和减少计算周期的任何建议。
  • 您可以尝试将_mm512_mask_reduce_add_epi32(countMask,flag) 替换为_mm_countbits(_mm512_mask2int(countMask)),但我怀疑它会不会有任何效果。
  • 感谢您的建议,_mm_countbits() 比使用 _mm512_mask_reduce_add_epi32() 方法慢。在考虑了您之前评论中的所有其他建议后,我的代码现在运行速度比自动矢量化代码快 50%。我编译代码如下: icc -mmic -O -std=c99
  • “至强融核没有硬件预取”。错误的。它有一个到 L2 的硬件预取器。 (例如,参见幻灯片 7,software.intel.com/sites/default/files/article/326703/…
  • @JimCownie,你是对的,但关键是有问题的内核需要软件预取以获得更好的性能。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-12
相关资源
最近更新 更多