gpt4 book ai didi

c - 大型阵列的 SSE 性能较慢

转载 作者:太空狗 更新时间:2023-10-29 16:10:51 29 4
gpt4 key购买 nike

我是 SSE 编程的新手,所以我希望有人能帮助我。我最近使用 GCC SSE 内在函数实现了一个函数来计算 32 位整数数组的总和。下面给出了我的实现代码。

int ssum(const int *d, unsigned int len)
{
static const unsigned int BLOCKSIZE=4;
unsigned int i,remainder;
int output;
__m128i xmm0, accumulator;
__m128i* src;

remainder = len%BLOCKSIZE;
src = (__m128i*)d;
accumulator = _mm_loadu_si128(src);

output = 0;
for(i=BLOCKSIZE;i<len-remainder;i+=BLOCKSIZE){
xmm0 = _mm_loadu_si128(++src);
accumulator = _mm_add_epi32(accumulator,xmm0);
}

accumulator = _mm_add_epi32(accumulator, _mm_srli_si128(accumulator, 8));
accumulator = _mm_add_epi32(accumulator, _mm_srli_si128(accumulator, 4));
output = _mm_cvtsi128_si32(accumulator);


for(i=len-remainder;i<len;i++){
output += d[i];
}
return output;
}

如您所见,这是一个相当直接的实现,我使用扩展的 xmm 寄存器一次对数组 4 求和,然后在最后通过将剩余元素相加来清理。

然后我将此 SIMD 实现的性能与普通的 for 循环进行了比较。该实验的结果可在此处获得:

SIMD vs. for-loop

如您所见,与 for 循环相比,此实现确实显示了大约 60% 的输入大小(意味着数组的长度)高达大约 5M 元素的加速。然而,对于较大的输入大小值,与 for 循环相关的性能会急剧下降,仅产生大约 20% 的加速。

我无法解释性能的急剧下降。我或多或少在内存中线性步进,因此缓存未命中和页面错误的影响对于两种实现应该大致相同。我在这里错过了什么?有什么办法可以拉平这条曲线吗?任何想法将不胜感激。

最佳答案

对于大输入,数据在缓存之外,代码受内存限制。
对于小输入,数据在缓存中(即 L1/L2/L3 缓存),代码是有计算限制的。
我假设您在性能测量之前没有尝试刷新缓存。

缓存内存在CPU内部,缓存内存和ALU(或SSE)单元之间的带宽非常高(高带宽-传输数据的时间更少)。
您的最高级别缓存(即 L3)大小约为 4MB 到 8MB(取决于您的 CPU 型号)。
较大的数据量必须位于 DDR SDRAM 上,也就是外部 RAM(在 CPU 之外)。
CPU通过内存总线连接到DDR SDRAM,带宽比高速缓存低很多。

例子:
假设您的外部 RAM 类型是 Dual Channel DDR3 SDRAM 1600 .外部 RAM 和 CPU 之间的最大理论带宽约为 25GB/Sec。

从 RAM 向 CPU 读取 100MBytes 的数据(以 25GB/S 的速度)大约需要 100e6/25e9 = 4 毫秒。
根据我的经验,使用的带宽约为理论带宽的一半,因此读取时间约为 8 毫秒。

计算时间更短:
假设循环的每次迭代大约需要 2 个 CPU 时钟(仅作为示例)。
每次迭代处理 16 个字节的数据。
处理 100MB 的总 CPU 时钟大约需要 (100e6/16)*2 = 12500000 时钟。
假设 CPU 频率为 3GHz。
总 SSE 处理时间约为 12500000/3e9 = 4.2 毫秒。

如您所见,从外部 RAM 读取数据所花费的时间是 SSE 计算时间的两倍。

由于数据传输和计算是并行发生的,总时间是4.2mesc和8msec的最大值(即8msec)。

假设不使用 SSE 的循环需要两倍的计算时间,因此不使用 SSE 的计算时间约为 8.4 毫秒。

在上面的示例中,使用 SSE 的总改进约为 0.4 毫秒。

注意:所选数字仅供示例。


基准:
我在我的系统上做了一些基准测试。
我正在使用 Windows 10 和 Visual Studio 2010。
基准测试:Summing 100MBytes of data (summing 25*1024^2 32bits integers).

中央处理器

  • 英特尔酷睿 i5 3550( Ivy 桥)。
  • CPU 基本频率为 3.3GHz。
  • 测试期间的实际核心速度:3.6GHz(已启用涡轮增压)。
  • L1 数据缓存大小:32KB。
  • 二级缓存大小:256Bytes(单核二级缓存大小)。
  • L3 缓存大小:6MBytes。

内存:

  • 8GB DDR3 双 channel 。
  • RAM 频率:666MHz(相当于没有 DDR 时的 1333MHz)。
  • 内存理论最大带宽:(128*1333/8)/1024 = 20.8GBytes/Sec。

  1. 用 SSE 将 100MB 作为大块求和(外部 RAM 中的数据)。
    处理时间:6.22毫秒
  2. 使用 SSE 对 1KB 求和 100 次(缓存中的数据)。
    处理时间:3.86毫秒
  3. 总计 100MB 作为大块没有 SSE(外部 RAM 中的数据)。
    处理时间:8.1毫秒
  4. 总和 1KB 100 次没有 SSE(缓存中的数据)。
    处理时间:4.73毫秒

已用内存带宽:100/6.22 = 16GB/Sec (按时间划分数据大小)
SSE 每次迭代的平均时钟(缓存中的数据):(3.6e9*3.86e-3)/(25/4*1024^2) = 2.1 clks/iteration (除以总 CPU按迭代次数计时)

关于c - 大型阵列的 SSE 性能较慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38996265/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com