相对于标准代码测试 simd 加速的正确方法是啥

Posted

技术标签:

【中文标题】相对于标准代码测试 simd 加速的正确方法是啥【英文标题】:What's the proper way of testing simd speedup relative to standard code相对于标准代码测试 simd 加速的正确方法是什么 【发布时间】:2017-11-11 03:28:09 【问题描述】:

我正在学习如何使用 C 语言使用 SIMD 指令,并且我想比较使用 SIMD 的代码和不使用 SIMD 的代码。有没有人有一个测试模板可以准确识别 SIMD 代码与标准代码的预期加速?

具体来说,我注意到以下在特定配置下的大致性能时间:

SIMD first, single run:
SIMD: 0.15 s
standard: 0.35 s

SIMD first, standard second, repeated 10x:
SIMD: 0.15 s - first run, 0.05 s on subsequent runs
standard: 0.35 s - first run, 0.34 on subsequent runs

standard first, SIMD second, repeated 10x:
standard: 0.45 s - first run, 0.35 s on subsequent runs
SIMD: 0.05 s - first run, 0.05 s on subsequent runs

代码示例正在运行 uint16_t 类型的 1e8 值的数据集。数据分配和初始化在循环之外。如果我在重复循环内分配数据,则循环都具有相同的时间。如果我在 SIMD 和标准部分之前执行此操作,而不是在先到者之前执行此操作,则两者的时间都会更长:

standard: 0.45 s
SIMD: 0.15 s

那么为什么数据分配会导致这样的时间差异呢?什么是真正的加速?

代码链接: https://gist.github.com/JimHokanson/55ce2e5cac75d7df6dc24dadf383e68f

我正在使用 m3 处理器的 2016 年初 Macbook 上进行测试...

【问题讨论】:

所以,我的大部分问题都与使用 calloc 有关!我不确定来源,但我很确定我在某处看到操作系统可以用 calloc 做一些非常奇特的事情。如果我运行循环并将所有值分配给 0,则标准方法的时间差将回到 0.35 秒,而 SIMD 方法的时间差会回到 0.05 秒。我认为这代表了我的数组已初始化的真实世界用例(完全,即显式设置的每个值)。 更新: malloc 也会出现这种情况。那么这是操作系统的问题还是处理器的一些奇怪的缓存效果? 没有看到你是如何测试的,我只能猜测,但听起来你正在测试你的分配代码以及你试图加速的任何操作。也有可能即使您没有,您分配的内存也没有被触及,因此访问它会导致页面错误,因为它被交换或映射。最好包含您的实际测试代码以获得真正的答案。 我在网上发布了一个要点。我将内存分配增加得更高(1e9 个样本),并且保持相同的趋势。 如果 malloc 或 calloc 并且只读取它,则所有页面都可以写入时复制映射到相同的物理零页面,因此您可以获得 L1D 缓存命中。您的问题对您的访问模式不是很清楚。或者关于您正在测试的硬件。 【参考方案1】:

因此,问题似乎只是未能按预期实际初始化内存。我曾认为它可能是特定于 SIMD 测试的东西,而不仅仅是一般的 C。

因此,正确初始化内存的正确方法如下:

data = malloc(1e8);
//- Do a loop to initialize data (previously memset to 0 but it was suggested that this may be optimized away)
//- Do SIMD comparison vs standard approach - loop and average results

优化设置:此外,在尝试与标准库汇编代码竞争时,请记住启用优化!看: why is strchr twice as fast as my simd code 基本要点是我将 SIMD 与标准库代码进行了比较,并具有非常优化的程序集。不优化 SIMD 代码太慢,但优化后结果更合理。

过度优化:有时编译器会在一种情况下优化掉代码,而不是在另一种情况下。例如我有以下代码:

for (size_t n2 = 0; n2 < n_loops_inner; n2++)
   str2 = memchr(str,'b',N);
   char_index2 = str2 - str;

但是,这段代码执行得太快了。我在循环内搜索之前添加了以下行。

  str[(size_t)char_position] = 'b';

此外,我还将 char_index2 标记为 volatile。这些更改共同提供了更合理的执行时间。 (即比没有这些更改时慢 1000 倍)

【讨论】:

gcc 会将malloc + memset(0) 优化为calloc。如果你正在做整数的东西,memset 用非零的东西。

以上是关于相对于标准代码测试 simd 加速的正确方法是啥的主要内容,如果未能解决你的问题,请参考以下文章

这个 Delphi 6 位图修改代码可以用 SIMD 或其他方法加速吗?

使用 SIMD 将累积(单个)值打包成两个值管理清理代码循环的方法是啥?

加速图像处理的神器: Intel ISPC编译器ISPC简介

加速图像处理的神器: Intel ISPC编译器ISPC简介

SIMD加速计算矩阵(组成原理实验5)

如何使用 SIMD 加速两个内存块的异或?