在Erlang OTP 18上实现并发/可扩展性的最佳时钟或数字生成器功能?

use*_*020 5 concurrency erlang performance time benchmarking

目前正在将erlang应用程序从版本17转换为18.可伸缩性和性能是设计中的主要指令.该程序需要一种方法来区分和排序新输入,或者使用许多独特的单调递增数字(连续的数据流)或其他一些机制.当前版本(17)没有使用now(),因为它是一个可伸缩性瓶颈(全局锁定),所以它是通过读取时钟和做其他事情来为进入的数据生成标记.我是试图找出在18年做到这一点的最好方法,并从我运行的测试中得到一些有趣的结果.

我期望erlang:unique_integer([monotonic])得到不好的结果,因为我希望它有像now()这样的全局锁.假设可以并行读取时钟,我期望其中一个时钟功能具有最佳结果.相反,erlang:unique_integer([monotonic])从我基准测试的所有函数中获得最佳结果,并且时钟函数更糟糕.

有人可以解释结果,告诉我哪些erlang函数应该给出最好的结果,以及哪些东西(时钟,数字生成器等)是否全局锁定在18?此外,如果您发现我的测试方法存在任何问题,请务必指出它们.

测试平台/方法

windows 7 64 bit
erlang otp 18 (x64)
2 intel cores (celeron 1.8GHz)
2 erlang processes spawned to run each test function concurrently 500000 times
    for a total of 1000000 times, timed with timer:tc
each test run 10 times in succession and all results recorded
Run Code Online (Sandbox Code Playgroud)

BASELINE TEST,SEQENTIAL

erlang:unique_integer([monotonic])
47000-94000
Run Code Online (Sandbox Code Playgroud)

平行时代

erlang:unique_integer([monotonic])
~94000

ets:update_counter
450000-480000

erlang:monotonic_time
202000-218000

erlang:system_time
218000-234000

os:system_time
124000-141000

calendar:universal_time
453000-530000
Run Code Online (Sandbox Code Playgroud)

Hyn*_*dil 2

如果您询问测试方法,我希望您也包括您的代码,因为基准代码中可能存在一个小错误,这可能会破坏结果。所以我写了一个并制作了要点,这样我们就可以使用相同的代码来比较结果。YMMV 特别是因为我使用 Linux 和计时器强烈依赖于底层操作系统。有我的结果:

$ uname -a
Linux hynek-notebook 4.1.0-1-amd64 #1 SMP Debian 4.1.3-1 (2015-08-03) x86_64 GNU/Linux
$ grep 'model name' /proc/cpuinfo 
model name      : Intel(R) Core(TM) i5 CPU       M 520  @ 2.40GHz
model name      : Intel(R) Core(TM) i5 CPU       M 520  @ 2.40GHz
model name      : Intel(R) Core(TM) i5 CPU       M 520  @ 2.40GHz
model name      : Intel(R) Core(TM) i5 CPU       M 520  @ 2.40GHz
$ erl
Erlang/OTP 18 [erts-7.0] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]

Eshell V7.0  (abort with ^G)
1> c(test).
{ok,test}
2> test:bench_all(1).
[{unique_monotonic_integer,{38341,39804}},
 {update_counter,{158248,159319}},
 {monotonic_time,{217531,218272}},
 {system_time,{224630,226960}},
 {os_system_time,{53489,53691}},
 {universal_time,{114125,116324}}]
3> test:bench_all(2).
[{unique_monotonic_integer,{40109,40238}},
 {update_counter,{307393,338993}},
 {monotonic_time,{120024,121612}},
 {system_time,{123634,124928}},
 {os_system_time,{29606,29992}},
 {universal_time,{177544,178820}}]
4> test:bench_all(20).
[{unique_monotonic_integer,{23796,26364}},
 {update_counter,{514835,527087}},
 {monotonic_time,{91916,93662}},
 {system_time,{94615,96249}},
 {os_system_time,{27194,27598}},
 {universal_time,{317353,340187}}]
5>
Run Code Online (Sandbox Code Playgroud)

我首先要注意的是,只有erlang:unique_integer/0,1ets:update_counter/3,4,5产生独特的价值。甚至erlang:monotonic_time/0可以生成两个相同的时间戳!因此,如果您想要唯一的编号,除了使用之外别无选择erlang:unique_integer/0,1。如果您想要唯一的单调时间戳,您可以使用{erlang:monotonic_time(), erlang:unique_integer()},或者如果您不需要时间部分,您可以使用erlang:unique_integer([monotonic]). 如果你不需要单调和独特,你可以使用其他选项。因此,如果您需要唯一的单调数,那么只有一个不错的选择,那就是erlang:unique_integer([monotonic])

第二次我应该注意到,生成两个进程不足以测试可扩展性。正如您所看到的,当我使用os:timestamp/020 个进程时,它们开始赶上erlang:unique_integer/0,1. 还有一个问题。我们都使用只有两个 CPU 的硬件。测试可扩展性的数量太少了。想象一下,在具有 64 个或更多核心的硬件上,结果会是什么样子。

编辑:使用{write_concurrency, true}会有所改善ets:update_counter,但仍然远远超出erlang:unique_integer/0,1

2> test:bench(test:update_counter(),1).
{203830,213657}
3> test:bench(test:update_counter(),2).
{129148,140627}
4> test:bench(test:update_counter(),20).
{471858,501198}
Run Code Online (Sandbox Code Playgroud)