【问题标题】:Testing Erlang function performance with timer用定时器测试 Erlang 函数的性能
【发布时间】:2015-11-02 17:11:47
【问题描述】:

我正在使用timer:tc/3 在紧密循环(比如 5000 次迭代)中测试函数的性能:

{Duration_us, _Result} = timer:tc(M, F, [A])

这将返回函数的持续时间(以微秒为单位)和结果。为了论证,持续时间是 N 微秒。

然后我对迭代结果进行简单的平均计算。

如果我在timer:tc/3 调用之前调用timer:sleep(1) 函数调用,所有迭代的平均持续时间总是> 没有睡眠的平均值:

timer:sleep(1),
timer:tc(M, F, [A]).

这对我来说没有多大意义,因为 timer:tc/3 函数应该是原子的,而不关心之前发生的任何事情。

谁能解释一下这个奇怪的功能?它是否与调度和减少有某种关系?

【问题讨论】:

标签: performance timer erlang


【解决方案1】:

你的意思是这样的:

4> foo:foo(10000).

地点:

-module(foo).
-export([foo/1, baz/1]).

foo(N) -> TL = bar(N), {TL,sum(TL)/N} .

bar(0) -> [];
bar(N) ->
    timer:sleep(1),
    {D,_} = timer:tc(?MODULE, baz, [1000]),
    [D|bar(N-1)]
    .

baz(0) -> ok;
baz(N) -> baz(N-1).

sum([]) -> 0;
sum([H|T]) -> H + sum(T).

我试过这个,很有趣。使用 sleep 语句,timer:tc/3 返回的平均时间为 19 到 22 微秒,而注释掉 sleep 后,平均值下降到 4 到 6 微秒。相当戏剧性!

我注意到计时中有人工制品,所以像这样的事件(这些数字是 timer:tc/3 返回的单个微秒计时)并不少见:

---- snip ----
  5,5,5,6,5,5,5,6,5,5,5,6,5,5,5,5,4,5,5,5,5,5,4,5,5,5,5,6,5,5,
  5,6,5,5,5,5,5,6,5,5,5,5,5,6,5,5,5,6,5,5,5,5,5,5,5,5,5,5,4,5,
  5,5,5,6,5,5,5,6,5,5,7,8,7,8,5,6,5,5,5,6,5,5,5,5,4,5,5,5,5,
  14,4,5,5,4,5,5,4,5,4,5,5,5,4,5,5,4,5,5,4,5,4,5,5,5,4,5,5,4,
  5,5,4,5,4,5,5,4,4,5,5,4,5,5,4,4,4,4,4,5,4,5,5,4,5,5,5,4,5,5,
  4,5,5,4,5,4,5,5,5,4,5,5,4,5,5,4,5,4,5,4,5,4,5,5,4,4,4,4,5,4,
  5,5,54,22,26,21,22,22,24,24,32,31,36,31,33,27,25,21,22,21,
  24,21,22,22,24,21,22,21,24,21,22,22,24,21,22,21,24,21,22,21,
  23,27,22,21,24,21,22,21,24,22,22,21,23,22,22,21,24,22,22,21,
  24,21,22,22,24,22,22,21,24,22,22,22,24,22,22,22,24,22,22,22,
  24,22,22,22,24,22,22,21,24,22,22,21,24,21,22,22,24,22,22,21,
  24,21,23,21,24,22,23,21,24,21,22,22,24,21,22,22,24,21,22,22,
  24,22,23,21,24,21,23,21,23,21,21,21,23,21,25,22,24,21,22,21,
  24,21,22,21,24,22,21,24,22,22,21,24,22,23,21,23,21,22,21,23,
  21,22,21,23,21,23,21,24,22,22,22,24,22,22,41,36,30,33,30,35,
  21,23,21,25,21,23,21,24,22,22,21,23,21,22,21,24,22,22,22,24,
  22,22,21,24,22,22,22,24,22,22,21,24,22,22,21,24,22,22,21,24,
  22,22,21,24,21,22,22,27,22,23,21,23,21,21,21,23,21,21,21,24,
  21,22,21,24,21,22,22,24,22,22,22,24,21,22,22,24,21,22,21,24,
  21,23,21,23,21,22,21,23,21,23,22,24,22,22,21,24,21,22,22,24,
  21,23,21,24,21,22,22,24,21,22,22,24,21,22,21,24,21,22,22,24,
  22,22,22,24,22,22,21,24,22,21,21,24,21,22,22,24,21,22,22,24,
  24,23,21,24,21,22,24,21,22,21,23,21,22,21,24,21,22,21,32,31,
  32,21,25,21,22,22,24,46,5,5,5,5,5,4,5,5,5,5,6,5,5,5,5,5,5,4,
  6,5,5,5,6,5,5,5,5,5,5,5,6,5,5,5,5,4,5,4,5,5,5,5,6,5,5,5,5,5,
  5,5,6,5,5,5,5,5,5,5,6,5,5,5,5,4,6,4,6,5,5,5,5,5,5,4,6,5,5,5,
  5,4,5,5,5,5,5,5,6,5,5,5,5,4,5,5,5,5,5,5,6,5,5,5,5,5,5,5,6,5,
  5,5,5,4,5,5,6,5,5,5,6,5,5,5,5,5,5,5,6,5,5,5,6,5,5,5,5,5,5,5,
  6,5,5,5,5,4,5,4,5,5,5,5,6,5,5,5,5,5,5,4,5,4,5,5,5,5,5,6,5,5,
  5,5,4,5,4,5,5,5,5,6,5,5,5,5,5,5,5,6,5,5,5,5,5,5,5,6,5,5,5,5,
---- snip ----

我认为这就是您所指的效果,尽管当您说 always > N 时,它是 always,还是只是大部分?无论如何,我并不总是这样。

上面的结果提取是没有睡眠的。通常在使用睡眠定时器时:tc/3 在没有睡眠的情况下大部分时间返回像 4 或 5 这样的低时间,但有时像 22 这样的大时间,并且在睡眠到位的情况下它通常是像 22 这样的大时间,偶尔会有一批低时间.

为什么会发生这种情况当然不明显,因为睡眠实际上只是意味着屈服。我想知道这一切是否都取决于 CPU 缓存。毕竟,尤其是在一台不忙的机器上,人们可能会期望没有睡眠的情况下可以一次性执行大部分代码,而不会将其移动到另一个内核,而无需对内核做太多其他事情,从而最大限度地利用超出缓存...但是当您休眠并因此产生并稍后返回时,缓存命中的机会可能会大大降低。

【讨论】:

  • 是的 - 这几乎是一般的想法。我又看了一遍,我正在平均结果——这更符合你所看到的。我更新了问题以反映我使用平均值的事实。
  • 是的,我当然从平均数开始,直到我意识到原始数据有伪影,因此与平均数一样有趣。
【解决方案2】:

测量性能是一项复杂的任务,尤其是在新硬件和现代操作系统中。有很多事情会影响你的结果。首先,你并不孤单。当您在台式机或笔记本电脑上进行测量时,可能会有其他进程干扰您的测量,包括系统进程。第二件事,有硬件本身。现代 CPU 具有许多很酷的功能,可以控制性能和功耗。它们可以在过热之前短时间内提高性能,当同一芯片上的其他 CPU 或同一 CPU 上的其他超线程上没有工作时,它们可以提高性能。另一方面,当没有足够的工作并且CPU对突然变化的反应不够快时,它们可以进入省电模式。很难说是不是你的情况,但是对于以前的工作或缺乏它不会影响你的测量来说是幼稚的。您应始终注意在稳定状态下测量足够长的时间(至少几秒钟),并尽可能多地去除其他可能影响测量的东西。 (不要忘记 Erlang 中的 GC。)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-02-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-10
    • 1970-01-01
    相关资源
    最近更新 更多