查看源码 基准测试
基准测试的主要目的是找出给定算法或函数的哪个实现最快。基准测试远非一门精确的科学。当今的操作系统通常运行难以关闭的后台任务。缓存和多核 CPU 也不利于基准测试。最好在基准测试时以单用户模式运行 UNIX 计算机,但至少对于临时测试来说,这很不方便。
使用 erlperf
一个有用的基准测试工具是 erlperf(文档)。它使查找哪个代码更快变得简单。例如,以下是如何比较两种生成随机字节的方法
% erlperf 'rand:bytes(2).' 'crypto:strong_rand_bytes(2).'
Code || QPS Time Rel
rand:bytes(2). 1 7784 Ki 128 ns 100%
crypto:strong_rand_bytes(2). 1 2286 Ki 437 ns 29%
从 Time 列,我们可以读出平均调用 rand:bytes(2)
执行时间为 128 纳秒,而调用 crypto:strong_rand_bytes(2)
执行时间为 437 纳秒。
从 QPS 列,我们可以读出每秒可以进行的调用次数。对于 rand:bytes(2)
,它为每秒 7,784,000 次调用。
Rel 列显示相对差异,100%
表示最快的代码。
当一次生成两个随机字节时,rand:bytes/1
比 crypto:strong_rand_bytes/1
快三倍以上。假设我们真的需要强随机数,并且需要尽可能快地获得它们,我们该怎么办?一种方法是一次生成多于两个字节。
% erlperf 'rand:bytes(100).' 'crypto:strong_rand_bytes(100).'
Code || QPS Time Rel
rand:bytes(100). 1 2124 Ki 470 ns 100%
crypto:strong_rand_bytes(100). 1 1915 Ki 522 ns 90%
当我们一次生成 100 个字节时,rand:bytes/1
仍然更快,但相对差异较小。
% erlperf 'rand:bytes(1000).' 'crypto:strong_rand_bytes(1000).'
Code || QPS Time Rel
crypto:strong_rand_bytes(1000). 1 1518 Ki 658 ns 100%
rand:bytes(1000). 1 284 Ki 3521 ns 19%
当我们一次生成 1000 个字节时,crypto:strong_rand_bytes/1
现在是最快的。
使用 Erlang/OTP 功能进行基准测试
基准测试可以测量挂钟时间或 CPU 时间。
timer:tc/3
测量挂钟时间。挂钟时间的优点是 I/O、交换以及操作系统内核中的其他活动都包含在测量中。缺点是测量值通常变化很大。通常最好多次运行基准测试并记录最短时间,这是在最佳情况下可能实现的最短时间。statistics(runtime)
测量在 Erlang 虚拟机中花费的 CPU 时间。CPU 时间的优点是结果在每次运行中更加一致。缺点是不包括在操作系统内核中花费的时间(例如交换和 I/O)。因此,如果涉及任何 I/O(文件或套接字),则测量 CPU 时间会产生误导。
进行挂钟测量和 CPU 时间测量可能都是一个好主意。
一些最后的建议
- 两种测量类型的粒度都可能很高。因此,请确保每次单独的测量至少持续几秒钟。
- 为了使测试公平,每次新的测试运行都应在其自己的新创建的 Erlang 进程中运行。否则,如果所有测试都在同一个进程中运行,则后面的测试将从更大的堆大小开始,因此可能进行更少的垃圾收集。还要考虑在每次测试之间重新启动 Erlang 模拟器。
- 不要假设在计算机体系结构 X 上给定算法的最快实现也是在计算机体系结构 Y 上最快的实现。