【发布时间】:2019-06-11 01:17:05
【问题描述】:
我想使用 SSE2 内在函数计算每个通道的图像平均值(3 个感兴趣的通道 + 1 个我们在此忽略的 alpha 通道)。我试过了:
__m128 average = _mm_setzero_ps();
#pragma omp parallel for reduction(+:average)
for(size_t k = 0; k < roi_out->height * roi_out->width * ch; k += ch)
{
float *in = ((float *)temp) + k;
average += _mm_load_ps(in);
}
但我在 GCC 中收到此错误:user-defined reduction not found for average。
SSE2 有可能吗?怎么了?
编辑
这行得通:
float sum[4] = { 0.0f };
#pragma omp parallel for simd reduction(+:sum[:4])
for(size_t k = 0; k < roi_out->height * roi_out->width * ch; k += ch)
{
float *in = ((float *)temp) + k;
for (int i = 0; i < ch; ++i) sum[i] += in[i];
}
const __m128 average = _mm_load_ps(sum) / ((float)roi_out->height * roi_out->width);
【问题讨论】:
-
如果您希望人们尝试改进您的代码,您应该提供一个最低限度的工作和可编译示例。也许你应该看看#pragma omp declare reduction。显然 gcc 抱怨它没有定义。
-
_mm_load_ps(in);只有在in是 16 字节对齐时才是安全的。如果ch=3则不会。此外,让编译器使用omp simd进行自动向量化将有望让它使用多个累加器来隐藏 FP-add 延迟。如果它使用 3 或 6 个累加器,它可能会避免重叠并利用所有 12 或 24 个向量元素,并在最后整理混洗以将正确的元素加在一起成为 3 个不同的和。 (或者,如果可能的话,您应该自己这样做,以便将单线程性能提高 3 或 4(addps 延迟)乘以 4/3,以免浪费一个元素。) -
_mm_loadu_ps(in)。或者像我建议的那样,使用 3 或 6 个向量手动进行向量化(因为3*4 = 12 = lcm(channels, vector_width)将通道与向量元素对齐),因此您只需要没有重叠的对齐负载。您也许可以让 OpenMP 帮助您并行化手动矢量化循环,但我强烈建议您使用它,以使每个线程的速度提高约 4 倍(如果您开始遇到 DRAM 或 L3 每核带宽瓶颈,则速度会更低,但是在诸如台式机或笔记本电脑之类的内核很少的机器上,您应该会获得很大的加速,并且只需要几个内核就可以使 DRAM 饱和。) -
哦,那很好,您的数据已对齐。您的问题说您的图像有 3 个通道,所以我假设
ch=3。你没有说有一个阿尔法通道。您仍然希望使用多个累加器来隐藏 FP 延迟,尤其是当您的数据在 L2 或 L3 缓存中可能很热时。但是每 4 个周期 16 个字节可能仍然是比内存更严重的瓶颈。 -
没有为 simd 矢量类型预定义缩减。您可以在 gcc 的 bugzilla 上提交增强请求,这似乎是一个明智且简单的扩展。