- Notifications
You must be signed in to change notification settings - Fork0
Ane (atomics and ets) is a library to share mutable data efficiently by utilizing atomics and ets modules.
License
gyson/ane
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
Ane (atomics and ets) is a library to share mutable data efficiently byutilizingatomics andets modules.
- It stores all data with versionstamp in ETS table.
- It keeps a cached copy with versionstamp locally.
- It uses atomics to save latest versionstamp and syncs data between ETS table and local cache.
- Read operation would use cached data if cache hits and fallback to ETS lookup if cache expires.
- Write operation would update ETS table and versionstamp in atomics array.
Similar to atomics standalone,
- Ane's read/write operations guarantee atomicity.
- Ane's read/write operations are mutually ordered.
- Ane uses one-based index.
Compare to atomics standalone,
- Ane could save arbitrary term instead of 64 bits integer.
Compare to ETS standalone,
Ane has much faster read operation when cache hit (this is common for read-heavy application).
- It needs 1 Map operation and 1 atomics operation.
- It does not need to copy data from ETS table.
- It does not need to lookup from ETS table, which could make underneath ETS table's write operation faster.
- Benchmarking showed that it's 2 ~ 10+ times faster.
Ane could have slightly slower read operation when cache missed or expired.
- It needs 2 Map operations, 1+ atomics operations and 1+ ETS operations.
- Ane could be faster for "hot key" case.
Ane could have slower write operation.
- It needs to do 2 ETS operations and 2+ atomics operations.
- Ane could be faster for "hot key" case.
Ane has much faster read/write operations for "hot key" case.
- ETS table performance degrades when a key is too hot due to internal locking.
- Ane avoids "hot key" issue by distributing read/write operations to different keys in underneath ETS table.
Ane only supports
:atomics
-like one-based index as key.- I feel it's possible to extend it to be
:ets
-like arbitrary key with some extra complexity. But I do not have that need at the moment.
- I feel it's possible to extend it to be
Compare topersistent_term,
Like persistent_term, Ane's read operation with cache hit is lock-free and copying-free (no need to copy since data exists in local cache).
Unlike persistent_term, Ane's read operation with cache miss/expire would require copy data from ETS table to the heap of current process.
Unlike persistent_term, Ane's write operation is fast and won't trigger global GC.
Note: it requires OTP 21.2 for:atomics
, which was released on Dec 12, 2018.
It can be installed by adding:ane
to your list of dependencies inmix.exs
:
defdepsdo[{:ane,"~> 0.1.1"}]end
API reference can be found athttps://hexdocs.pm/ane/Ane.html.
iex(1)>a=Ane.new(1){#Reference<0.376557974.4000972807.196270>,#Reference<0.376557974.4000972807.196268>,#Reference<0.376557974.4000972807.196269>, %{}}iex(2)>Ane.put(a,1,"hello"):okiex(3)>{a,value} =Ane.get(a,1){{#Reference<0.376557974.4000972807.196270>,#Reference<0.376557974.4000972807.196268>,#Reference<0.376557974.4000972807.196269>, %{1 => {1, "hello"}}}, "hello"}iex(4)>value"hello"iex(5)>Ane.put(a,1,"world"):okiex(6)>{a,value} =Ane.get(a,1){{#Reference<0.376557974.4000972807.196270>,#Reference<0.376557974.4000972807.196268>,#Reference<0.376557974.4000972807.196269>, %{1 => {2, "world"}}}, "world"}iex(7)>value"world"
Generally, Ane is faster for read-heavy case and ETS standalone is faster for write-heavy case. This library provide a way to switch between them seamlessly.
By specifymode: :ets
as following, it will use ETS standalone instead:
iex(1)>a=Ane.new(1,mode::ets){#Reference<0.2878440188.2128478212.58871>, 1}iex(2)>Ane.put(a,1,"hello"):okiex(3)>{a,value} =Ane.get(a,1){{#Reference<0.2878440188.2128478212.58871>, 1}, "hello"}iex(4)>value"hello"iex(5)>Ane.put(a,1,"world"):okiex(6)>{a,value} =Ane.get(a,1){{#Reference<0.2878440188.2128478212.58871>, 1}, "world"}iex(7)>value"world"
This is useful for comparing performance between Ane and ETS standalone.
Theread_concurrency
andwrite_concurrency
from ETS table are important configurations for performance tuning. You can adjust it while creating Ane instance like following:
ane=Ane.new(1,read_concurrency:true,write_concurrency:true)
These options would be passed to underneath ETS table. You can read more docs aboutread_concurrency
andwrite_concurrency
aterlang ets docs.
Benchmarking script is available atbench/comparison.exs
.
Following is the benchmarking result for comparing Ane and ETS standalone with 90% read operations and 10% write operations:
$ mix run bench/comparison.exsOperating System: macOS"CPU Information: Intel(R) Core(TM) i7-3720QM CPU @ 2.60GHzNumber of Available Cores: 8Available memory: 16 GBElixir 1.7.4Erlang 21.2Benchmark suite executing with the following configuration:warmup: 2 stime: 10 smemory time: 0 μsparallel: 16inputs: none specifiedEstimated total run time: 24 sBenchmarking size=16, mode=ane, Ane.get=90%, Ane.put=10.0%, read_concurrency=true, write_concurrency=true, info_size=100...Benchmarking size=16, mode=ets, Ane.get=90%, Ane.put=10.0%, read_concurrency=true, write_concurrency=true, info_size=100...Name ips average deviation median 99th %size=16, mode=ane, Ane.get=90%, Ane.put=10.0%, read_concurrency=true, write_concurrency=true, info_size=100 26.76 37.37 ms ±37.32% 36.79 ms 72.50 mssize=16, mode=ets, Ane.get=90%, Ane.put=10.0%, read_concurrency=true, write_concurrency=true, info_size=100 9.66 103.55 ms ±37.82% 98.66 ms 187.74 msComparison:size=16, mode=ane, Ane.get=90%, Ane.put=10.0%, read_concurrency=true, write_concurrency=true, info_size=100 26.76size=16, mode=ets, Ane.get=90%, Ane.put=10.0%, read_concurrency=true, write_concurrency=true, info_size=100 9.66 - 2.77x slower
Following is the benchamrking result for comparing Ane and ETS standalone for "hot key" issue:
$ mix run bench/comparison.exsOperating System: macOS"CPU Information: Intel(R) Core(TM) i7-3720QM CPU @ 2.60GHzNumber of Available Cores: 8Available memory: 16 GBElixir 1.7.4Erlang 21.2Benchmark suite executing with the following configuration:warmup: 2 stime: 10 smemory time: 0 μsparallel: 16inputs: none specifiedEstimated total run time: 24 sBenchmarking size=1, mode=ane, Ane.get=90%, Ane.put=10.0%, read_concurrency=true, write_concurrency=true, info_size=100...Benchmarking size=1, mode=ets, Ane.get=90%, Ane.put=10.0%, read_concurrency=true, write_concurrency=true, info_size=100...Name ips average deviation median 99th %size=1, mode=ane, Ane.get=90%, Ane.put=10.0%, read_concurrency=true, write_concurrency=true, info_size=100 27.03 37.00 ms ±45.40% 36.15 ms 71.12 mssize=1, mode=ets, Ane.get=90%, Ane.put=10.0%, read_concurrency=true, write_concurrency=true, info_size=100 1.33 754.31 ms ±25.91% 762.88 ms 1212.87 msComparison:size=1, mode=ane, Ane.get=90%, Ane.put=10.0%, read_concurrency=true, write_concurrency=true, info_size=100 27.03size=1, mode=ets, Ane.get=90%, Ane.put=10.0%, read_concurrency=true, write_concurrency=true, info_size=100 1.33 - 20.39x slower
Write operation (Ane.put
) includes one:ets.insert
operation and one:ets.delete
operation.When the process runningAne.put
is interrupted (e.g. by:erlang.exit(pid, :kill)
), garbagedata could be generated if it finished insert operation but did not start delete operation. Thesegarbabge data could be removed by callingAne.clear
(periodically if it needs to handle constantly interruptions).
# type check with dialyzermix dialyzer# type check with ex_typemixtype
MIT
About
Ane (atomics and ets) is a library to share mutable data efficiently by utilizing atomics and ets modules.
Topics
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Releases
Packages0
Uh oh!
There was an error while loading.Please reload this page.