# 前言

ACM 时,曾经测试过 Lutece (opens new window) 的速度,毕竟 OJ 运算速度直接关系到了每一次写的代码的复杂度。

脱坑 ACM 以后,在编程珠玑里面看到了自己的电脑的运算速度应该作为常识记住,于是就花一天的时间测了一下,并进行了简要分析。附测试的代码完整数据

本文涉及到的次数并不准确,因为没有计算 for 循环递增量以及执行 for 循环的时间。本文只是粗略统计一下电脑计算速度的数量级,以及比较各状态下的速度。

# 测试环境

测试电脑:Surface Book CPU:Intel(R) Core(TM) i7-6600U CPU @ 2.60GHz 测试线程数:1 操作系统:Windows 10 10.0.18912.1001 (64 bit) 编译器:MSVC 2017 编译选项(除测试架构和编译选项时):X64, Release, O2 优化 每次测试组数:20(程序启动的前两组速度有明显的差异,因此统计运算速度时之计后 18 组)

# 结论

  1. 对于架构和编译选项来说,O2 优化能带给程序 3-9 倍的速度提升;同样的代码编译出的 64 位程序较 32 位快 13% - 218%;
  2. 整型的运算的加、乘都是 1e9 次/秒数量级的,浮点类型的加、乘都是 1e8 次/秒数量级。对于 log 和 sqrt 类型,整型可能会慢于浮点类型,但都在 1e7次/秒 数量级;
  3. 极端数据范围的数据运算不影响整型运算的速度,略微影响浮点型运算的速度(10%);
  4. 高精度运算在 long long 范围内加,比 C 自带整型类型慢三个数量级;100 位的加法也只比前面慢不到一个数量级(后经测试,乘法慢于加法,但差别不大,虽然是乘法 O(n2)O(n^2),但 n=100 也较小)。

# 测试一:架构和编译选项

测试的是 long long 类型从 1 加到 1e9。以下是测试结果:

1s 运算次数 debug(Od) release(Od) release(O1) release(O2)
x86 3.05E+08 9.55E+08
x64 3.47E+08 3.61E+08 1.9E+09 3.04E+09

注:Od 为禁用优化,O1 为最大优化(优选大小),O2 为最大优化(优选速度)。

可以观察到,在没有优化的情况下,debug 和 release 的区别其实不大。但是开了 O2 优化以后,程序获得了 3-9 倍的加速。

而对于 x86 和 x64 来说,无优化下,x64 略快于 x86 (约 13%);而在 O2 优化下,x64 较 x86 快了 218%。

# 测试二:数据类型和运算

测试的类型有 intlong longfloatdouble 和一个自定义的高精度运算 BigInteger(源码包含在测试代码中)。测试的运算有 +*cmath 中的 sqrt()log()

intlong longdouble 都是从 1 测到 1e9。
在进行 float 的测试时发现使用 i++,当数据达到 2241.6×1072^{24} \approx 1.6 \times 10^7 时,i 的值不会发生变化(1 溢出了,这和和浮点数的记法有关)。于是对于 float 的测试都是从 1 测到 1e7,重复 100 次。
BigInteger 由于太慢,只从 1 测到 1e7。

以下是测试结果:

1s 运算次数 int long long float double BigInteger
+ 3.26E+09 3.04E+09 5.92E+08 5.20E+08 1.51E+06
* 1.99E+09 2.00E+09 6.36E+08 6.43E+08 1.11E+06
log() 3.70E+07 4.94E+07 5.69E+07 5.35E+07 \
sqrt() 6.79E+07 6.83E+07 7.63E+07 5.59E+07 \

从数量级上来看,整数的加和乘能达到 1e9 次/秒 的数量级,而浮点数也能达到 1e8 次/秒 的数量级。
相比下,高精度就慢得多了,只能达到 1e6 次/秒 的数量级,比整数运算低了 3 个数量级。(所以 ACM 中,能用 __int128 就别用高精度)。

对于不同精度的整型和浮点型数据,加法上,intlong long 快约 7%,floatdouble 快约 14%。但是乘法上,long long 甚至比 int 快不到 1%,doublefloat 快 1%。都不是很明显。

对于 logsqrt 函数来说,intlong long 反而可能比 floatdouble 慢。这可能是因为 C 语言并没有实现整型的 logsqrt 函数,而是把整型强转为浮点数来进行计算。但是这几种数据类型的运算仍然在一个数量级。

# 测试三:数据范围对运算速度的影响

由于浮点数有移位的过程,所以可能数据范围对运算速度也有一定影响。

本次测试选用了 long long(做对照),floatdoubleBigInteger,分别从 1e7, 1e10, 1e14 开始累加量为 1,累加 1e9 次(或 1e7次,循环 100 遍)(BigInteger 由于过慢,只累加 1e7 次;float 由于上面提到的 1 溢出问题,只计算从 1e7 累加至 1.5e7)。

1s 运算次数 long long float double BigInteger
1e7 2.03E+09 5.87E+08 5.97E+08 1.70E+06
1e10 2.02E+09 5.36E+08 1.08E+06
1e14 2.11E+09 5.31E+08 9.78E+05
1e100 3.32E+05

可以注意到,long long 的数据范围和运算速度关系确实不大;而double 会有影响:数据范围从 1e7 扩大到 1e14,运算速度下降了 11%,但是仍然在一个数量级内。

BigInteger 的影响同样也不大,即使是从 1e7 加到 1e100,运算速度也只下降了一个数量级。可能是其他操作比较慢