【发布时间】:2020-11-25 11:44:22
【问题描述】:
我最近听说了无分支编程的想法,我想尝试一下,看看它是否可以提高性能。我有以下 C 函数。
int square(int num) {
int result = 0;
if (num > 10) {
result += num;
}
return result * result;
}
删除 if 分支后,我有这个:
int square(int num) {
int result = 0;
int tmp = num > 10;
result = result * tmp + num * tmp + result * !tmp;
return result * result;
}
现在我想知道无分支版本是否更快。我四处搜寻,发现了一个名为 hyperfine (https://github.com/sharkdp/hyperfine) 的工具。于是我写了下面的main函数,用hyperfine测试了square函数的两个版本。
int main() {
printf("%d\n", square(38));
return 0;
}
问题是基于超精细结果,我无法确定哪个版本更好。在 C 编程中,人们通常如何确定函数的哪个版本更快?
以下是我的一些hyperfine 结果。
C:\my_projects\untitled>hyperfine branchless.exe
Benchmark #1: branchless.exe
Time (mean ± σ): 5.4 ms ± 0.2 ms [User: 2.2 ms, System: 3.2 ms]
Range (min … max): 4.9 ms … 6.1 ms 230 runs
C:\my_projects\untitled>hyperfine branch.exe
Benchmark #1: branch.exe
Time (mean ± σ): 6.1 ms ± 0.7 ms [User: 2.2 ms, System: 3.7 ms]
Range (min … max): 5.0 ms … 9.7 ms 225 runs
C:\my_projects\untitled>hyperfine branch.exe
Benchmark #1: branch.exe
Time (mean ± σ): 5.5 ms ± 0.3 ms [User: 2.1 ms, System: 3.5 ms]
Range (min … max): 4.9 ms … 7.0 ms 211 runs
C:\my_projects\untitled>hyperfine branch.exe
Benchmark #1: branch.exe
Time (mean ± σ): 5.6 ms ± 0.4 ms [User: 2.0 ms, System: 3.9 ms]
Range (min … max): 4.8 ms … 7.0 ms 217 runs
Warning: Command took less than 5 ms to complete. Results might be inaccurate.
C:\my_projects\untitled>hyperfine branch.exe
Benchmark #1: branch.exe
Time (mean ± σ): 5.7 ms ± 0.3 ms [User: 1.9 ms, System: 4.0 ms]
Range (min … max): 5.0 ms … 6.6 ms 220 runs
C:\my_projects\untitled>hyperfine branchless.exe
Benchmark #1: branchless.exe
Time (mean ± σ): 5.6 ms ± 0.3 ms [User: 1.9 ms, System: 3.9 ms]
Range (min … max): 4.8 ms … 6.9 ms 219 runs
C:\my_projects\untitled>hyperfine branchless.exe
Benchmark #1: branchless.exe
Time (mean ± σ): 5.8 ms ± 0.3 ms [User: 1.5 ms, System: 4.0 ms]
Range (min … max): 5.2 ms … 7.3 ms 224 runs
C:\my_projects\untitled>
【问题讨论】:
-
how does people usually determine which version of a function is faster?在这种简单的情况下,查看生成的程序集。请注意,您不是单独对代码进行基准测试,而是对编译器+编译器选项+代码的组合进行基准测试。 -
查看程序集,您可以看到两个代码示例使用了多少指令。对于这个最小的代码,我认为(取决于您正在工作的平台)每个汇编程序命令都需要一个时钟周期。
-
其次,您似乎也在测量
printf的执行时间,这很可能比您的函数的执行时间高几个数量级。这将隐藏功能之间任何差异的影响。这就像试图在太阳旁边看到一颗遥远恒星的光:它是不可见的。 -
为什么不直接做
int tmp = num > 10; return num * num * tmp;?这更简单,可能更快。此外,条件跳转只有在现代处理器上难以预测时才会变慢。但是,由于num > 10在计算square(38)时始终为真,因此带有分支的版本应该很快。 -
@KrisVandermotten:更重要的是,他们正在测量整个过程启动和退出的总时间!!这通常包括一个或两个页面错误,以及动态链接。使用
printf可能是其中的一个重要部分,尤其是在慢速终端窗口中的 Windows 上,但是,是的,完全疯了。不是更通用的Idiomatic way of performance evaluation? 的完全复制品,但它指出了几个方法问题,以及试图为这么短的东西找到一个简单的一维成本的根本缺陷。
标签: c performance performance-testing benchmarking microbenchmark