atomic-counter 0.1 → 0.1.1
raw patch · 7 files changed
+904/−706 lines, 7 filesdep ~tastydep ~tasty-benchPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependency ranges changed: tasty, tasty-bench
API changes (from Hackage documentation)
- Control.Concurrent.Counter.Unlifted: set :: Counter s -> Int# -> State# s -> State# s
+ Control.Concurrent.Counter.Unlifted: set :: Counter s -> Int# -> State# s -> (# State# s #)
Files
- Changelog.md +6/−0
- Counter.cmm +96/−0
- Readme.md +708/−693
- atomic-counter.cabal +21/−4
- bench/BenchMain.hs +12/−2
- src/Control/Concurrent/Counter/Lifted/ST.hs +1/−1
- src/Control/Concurrent/Counter/Unlifted.hs +60/−6
Changelog.md view
@@ -1,3 +1,9 @@+# 0.1.1++- On non-javascript platforms the counter is implemented directly in+ CMM instead of using singleton primitive array. Likely to occupy+ less memory and perform faster (https://github.com/sergv/atomic-counter/pull/3).+ # 0.1 Initial release
+ Counter.cmm view
@@ -0,0 +1,96 @@+#include "Cmm.h"++INFO_TABLE(stg_Counter, 0, 1, MUT_PRIM, "Counter", "Counter") ()+{+ foreign "C" barf("stg_Counter entered!", NULL) never returns;+}++stg_newCounterzh (W_ x)+{+ P_ c;+ ("ptr" c) = ccall allocate(MyCapability() "ptr", BYTES_TO_WDS(SIZEOF_StgHeader + WDS(1)));+ SET_HDR(c, stg_Counter_info, CCCS);+ W_[c + SIZEOF_StgHeader] = x;+ return (c);+}++stg_atomicGetCounterzh (P_ c)+{+ W_ x;+ // load_seqcst64 is available since GHC 9.4+ (x) = prim %load_seqcst64(c + SIZEOF_StgHeader);+ return (x);+}++stg_atomicSetCounterzh (P_ c, W_ x)+{+ // store_seqcst64 is available since GHC 9.4+ prim %store_seqcst64(c + SIZEOF_StgHeader, x);+ return ();+}++stg_atomicAddCounterzh (P_ c, W_ x)+{+ W_ y;+#if __GLASGOW_HASKELL__ >= 907+ (y) = prim %fetch_add64(c + SIZEOF_StgHeader, x);+#else+ (y) = ccall hs_atomic_add64(c + SIZEOF_StgHeader, x);+#endif+ return (y);+}++stg_atomicSubCounterzh (P_ c, W_ x)+{+ W_ y;+#if __GLASGOW_HASKELL__ >= 907+ (y) = prim %fetch_sub64(c + SIZEOF_StgHeader, x);+#else+ (y) = ccall hs_atomic_sub64(c + SIZEOF_StgHeader, x);+#endif+ return (y);+}++stg_atomicAndCounterzh (P_ c, W_ x)+{+ W_ y;+#if __GLASGOW_HASKELL__ >= 907+ (y) = prim %fetch_and64(c + SIZEOF_StgHeader, x);+#else+ (y) = ccall hs_atomic_and64(c + SIZEOF_StgHeader, x);+#endif+ return (y);+}++stg_atomicOrCounterzh (P_ c, W_ x)+{+ W_ y;+#if __GLASGOW_HASKELL__ >= 907+ (y) = prim %fetch_or64(c + SIZEOF_StgHeader, x);+#else+ (y) = ccall hs_atomic_or64(c + SIZEOF_StgHeader, x);+#endif+ return (y);+}++stg_atomicXorCounterzh (P_ c, W_ x)+{+ W_ y;+#if __GLASGOW_HASKELL__ >= 907+ (y) = prim %fetch_xor64(c + SIZEOF_StgHeader, x);+#else+ (y) = ccall hs_atomic_xor64(c + SIZEOF_StgHeader, x);+#endif+ return (y);+}++stg_atomicNandCounterzh (P_ c, W_ x)+{+ W_ y;+#if __GLASGOW_HASKELL__ >= 907+ (y) = prim %fetch_nand64(c + SIZEOF_StgHeader, x);+#else+ (y) = ccall hs_atomic_nand64(c + SIZEOF_StgHeader, x);+#endif+ return (y);+}
Readme.md view
@@ -8,699 +8,714 @@ Good use case is a shared counter that multiple threads increment. -Operations translate to atomic CPU instructions which make these cells-way faster that even vanilla `IORef`. Value inside the cell is unboxed-which contributes to outperforming all other shared vars because they-box the value they store. For integers boxing is especially bad.--# Benchmark--### Summary--Depending on number of threads `Counter` from this package can be-up to 10 times faster than next best `TVar` from the `stm` package.--### Details--The benchmark is to spawn N threads each of which will increment the-same counter by 1 for a number of iterations.--Test setup: Intel i5-9600K CPU @ 3.70GHz, 6 cores, no hyperthreading, GHC 9.4.4.--NB `IORef inconsistent` is the benchmark that just reads and writes-`IORef` with the fastest functions the `IORef` supports. It gives-wrong results when multiple threads access `IORef` this way since-these reads and writes are not synchronized. It’s included only for-speed comparison purposes. All others do proper synchronization of-increments between the threads. Current package is shown as `Counter`.--```-$ cabal run --builddir /tmp/dist bench -- -j1 --timeout 30-All- Correctness: OK (15.52s)- +++ OK, passed 10000 tests.- Read/write contention with 10 iterations and 1 threads- Counter: OK (0.42s)- 785 ns ± 27 ns- IORef inconsistent: OK (0.16s)- 1.12 μs ± 95 ns- IORef atomic: OK (0.16s)- 1.08 μs ± 99 ns- MVar: OK (0.62s)- 1.15 μs ± 40 ns- TMVar: OK (0.50s)- 896 ns ± 31 ns- TVar: OK (0.15s)- 1.01 μs ± 92 ns- Addr: OK (0.74s)- 679 ns ± 48 ns- Read/write contention with 100 iterations and 1 threads- Counter: OK (0.15s)- 1.01 μs ± 89 ns- IORef inconsistent: OK (0.23s)- 3.38 μs ± 198 ns- IORef atomic: OK (0.44s)- 3.03 μs ± 91 ns- MVar: OK (0.24s)- 3.35 μs ± 167 ns- TMVar: OK (0.17s)- 4.64 μs ± 328 ns- TVar: OK (0.14s)- 4.09 μs ± 372 ns- Addr: OK (0.57s)- 1.04 μs ± 43 ns- Read/write contention with 1000 iterations and 1 threads- Counter: OK (0.17s)- 4.80 μs ± 389 ns- IORef inconsistent: OK (0.16s)- 18.4 μs ± 1.4 μs- IORef atomic: OK (0.19s)- 23.3 μs ± 2.1 μs- MVar: OK (0.13s)- 15.6 μs ± 1.3 μs- TMVar: OK (0.21s)- 49.5 μs ± 4.8 μs- TVar: OK (0.16s)- 38.3 μs ± 3.3 μs- Addr: OK (0.22s)- 5.88 μs ± 369 ns- Read/write contention with 10000 iterations and 1 threads- Counter: OK (0.18s)- 41.8 μs ± 2.7 μs- IORef inconsistent: OK (0.23s)- 224 μs ± 21 μs- IORef atomic: OK (0.18s)- 169 μs ± 15 μs- MVar: OK (0.20s)- 187 μs ± 15 μs- TMVar: OK (0.21s)- 409 μs ± 25 μs- TVar: OK (0.16s)- 304 μs ± 25 μs- Addr: OK (0.22s)- 52.7 μs ± 4.1 μs- Read/write contention with 10 iterations and 2 threads- Counter: OK (0.11s)- 7.05 μs ± 667 ns- IORef inconsistent: OK (0.22s)- 7.43 μs ± 599 ns- IORef atomic: OK (0.22s)- 7.19 μs ± 719 ns- MVar: OK (0.47s)- 7.42 μs ± 671 ns- TMVar: OK (0.24s)- 8.01 μs ± 773 ns- TVar: OK (0.22s)- 7.45 μs ± 585 ns- Addr: OK (0.23s)- 6.99 μs ± 601 ns- Read/write contention with 100 iterations and 2 threads- Counter: OK (0.16s)- 8.88 μs ± 749 ns- IORef inconsistent: OK (0.16s)- 12.1 μs ± 975 ns- IORef atomic: OK (0.16s)- 12.1 μs ± 736 ns- MVar: OK (0.13s)- 17.6 μs ± 1.7 μs- TMVar: OK (0.18s)- 29.7 μs ± 2.2 μs- TVar: OK (0.14s)- 24.8 μs ± 2.0 μs- Addr: OK (0.29s)- 8.52 μs ± 555 ns- Read/write contention with 1000 iterations and 2 threads- Counter: OK (0.85s)- 42.8 μs ± 1.2 μs- IORef inconsistent: OK (4.85s)- 140 μs ± 2.1 μs- IORef atomic: OK (0.30s)- 277 μs ± 12 μs- MVar: OK (0.33s)- 5.29 ms ± 224 μs- TMVar: OK (0.64s)- 270 μs ± 16 μs- TVar: OK (0.64s)- 306 μs ± 18 μs- Addr: OK (0.23s)- 47.4 μs ± 2.1 μs- Read/write contention with 10000 iterations and 2 threads- Counter: OK (0.11s)- 298 μs ± 28 μs- IORef inconsistent: OK (0.08s)- 1.01 ms ± 99 μs- IORef atomic: OK (0.99s)- 1.26 ms ± 19 μs- MVar: OK (0.45s)- 62.7 ms ± 3.9 ms- TMVar: OK (0.42s)- 2.96 ms ± 257 μs- TVar: OK (6.90s)- 3.26 ms ± 149 μs- Addr: OK (0.08s)- 527 μs ± 45 μs- Read/write contention with 10 iterations and 4 threads- Counter: OK (0.16s)- 15.0 μs ± 820 ns- IORef inconsistent: OK (0.17s)- 16.4 μs ± 946 ns- IORef atomic: OK (0.38s)- 19.2 μs ± 937 ns- MVar: OK (0.20s)- 27.5 μs ± 2.4 μs- TMVar: OK (0.10s)- 20.2 μs ± 1.3 μs- TVar: OK (0.19s)- 18.2 μs ± 781 ns- Addr: OK (0.09s)- 15.5 μs ± 1.4 μs- Read/write contention with 100 iterations and 4 threads- Counter: OK (0.40s)- 23.1 μs ± 1.8 μs- IORef inconsistent: OK (0.09s)- 56.3 μs ± 4.7 μs- IORef atomic: OK (0.24s)- 157 μs ± 3.9 μs- MVar: OK (0.16s)- 1.20 ms ± 115 μs- TMVar: OK (0.07s)- 193 μs ± 19 μs- TVar: OK (0.13s)- 214 μs ± 7.9 μs- Addr: OK (0.39s)- 22.3 μs ± 491 ns- Read/write contention with 1000 iterations and 4 threads- Counter: OK (0.17s)- 242 μs ± 13 μs- IORef inconsistent: OK (0.07s)- 485 μs ± 32 μs- IORef atomic: OK (2.02s)- 1.51 ms ± 54 μs- MVar: OK (0.19s)- 12.4 ms ± 1.2 ms- TMVar: OK (0.14s)- 2.01 ms ± 76 μs- TVar: OK (0.07s)- 1.92 ms ± 99 μs- Addr: OK (0.08s)- 233 μs ± 12 μs- Read/write contention with 10000 iterations and 4 threads- Counter: OK (0.17s)- 2.44 ms ± 82 μs- IORef inconsistent: OK (0.08s)- 4.49 ms ± 291 μs- IORef atomic: OK (0.84s)- 18.3 ms ± 941 μs- MVar: OK (0.39s)- 125 ms ± 12 ms- TMVar: OK (0.16s)- 18.7 ms ± 1.8 ms- TVar: OK (0.08s)- 18.8 ms ± 1.3 ms- Addr: OK (0.67s)- 2.27 ms ± 80 μs- Read/write contention with 10 iterations and 6 threads- Counter: OK (0.23s)- 24.1 μs ± 1.7 μs- IORef inconsistent: OK (0.25s)- 26.1 μs ± 1.4 μs- IORef atomic: OK (0.18s)- 41.1 μs ± 2.2 μs- MVar: OK (0.32s)- 46.8 μs ± 3.2 μs- TMVar: OK (0.53s)- 33.8 μs ± 2.5 μs- TVar: OK (0.07s)- 32.5 μs ± 2.7 μs- Addr: OK (0.12s)- 24.5 μs ± 2.4 μs- Read/write contention with 100 iterations and 6 threads- Counter: OK (0.29s)- 40.2 μs ± 3.1 μs- IORef inconsistent: OK (0.12s)- 100 μs ± 5.9 μs- IORef atomic: OK (0.14s)- 352 μs ± 30 μs- MVar: OK (0.12s)- 1.82 ms ± 168 μs- TMVar: OK (0.92s)- 544 μs ± 43 μs- TVar: OK (0.23s)- 561 μs ± 28 μs- Addr: OK (0.30s)- 38.9 μs ± 2.1 μs- Read/write contention with 1000 iterations and 6 threads- Counter: OK (0.87s)- 477 μs ± 4.3 μs- IORef inconsistent: OK (0.20s)- 1.01 ms ± 61 μs- IORef atomic: OK (0.85s)- 3.05 ms ± 154 μs- MVar: OK (0.62s)- 19.8 ms ± 1.5 ms- TMVar: OK (0.06s)- 4.65 ms ± 428 μs- TVar: OK (0.25s)- 4.78 ms ± 117 μs- Addr: OK (0.22s)- 477 μs ± 32 μs- Read/write contention with 10000 iterations and 6 threads- Counter: OK (2.01s)- 4.82 ms ± 131 μs- IORef inconsistent: OK (0.12s)- 9.87 ms ± 230 μs- IORef atomic: OK (6.61s)- 66.0 ms ± 24 ms- MVar: OK (1.31s)- 194 ms ± 3.5 ms- TMVar: OK (0.63s)- 52.3 ms ± 290 μs- TVar: OK (0.07s)- 48.5 ms ± 4.5 ms- Addr: OK (0.03s)- 4.82 ms ± 479 μs- Read/write contention with 10 iterations and 8 threads- Counter: OK (0.13s)- 26.3 μs ± 2.1 μs- IORef inconsistent: OK (0.16s)- 28.8 μs ± 1.8 μs- IORef atomic: OK (0.25s)- 56.2 μs ± 5.2 μs- MVar: OK (0.71s)- 95.8 μs ± 9.1 μs- TMVar: OK (0.19s)- 52.3 μs ± 1.8 μs- TVar: OK (0.18s)- 47.9 μs ± 2.0 μs- Addr: OK (0.13s)- 26.1 μs ± 1.9 μs- Read/write contention with 100 iterations and 8 threads- Counter: OK (0.05s)- 60.9 μs ± 5.5 μs- IORef inconsistent: OK (0.58s)- 120 μs ± 2.4 μs- IORef atomic: OK (0.62s)- 706 μs ± 9.6 μs- MVar: OK (0.16s)- 2.26 ms ± 209 μs- TMVar: OK (0.07s)- 571 μs ± 53 μs- TVar: OK (0.28s)- 556 μs ± 6.8 μs- Addr: OK (0.10s)- 58.8 μs ± 5.0 μs- Read/write contention with 1000 iterations and 8 threads- Counter: OK (0.28s)- 588 μs ± 17 μs- IORef inconsistent: OK (1.07s)- 1.15 ms ± 8.3 μs- IORef atomic: OK (5.86s)- 6.91 ms ± 986 μs- MVar: OK (0.71s)- 22.9 ms ± 842 μs- TMVar: OK (0.16s)- 6.10 ms ± 542 μs- TVar: OK (0.09s)- 5.76 ms ± 457 μs- Addr: OK (0.07s)- 624 μs ± 49 μs- Read/write contention with 10000 iterations and 8 threads- Counter: OK (0.65s)- 5.99 ms ± 259 μs- IORef inconsistent: OK (0.08s)- 11.4 ms ± 403 μs- IORef atomic: OK (9.50s)- 160 ms ± 43 ms- MVar: OK (0.74s)- 246 ms ± 21 ms- TMVar: OK (0.04s)- 50.4 ms ± 3.0 ms- TVar: OK (0.79s)- 58.5 ms ± 1.3 ms- Addr: OK (0.17s)- 5.51 ms ± 226 μs- Read/write contention with 10 iterations and 12 threads- Counter: OK (0.14s)- 29.8 μs ± 2.8 μs- IORef inconsistent: OK (0.08s)- 34.5 μs ± 2.8 μs- IORef atomic: OK (0.27s)- 71.0 μs ± 3.3 μs- MVar: OK (0.23s)- 229 μs ± 15 μs- TMVar: OK (0.87s)- 81.1 μs ± 697 ns- TVar: OK (0.21s)- 76.2 μs ± 5.6 μs- Addr: OK (0.14s)- 29.3 μs ± 2.3 μs- Read/write contention with 100 iterations and 12 threads- Counter: OK (0.25s)- 92.8 μs ± 7.8 μs- IORef inconsistent: OK (0.06s)- 179 μs ± 16 μs- IORef atomic: OK (0.43s)- 976 μs ± 30 μs- MVar: OK (0.43s)- 3.07 ms ± 172 μs- TMVar: OK (0.11s)- 946 μs ± 44 μs- TVar: OK (0.24s)- 1.05 ms ± 43 μs- Addr: OK (0.24s)- 90.4 μs ± 4.5 μs- Read/write contention with 1000 iterations and 12 threads- Counter: OK (0.11s)- 1.01 ms ± 37 μs- IORef inconsistent: OK (0.10s)- 1.76 ms ± 174 μs- IORef atomic: OK (2.73s)- 11.9 ms ± 434 μs- MVar: OK (0.28s)- 36.3 ms ± 2.7 ms- TMVar: OK (0.07s)- 10.6 ms ± 819 μs- TVar: OK (0.25s)- 9.24 ms ± 596 μs- Addr: OK (0.84s)- 902 μs ± 61 μs- Read/write contention with 10000 iterations and 12 threads- Counter: OK (0.25s)- 9.15 ms ± 853 μs- IORef inconsistent: OK (0.12s)- 18.7 ms ± 904 μs- IORef atomic: OK (2.28s)- 292 ms ± 19 ms- MVar: OK (5.38s)- 350 ms ± 4.1 ms- TMVar: OK (2.57s)- 104 ms ± 5.6 ms- TVar: OK (0.62s)- 104 ms ± 3.0 ms- Addr: OK (0.12s)- 9.65 ms ± 799 μs- Read/write contention with 10 iterations and 16 threads- Counter: OK (0.09s)- 40.4 μs ± 2.8 μs- IORef inconsistent: OK (0.11s)- 47.7 μs ± 2.9 μs- IORef atomic: OK (0.29s)- 133 μs ± 8.6 μs- MVar: OK (0.76s)- 390 μs ± 13 μs- TMVar: OK (0.08s)- 98.5 μs ± 6.9 μs- TVar: OK (1.18s)- 125 μs ± 10 μs- Addr: OK (0.09s)- 40.6 μs ± 2.7 μs- Read/write contention with 100 iterations and 16 threads- Counter: OK (0.08s)- 133 μs ± 6.5 μs- IORef inconsistent: OK (0.14s)- 275 μs ± 9.8 μs- IORef atomic: OK (1.04s)- 1.28 ms ± 31 μs- MVar: OK (0.57s)- 4.34 ms ± 330 μs- TMVar: OK (0.60s)- 1.39 ms ± 8.3 μs- TVar: OK (0.16s)- 1.40 ms ± 73 μs- Addr: OK (0.16s)- 130 μs ± 12 μs- Read/write contention with 1000 iterations and 16 threads- Counter: OK (0.30s)- 1.31 ms ± 82 μs- IORef inconsistent: OK (0.14s)- 2.51 ms ± 56 μs- IORef atomic: OK (3.03s)- 14.6 ms ± 1.4 ms- MVar: OK (0.71s)- 46.5 ms ± 3.9 ms- TMVar: OK (0.08s)- 13.8 ms ± 721 μs- TVar: OK (0.69s)- 12.8 ms ± 806 μs- Addr: OK (0.29s)- 1.33 ms ± 23 μs- Read/write contention with 10000 iterations and 16 threads- Counter: OK (0.16s)- 12.9 ms ± 1.1 ms- IORef inconsistent: OK (0.15s)- 25.5 ms ± 2.2 ms- IORef atomic: OK (8.01s)- 515 ms ± 51 ms- MVar: OK (1.57s)- 501 ms ± 31 ms- TMVar: OK (0.88s)- 149 ms ± 11 ms- TVar: OK (1.72s)- 145 ms ± 1.1 ms- Addr: OK (0.08s)- 11.4 ms ± 480 μs- Read/write contention with 10 iterations and 20 threads- Counter: OK (0.11s)- 50.2 μs ± 4.9 μs- IORef inconsistent: OK (0.10s)- 57.2 μs ± 3.8 μs- IORef atomic: OK (0.35s)- 192 μs ± 17 μs- MVar: OK (0.26s)- 533 μs ± 29 μs- TMVar: OK (0.10s)- 162 μs ± 14 μs- TVar: OK (0.05s)- 166 μs ± 11 μs- Addr: OK (0.09s)- 48.5 μs ± 3.3 μs- Read/write contention with 100 iterations and 20 threads- Counter: OK (0.05s)- 170 μs ± 14 μs- IORef inconsistent: OK (0.16s)- 334 μs ± 11 μs- IORef atomic: OK (0.53s)- 1.44 ms ± 112 μs- MVar: OK (0.35s)- 5.29 ms ± 217 μs- TMVar: OK (0.05s)- 1.90 ms ± 114 μs- TVar: OK (0.05s)- 1.75 ms ± 111 μs- Addr: OK (0.05s)- 131 μs ± 11 μs- Read/write contention with 1000 iterations and 20 threads- Counter: OK (0.18s)- 1.73 ms ± 38 μs- IORef inconsistent: OK (0.04s)- 3.06 ms ± 305 μs- IORef atomic: OK (0.44s)- 17.4 ms ± 797 μs- MVar: OK (1.80s)- 56.3 ms ± 1.3 ms- TMVar: OK (0.44s)- 18.7 ms ± 761 μs- TVar: OK (0.05s)- 18.3 ms ± 1.8 ms- Addr: OK (0.05s)- 1.72 ms ± 103 μs- Read/write contention with 10000 iterations and 20 threads- Counter: OK (0.05s)- 17.6 ms ± 1.2 ms- IORef inconsistent: OK (0.04s)- 31.2 ms ± 1.8 ms- IORef atomic: OK (10.54s)- 659 ms ± 346 ms- MVar: OK (1.96s)- 627 ms ± 46 ms- TMVar: OK (1.10s)- 192 ms ± 9.0 ms- TVar: OK (1.09s)- 183 ms ± 16 ms- Addr: OK (0.21s)- 17.0 ms ± 260 μs- Read/write contention with 10 iterations and 32 threads- Counter: OK (0.12s)- 70.1 μs ± 2.7 μs- IORef inconsistent: OK (0.08s)- 86.8 μs ± 7.5 μs- IORef atomic: OK (0.95s)- 183 μs ± 12 μs- MVar: OK (0.23s)- 913 μs ± 46 μs- TMVar: OK (0.07s)- 281 μs ± 15 μs- TVar: OK (0.07s)- 283 μs ± 20 μs- Addr: OK (0.12s)- 69.4 μs ± 3.2 μs- Read/write contention with 100 iterations and 32 threads- Counter: OK (0.05s)- 280 μs ± 27 μs- IORef inconsistent: OK (0.06s)- 487 μs ± 37 μs- IORef atomic: OK (0.07s)- 1.89 ms ± 121 μs- MVar: OK (0.57s)- 8.50 ms ± 703 μs- TMVar: OK (0.04s)- 3.04 ms ± 280 μs- TVar: OK (0.07s)- 2.95 ms ± 175 μs- Addr: OK (0.04s)- 277 μs ± 26 μs- Read/write contention with 1000 iterations and 32 threads- Counter: OK (0.08s)- 2.75 ms ± 198 μs- IORef inconsistent: OK (0.03s)- 5.20 ms ± 388 μs- IORef atomic: OK (0.39s)- 22.5 ms ± 809 μs- MVar: OK (0.68s)- 92.3 ms ± 4.3 ms- TMVar: OK (0.18s)- 31.4 ms ± 2.2 ms- TVar: OK (0.09s)- 30.0 ms ± 2.5 ms- Addr: OK (0.04s)- 2.85 ms ± 243 μs- Read/write contention with 10000 iterations and 32 threads- Counter: OK (0.08s)- 28.1 ms ± 708 μs- IORef inconsistent: OK (0.07s)- 53.6 ms ± 1.4 ms- IORef atomic: OK (9.80s)- 1.223 s ± 410 ms- MVar: OK (7.32s)- 1.009 s ± 37 ms- TMVar: OK (0.17s)- 296 ms ± 23 ms- TVar: OK (1.76s)- 302 ms ± 5.6 ms- Addr: OK (0.08s)- 29.1 ms ± 2.6 ms- Read/write contention with 10 iterations and 64 threads- Counter: OK (0.05s)- 127 μs ± 12 μs- IORef inconsistent: OK (0.12s)- 158 μs ± 5.8 μs- IORef atomic: OK (0.05s)- 320 μs ± 29 μs- MVar: OK (0.23s)- 1.86 ms ± 145 μs- TMVar: OK (1.06s)- 529 μs ± 41 μs- TVar: OK (0.26s)- 569 μs ± 24 μs- Addr: OK (0.06s)- 125 μs ± 11 μs- Read/write contention with 100 iterations and 64 threads- Counter: OK (0.04s)- 564 μs ± 47 μs- IORef inconsistent: OK (0.04s)- 1.03 ms ± 98 μs- IORef atomic: OK (0.31s)- 3.96 ms ± 240 μs- MVar: OK (1.15s)- 17.6 ms ± 1.3 ms- TMVar: OK (0.30s)- 6.30 ms ± 476 μs- TVar: OK (0.30s)- 6.28 ms ± 66 μs- Addr: OK (0.06s)- 560 μs ± 24 μs- Read/write contention with 1000 iterations and 64 threads- Counter: OK (0.14s)- 4.93 ms ± 91 μs- IORef inconsistent: OK (0.03s)- 8.83 ms ± 711 μs- IORef atomic: OK (6.68s)- 110 ms ± 21 ms- MVar: OK (1.34s)- 188 ms ± 7.7 ms- TMVar: OK (0.36s)- 64.3 ms ± 4.0 ms- TVar: OK (0.08s)- 64.3 ms ± 2.3 ms- Addr: OK (0.04s)- 5.85 ms ± 457 μs- Read/write contention with 10000 iterations and 64 threads- Counter: OK (0.08s)- 59.1 ms ± 2.2 ms- IORef inconsistent: OK (0.14s)- 102 ms ± 5.3 ms- IORef atomic: OK (11.44s)- 2.924 s ± 306 ms- MVar: OK (6.17s)- 1.982 s ± 34 ms- TMVar: OK (0.35s)- 657 ms ± 5.3 ms- TVar: OK (3.62s)- 631 ms ± 83 ms- Addr: OK (0.03s)- 47.7 ms ± 3.7 ms- Read/write contention with 10 iterations and 128 threads- Counter: OK (0.08s)- 232 μs ± 11 μs- IORef inconsistent: OK (0.12s)- 322 μs ± 11 μs- IORef atomic: OK (0.36s)- 636 μs ± 14 μs- MVar: OK (0.23s)- 3.65 ms ± 264 μs- TMVar: OK (1.04s)- 1.07 ms ± 59 μs- TVar: OK (0.07s)- 1.09 ms ± 97 μs- Addr: OK (0.05s)- 227 μs ± 22 μs- Read/write contention with 100 iterations and 128 threads- Counter: OK (0.04s)- 1.16 ms ± 85 μs- IORef inconsistent: OK (0.03s)- 2.08 ms ± 182 μs- IORef atomic: OK (0.07s)- 5.79 ms ± 393 μs- MVar: OK (1.14s)- 35.6 ms ± 1.1 ms- TMVar: OK (0.62s)- 13.1 ms ± 417 μs- TVar: OK (0.07s)- 10.4 ms ± 412 μs- Addr: OK (0.13s)- 1.10 ms ± 95 μs- Read/write contention with 1000 iterations and 128 threads- Counter: OK (0.04s)- 11.7 ms ± 1.2 ms- IORef inconsistent: OK (0.06s)- 21.8 ms ± 2.0 ms- IORef atomic: OK (10.69s)- 318 ms ± 82 ms- MVar: OK (5.87s)- 373 ms ± 8.3 ms- TMVar: OK (0.36s)- 133 ms ± 4.1 ms- TVar: OK (0.17s)- 128 ms ± 8.6 ms- Addr: OK (0.07s)- 10.0 ms ± 504 μs- Read/write contention with 10000 iterations and 128 threads- Counter: OK (0.33s)- 119 ms ± 8.4 ms- IORef inconsistent: OK (0.28s)- 210 ms ± 11 ms- IORef atomic: OK (11.27s)- 6.930 s ± 818 ms- MVar: OK (12.48s)- 3.977 s ± 43 ms- TMVar: OK (3.52s)- 1.316 s ± 11 ms- TVar: OK (1.60s)- 1.291 s ± 126 ms- Addr: OK (0.67s)- 118 ms ± 3.0 ms--All 309 tests passed (264.24s)+Operations translate to atomic CPU instructions which involve memory+barrier. For limited set of operation this package provides in+concurrent setting they tend to be faster than atomic modify operation+on `IORef`s and other shared var types. Value inside the `Counter`+type is unboxed which contributes to outperforming all other shared+vars because they box the value they store. For integers boxing is+especially bad.++For single-threaded use case this package will likely not outperform+vanilla `IORef`s or `STRef`s (it depends on whether GHC adds memory+barrier for them or not, cf+https://gitlab.haskell.org/ghc/ghc/-/issues/22764, if it does end up+adding the same memory barrier then both this package and `IORef`+should be equal). All in all, operations this package provides will+incur guaranteed memory barrier which will serve no purpose in+single-threaded settings so for that use case this package is probably+not the best. The intended use of this package is in the concurrent+setting where it does seem like a clear winner (please see benchmarks+below).++# Benchmark++### Summary++Depending on number of threads `Counter` from this package can be+up to 10 times faster than next best `TVar` from the `stm` package.++### Details++The benchmark is to spawn N threads each of which will increment the+same counter by 1 for a number of iterations.++Test setup: Intel i5-9600K CPU @ 3.70GHz, 6 cores, no hyperthreading, GHC 9.6.1.++NB `IORef inconsistent` is the benchmark that just reads and writes+`IORef` with the fastest functions the `IORef` supports. It gives+wrong results when multiple threads access `IORef` this way since+these reads and writes are not synchronized. It’s included only for+speed comparison purposes. All others do proper synchronization of+increments between the threads. Current package is shown as `Counter`.++```+$ cabal run bench -- -j1 --timeout 300 --stdev 2+All+ Correctness: OK (13.97s)+ +++ OK, passed 10000 tests.+ Read/write contention with 10 iterations and 1 threads+ Counter: OK (14.23s)+ 839 ns ± 33 ns+ IORef inconsistent: OK (1.08s)+ 1.03 μs ± 36 ns, 1.23x+ IORef atomic: OK (1.30s)+ 1.25 μs ± 40 ns, 1.48x+ MVar: OK (10.53s)+ 1.24 μs ± 13 ns, 1.48x+ TMVar: OK (0.65s)+ 1.18 μs ± 30 ns, 1.41x+ TVar: OK (0.33s)+ 1.13 μs ± 45 ns, 1.35x+ Addr: OK (3.37s)+ 818 ns ± 28 ns, 0.97x+ Read/write contention with 100 iterations and 1 threads+ Counter: OK (1.47s)+ 1.35 μs ± 24 ns+ IORef inconsistent: OK (17.27s)+ 2.09 μs ± 47 ns, 1.55x+ IORef atomic: OK (0.45s)+ 3.22 μs ± 122 ns, 2.38x+ MVar: OK (3.15s)+ 2.97 μs ± 30 ns, 2.20x+ TMVar: OK (1.24s)+ 4.65 μs ± 64 ns, 3.44x+ TVar: OK (2.43s)+ 4.61 μs ± 158 ns, 3.41x+ Addr: OK (1.16s)+ 1.09 μs ± 22 ns, 0.80x+ Read/write contention with 1000 iterations and 1 threads+ Counter: OK (0.47s)+ 6.48 μs ± 235 ns+ IORef inconsistent: OK (0.78s)+ 2.76 μs ± 88 ns, 0.43x+ IORef atomic: OK (0.60s)+ 17.5 μs ± 399 ns, 2.69x+ MVar: OK (2.09s)+ 15.8 μs ± 208 ns, 2.43x+ TMVar: OK (0.40s)+ 48.3 μs ± 1.9 μs, 7.46x+ TVar: OK (0.55s)+ 32.4 μs ± 713 ns, 5.00x+ Addr: OK (0.34s)+ 4.73 μs ± 189 ns, 0.73x+ Read/write contention with 10000 iterations and 1 threads+ Counter: OK (0.50s)+ 58.4 μs ± 2.1 μs+ IORef inconsistent: OK (0.42s)+ 23.8 μs ± 829 ns, 0.41x+ IORef atomic: OK (0.73s)+ 168 μs ± 3.8 μs, 2.87x+ MVar: OK (0.66s)+ 157 μs ± 4.6 μs, 2.69x+ TMVar: OK (2.09s)+ 512 μs ± 7.2 μs, 8.78x+ TVar: OK (1.39s)+ 321 μs ± 6.7 μs, 5.50x+ Addr: OK (47.12s)+ 43.8 μs ± 562 ns, 0.75x+ Read/write contention with 10 iterations and 2 threads+ Counter: OK (1.10s)+ 7.85 μs ± 144 ns+ IORef inconsistent: OK (1.93s)+ 7.65 μs ± 201 ns, 0.97x+ IORef atomic: OK (0.54s)+ 8.16 μs ± 235 ns, 1.04x+ MVar: OK (7.81s)+ 7.81 μs ± 72 ns, 0.99x+ TMVar: OK (0.49s)+ 7.98 μs ± 292 ns, 1.02x+ TVar: OK (1.91s)+ 7.98 μs ± 181 ns, 1.02x+ Addr: OK (2.01s)+ 7.64 μs ± 213 ns, 0.97x+ Read/write contention with 100 iterations and 2 threads+ Counter: OK (1.00s)+ 8.50 μs ± 290 ns+ IORef inconsistent: OK (8.00s)+ 8.13 μs ± 133 ns, 0.96x+ IORef atomic: OK (2.80s)+ 13.3 μs ± 358 ns, 1.56x+ MVar: OK (4.64s)+ 19.2 μs ± 473 ns, 2.25x+ TMVar: OK (5.52s)+ 32.3 μs ± 1.1 μs, 3.80x+ TVar: OK (83.27s)+ 30.5 μs ± 279 ns, 3.59x+ Addr: OK (4.38s)+ 8.71 μs ± 333 ns, 1.03x+ Read/write contention with 1000 iterations and 2 threads+ Counter: OK (28.57s)+ 43.7 μs ± 106 ns+ IORef inconsistent: OK (0.91s)+ 17.2 μs ± 669 ns, 0.39x+ IORef atomic: OK (130.48s)+ 223 μs ± 10 μs, 5.09x+ MVar: OK (1.19s)+ 5.11 ms ± 173 μs, 116.88x+ TMVar: OK (0.33s)+ 295 μs ± 7.7 μs, 6.75x+ TVar: OK (1.44s)+ 337 μs ± 10 μs, 7.71x+ Addr: OK (0.47s)+ 49.3 μs ± 915 ns, 1.13x+ Read/write contention with 10000 iterations and 2 threads+ Counter: OK (0.51s)+ 466 μs ± 17 μs+ IORef inconsistent: OK (13.49s)+ 190 μs ± 1.2 μs, 0.41x+ IORef atomic: OK (71.77s)+ 1.80 ms ± 33 μs, 3.86x+ MVar: OK (7.23s)+ 60.1 ms ± 505 μs, 128.83x+ TMVar: OK (1.50s)+ 2.39 ms ± 39 μs, 5.13x+ TVar: OK (108.24s)+ 3.13 ms ± 405 μs, 6.70x+ Addr: OK (0.29s)+ 531 μs ± 20 μs, 1.14x+ Read/write contention with 10 iterations and 4 threads+ Counter: OK (2.33s)+ 15.5 μs ± 384 ns+ IORef inconsistent: OK (2.41s)+ 15.6 μs ± 52 ns, 1.01x+ IORef atomic: OK (1.56s)+ 19.5 μs ± 487 ns, 1.26x+ MVar: OK (1.51s)+ 28.9 μs ± 647 ns, 1.87x+ TMVar: OK (2.88s)+ 19.9 μs ± 439 ns, 1.29x+ TVar: OK (0.38s)+ 18.2 μs ± 332 ns, 1.18x+ Addr: OK (0.59s)+ 15.3 μs ± 477 ns, 0.99x+ Read/write contention with 100 iterations and 4 threads+ Counter: OK (1.63s)+ 25.3 μs ± 723 ns+ IORef inconsistent: OK (0.35s)+ 17.6 μs ± 451 ns, 0.69x+ IORef atomic: OK (0.46s)+ 154 μs ± 2.1 μs, 6.08x+ MVar: OK (0.62s)+ 1.20 ms ± 38 μs, 47.27x+ TMVar: OK (0.12s)+ 191 μs ± 7.4 μs, 7.54x+ TVar: OK (0.14s)+ 221 μs ± 6.6 μs, 8.71x+ Addr: OK (0.75s)+ 21.0 μs ± 228 ns, 0.83x+ Read/write contention with 1000 iterations and 4 threads+ Counter: OK (0.17s)+ 242 μs ± 6.7 μs+ IORef inconsistent: OK (0.15s)+ 103 μs ± 2.7 μs, 0.43x+ IORef atomic: OK (2.28s)+ 1.71 ms ± 36 μs, 7.07x+ MVar: OK (0.41s)+ 12.9 ms ± 338 μs, 53.16x+ TMVar: OK (0.29s)+ 2.08 ms ± 65 μs, 8.61x+ TVar: OK (0.17s)+ 2.23 ms ± 73 μs, 9.21x+ Addr: OK (0.58s)+ 234 μs ± 1.9 μs, 0.97x+ Read/write contention with 10000 iterations and 4 threads+ Counter: OK (0.17s)+ 2.24 ms ± 60 μs+ IORef inconsistent: OK (9.34s)+ 1.00 ms ± 31 μs, 0.45x+ IORef atomic: OK (70.87s)+ 22.0 ms ± 820 μs, 9.80x+ MVar: OK (0.74s)+ 126 ms ± 1.9 ms, 56.07x+ TMVar: OK (0.33s)+ 19.0 ms ± 670 μs, 8.48x+ TVar: OK (0.09s)+ 20.8 ms ± 732 μs, 9.27x+ Addr: OK (0.18s)+ 2.45 ms ± 97 μs, 1.09x+ Read/write contention with 10 iterations and 6 threads+ Counter: OK (6.77s)+ 23.9 μs ± 813 ns+ IORef inconsistent: OK (0.42s)+ 23.4 μs ± 712 ns, 0.98x+ IORef atomic: OK (0.70s)+ 39.4 μs ± 747 ns, 1.65x+ MVar: OK (1.32s)+ 50.0 μs ± 1.0 μs, 2.09x+ TMVar: OK (0.52s)+ 35.3 μs ± 449 ns, 1.47x+ TVar: OK (3.93s)+ 32.6 μs ± 93 ns, 1.37x+ Addr: OK (0.42s)+ 23.4 μs ± 381 ns, 0.98x+ Read/write contention with 100 iterations and 6 threads+ Counter: OK (1.20s)+ 47.8 μs ± 866 ns+ IORef inconsistent: OK (0.24s)+ 26.9 μs ± 983 ns, 0.56x+ IORef atomic: OK (1.46s)+ 483 μs ± 17 μs, 10.10x+ MVar: OK (2.01s)+ 1.86 ms ± 13 μs, 38.97x+ TMVar: OK (0.49s)+ 519 μs ± 4.5 μs, 10.86x+ TVar: OK (14.78s)+ 521 μs ± 14 μs, 10.91x+ Addr: OK (0.56s)+ 43.1 μs ± 1.6 μs, 0.90x+ Read/write contention with 1000 iterations and 6 threads+ Counter: OK (13.77s)+ 459 μs ± 8.8 μs+ IORef inconsistent: OK (0.23s)+ 207 μs ± 4.1 μs, 0.45x+ IORef atomic: OK (2.10s)+ 3.48 ms ± 70 μs, 7.58x+ MVar: OK (0.64s)+ 19.3 ms ± 555 μs, 42.03x+ TMVar: OK (1.07s)+ 5.55 ms ± 161 μs, 12.08x+ TVar: OK (0.27s)+ 4.85 ms ± 189 μs, 10.56x+ Addr: OK (27.53s)+ 461 μs ± 4.8 μs, 1.00x+ Read/write contention with 10000 iterations and 6 threads+ Counter: OK (0.52s)+ 4.88 ms ± 142 μs+ IORef inconsistent: OK (0.25s)+ 2.25 ms ± 24 μs, 0.46x+ IORef atomic: OK (66.59s)+ 75.5 ms ± 11 ms, 15.46x+ MVar: OK (2.85s)+ 193 ms ± 4.2 ms, 39.54x+ TMVar: OK (0.16s)+ 54.9 ms ± 1.6 ms, 11.25x+ TVar: OK (10.04s)+ 47.1 ms ± 339 μs, 9.64x+ Addr: OK (0.52s)+ 4.94 ms ± 89 μs, 1.01x+ Read/write contention with 10 iterations and 8 threads+ Counter: OK (1.84s)+ 27.3 μs ± 1.0 μs+ IORef inconsistent: OK (1.98s)+ 27.5 μs ± 1.0 μs, 1.01x+ IORef atomic: OK (0.91s)+ 56.1 μs ± 787 ns, 2.06x+ MVar: OK (0.69s)+ 94.9 μs ± 3.2 μs, 3.48x+ TMVar: OK (1.35s)+ 56.1 μs ± 1.5 μs, 2.06x+ TVar: OK (0.36s)+ 51.7 μs ± 1.5 μs, 1.89x+ Addr: OK (0.24s)+ 26.3 μs ± 672 ns, 0.96x+ Read/write contention with 100 iterations and 8 threads+ Counter: OK (1.41s)+ 63.1 μs ± 1.2 μs+ IORef inconsistent: OK (8.38s)+ 33.3 μs ± 503 ns, 0.53x+ IORef atomic: OK (0.59s)+ 709 μs ± 21 μs, 11.25x+ MVar: OK (1.21s)+ 2.31 ms ± 49 μs, 36.65x+ TMVar: OK (4.67s)+ 667 μs ± 18 μs, 10.58x+ TVar: OK (4.81s)+ 690 μs ± 6.5 μs, 10.94x+ Addr: OK (0.69s)+ 59.6 μs ± 1.7 μs, 0.94x+ Read/write contention with 1000 iterations and 8 threads+ Counter: OK (0.16s)+ 583 μs ± 13 μs+ IORef inconsistent: OK (1.13s)+ 304 μs ± 3.5 μs, 0.52x+ IORef atomic: OK (93.87s)+ 7.12 ms ± 158 μs, 12.22x+ MVar: OK (1.54s)+ 24.4 ms ± 541 μs, 41.83x+ TMVar: OK (0.09s)+ 6.32 ms ± 241 μs, 10.84x+ TVar: OK (0.67s)+ 6.36 ms ± 221 μs, 10.92x+ Addr: OK (0.56s)+ 540 μs ± 20 μs, 0.93x+ Read/write contention with 10000 iterations and 8 threads+ Counter: OK (5.13s)+ 5.67 ms ± 68 μs+ IORef inconsistent: OK (0.16s)+ 3.14 ms ± 69 μs, 0.55x+ IORef atomic: OK (35.04s)+ 148 ms ± 319 μs, 26.14x+ MVar: OK (1.58s)+ 230 ms ± 8.7 ms, 40.61x+ TMVar: OK (26.99s)+ 65.9 ms ± 4.1 ms, 11.62x+ TVar: OK (3.41s)+ 66.6 ms ± 779 μs, 11.75x+ Addr: OK (0.16s)+ 5.84 ms ± 164 μs, 1.03x+ Read/write contention with 10 iterations and 12 threads+ Counter: OK (0.54s)+ 30.5 μs ± 452 ns+ IORef inconsistent: OK (0.29s)+ 32.4 μs ± 820 ns, 1.06x+ IORef atomic: OK (7.93s)+ 70.3 μs ± 436 ns, 2.30x+ MVar: OK (0.44s)+ 232 μs ± 8.7 μs, 7.59x+ TMVar: OK (13.74s)+ 80.4 μs ± 2.3 μs, 2.64x+ TVar: OK (1.73s)+ 79.2 μs ± 2.7 μs, 2.60x+ Addr: OK (0.26s)+ 29.6 μs ± 1.0 μs, 0.97x+ Read/write contention with 100 iterations and 12 threads+ Counter: OK (0.49s)+ 98.0 μs ± 1.1 μs+ IORef inconsistent: OK (0.66s)+ 48.2 μs ± 561 ns, 0.49x+ IORef atomic: OK (0.88s)+ 1.07 ms ± 39 μs, 10.89x+ MVar: OK (0.86s)+ 3.18 ms ± 61 μs, 32.48x+ TMVar: OK (0.21s)+ 1.00 ms ± 21 μs, 10.19x+ TVar: OK (0.12s)+ 1.06 ms ± 29 μs, 10.77x+ Addr: OK (0.13s)+ 94.0 μs ± 2.7 μs, 0.96x+ Read/write contention with 1000 iterations and 12 threads+ Counter: OK (6.78s)+ 955 μs ± 5.9 μs+ IORef inconsistent: OK (0.22s)+ 466 μs ± 16 μs, 0.49x+ IORef atomic: OK (2.46s)+ 11.1 ms ± 109 μs, 11.62x+ MVar: OK (1.14s)+ 34.3 ms ± 923 μs, 35.98x+ TMVar: OK (8.60s)+ 10.7 ms ± 226 μs, 11.22x+ TVar: OK (4.18s)+ 9.82 ms ± 42 μs, 10.29x+ Addr: OK (0.42s)+ 1.00 ms ± 40 μs, 1.05x+ Read/write contention with 10000 iterations and 12 threads+ Counter: OK (1.99s)+ 9.45 ms ± 116 μs+ IORef inconsistent: OK (7.78s)+ 4.74 ms ± 7.4 μs, 0.50x+ IORef atomic: OK (82.99s)+ 304 ms ± 66 ms, 32.15x+ MVar: OK (11.29s)+ 343 ms ± 8.3 ms, 36.34x+ TMVar: OK (0.33s)+ 116 ms ± 1.9 ms, 12.23x+ TVar: OK (10.63s)+ 105 ms ± 1.5 ms, 11.15x+ Addr: OK (3.97s)+ 9.47 ms ± 361 μs, 1.00x+ Read/write contention with 10 iterations and 16 threads+ Counter: OK (0.20s)+ 42.1 μs ± 1.3 μs+ IORef inconsistent: OK (0.18s)+ 40.5 μs ± 1.4 μs, 0.96x+ IORef atomic: OK (1.08s)+ 153 μs ± 662 ns, 3.63x+ MVar: OK (1.56s)+ 408 μs ± 9.1 μs, 9.69x+ TMVar: OK (2.43s)+ 128 μs ± 1.9 μs, 3.04x+ TVar: OK (0.16s)+ 135 μs ± 5.0 μs, 3.19x+ Addr: OK (0.32s)+ 40.6 μs ± 940 ns, 0.96x+ Read/write contention with 100 iterations and 16 threads+ Counter: OK (0.32s)+ 131 μs ± 2.1 μs+ IORef inconsistent: OK (1.68s)+ 75.4 μs ± 1.6 μs, 0.58x+ IORef atomic: OK (4.05s)+ 1.27 ms ± 45 μs, 9.70x+ MVar: OK (4.53s)+ 4.39 ms ± 85 μs, 33.50x+ TMVar: OK (0.16s)+ 1.48 ms ± 40 μs, 11.30x+ TVar: OK (1.20s)+ 1.45 ms ± 5.5 μs, 11.06x+ Addr: OK (4.85s)+ 131 μs ± 1.3 μs, 1.00x+ Read/write contention with 1000 iterations and 16 threads+ Counter: OK (0.15s)+ 1.05 ms ± 32 μs+ IORef inconsistent: OK (0.28s)+ 632 μs ± 12 μs, 0.60x+ IORef atomic: OK (53.85s)+ 16.3 ms ± 1.5 ms, 15.58x+ MVar: OK (2.98s)+ 46.6 ms ± 672 μs, 44.57x+ TMVar: OK (0.09s)+ 15.2 ms ± 472 μs, 14.54x+ TVar: OK (0.36s)+ 14.6 ms ± 354 μs, 13.96x+ Addr: OK (0.15s)+ 1.38 ms ± 47 μs, 1.32x+ Read/write contention with 10000 iterations and 16 threads+ Counter: OK (0.17s)+ 13.3 ms ± 301 μs+ IORef inconsistent: OK (0.16s)+ 5.73 ms ± 119 μs, 0.43x+ IORef atomic: OK (117.97s)+ 464 ms ± 38 ms, 34.81x+ MVar: OK (14.95s)+ 481 ms ± 4.5 ms, 36.11x+ TMVar: OK (1.80s)+ 155 ms ± 2.7 ms, 11.65x+ TVar: OK (7.20s)+ 146 ms ± 663 μs, 10.93x+ Addr: OK (5.34s)+ 12.8 ms ± 187 μs, 0.96x+ Read/write contention with 10 iterations and 20 threads+ Counter: OK (0.19s)+ 49.3 μs ± 1.9 μs+ IORef inconsistent: OK (0.20s)+ 48.2 μs ± 1.7 μs, 0.98x+ IORef atomic: OK (5.11s)+ 188 μs ± 5.2 μs, 3.82x+ MVar: OK (4.08s)+ 536 μs ± 5.4 μs, 10.86x+ TMVar: OK (1.44s)+ 159 μs ± 531 ns, 3.23x+ TVar: OK (0.37s)+ 176 μs ± 3.0 μs, 3.57x+ Addr: OK (0.82s)+ 50.6 μs ± 1.0 μs, 1.03x+ Read/write contention with 100 iterations and 20 threads+ Counter: OK (0.10s)+ 171 μs ± 5.7 μs+ IORef inconsistent: OK (0.25s)+ 90.0 μs ± 2.1 μs, 0.52x+ IORef atomic: OK (9.07s)+ 1.52 ms ± 59 μs, 8.87x+ MVar: OK (2.90s)+ 5.61 ms ± 159 μs, 32.73x+ TMVar: OK (1.57s)+ 1.85 ms ± 54 μs, 10.77x+ TVar: OK (0.19s)+ 1.88 ms ± 28 μs, 10.98x+ Addr: OK (0.18s)+ 169 μs ± 3.8 μs, 0.98x+ Read/write contention with 1000 iterations and 20 threads+ Counter: OK (0.74s)+ 1.68 ms ± 65 μs+ IORef inconsistent: OK (0.18s)+ 881 μs ± 24 μs, 0.52x+ IORef atomic: OK (73.70s)+ 21.3 ms ± 600 μs, 12.66x+ MVar: OK (0.94s)+ 61.0 ms ± 1.4 ms, 36.31x+ TMVar: OK (7.19s)+ 18.1 ms ± 472 μs, 10.75x+ TVar: OK (7.26s)+ 18.1 ms ± 494 μs, 10.79x+ Addr: OK (0.09s)+ 1.74 ms ± 43 μs, 1.04x+ Read/write contention with 10000 iterations and 20 threads+ Counter: OK (6.72s)+ 16.2 ms ± 174 μs+ IORef inconsistent: OK (6.36s)+ 7.73 ms ± 247 μs, 0.48x+ IORef atomic: OK (21.41s)+ 641 ms ± 264 μs, 39.55x+ MVar: OK (38.82s)+ 609 ms ± 21 ms, 37.53x+ TMVar: OK (4.56s)+ 192 ms ± 648 μs, 11.82x+ TVar: OK (1.13s)+ 193 ms ± 7.5 ms, 11.91x+ Addr: OK (0.43s)+ 16.8 ms ± 581 μs, 1.03x+ Read/write contention with 10 iterations and 32 threads+ Counter: OK (1.01s)+ 73.7 μs ± 1.3 μs+ IORef inconsistent: OK (0.51s)+ 72.0 μs ± 777 ns, 0.98x+ IORef atomic: OK (0.24s)+ 178 μs ± 3.8 μs, 2.41x+ MVar: OK (0.91s)+ 919 μs ± 17 μs, 12.47x+ TMVar: OK (2.27s)+ 277 μs ± 4.4 μs, 3.76x+ TVar: OK (0.15s)+ 299 μs ± 10 μs, 4.06x+ Addr: OK (0.50s)+ 71.8 μs ± 2.6 μs, 0.97x+ Read/write contention with 100 iterations and 32 threads+ Counter: OK (0.29s)+ 280 μs ± 6.4 μs+ IORef inconsistent: OK (1.44s)+ 150 μs ± 3.2 μs, 0.53x+ IORef atomic: OK (0.25s)+ 1.74 ms ± 56 μs, 6.22x+ MVar: OK (1.19s)+ 9.06 ms ± 269 μs, 32.31x+ TMVar: OK (4.80s)+ 2.98 ms ± 36 μs, 10.64x+ TVar: OK (19.94s)+ 3.10 ms ± 27 μs, 11.04x+ Addr: OK (0.55s)+ 272 μs ± 9.4 μs, 0.97x+ Read/write contention with 1000 iterations and 32 threads+ Counter: OK (2.27s)+ 2.69 ms ± 48 μs+ IORef inconsistent: OK (2.18s)+ 1.27 ms ± 23 μs, 0.47x+ IORef atomic: OK (62.21s)+ 35.2 ms ± 4.3 ms, 13.08x+ MVar: OK (5.90s)+ 89.9 ms ± 233 μs, 33.44x+ TMVar: OK (2.96s)+ 30.7 ms ± 470 μs, 11.40x+ TVar: OK (5.98s)+ 30.4 ms ± 124 μs, 11.30x+ Addr: OK (0.29s)+ 2.75 ms ± 93 μs, 1.02x+ Read/write contention with 10000 iterations and 32 threads+ Counter: OK (0.08s)+ 28.8 ms ± 1.0 ms+ IORef inconsistent: OK (0.32s)+ 12.9 ms ± 351 μs, 0.45x+ IORef atomic: OK (82.13s)+ 1.180 s ± 65 ms, 41.02x+ MVar: OK (128.00s)+ 982 ms ± 14 ms, 34.15x+ TMVar: OK (0.17s)+ 299 ms ± 7.6 ms, 10.41x+ TVar: OK (7.51s)+ 316 ms ± 12 ms, 11.00x+ Addr: OK (1.36s)+ 27.0 ms ± 871 μs, 0.94x+ Read/write contention with 10 iterations and 64 threads+ Counter: OK (0.84s)+ 132 μs ± 3.3 μs+ IORef inconsistent: OK (0.44s)+ 132 μs ± 2.4 μs, 1.00x+ IORef atomic: OK (0.18s)+ 323 μs ± 6.5 μs, 2.45x+ MVar: OK (0.47s)+ 1.89 ms ± 74 μs, 14.33x+ TMVar: OK (4.18s)+ 557 μs ± 1.7 μs, 4.22x+ TVar: OK (2.19s)+ 575 μs ± 1.6 μs, 4.36x+ Addr: OK (0.19s)+ 125 μs ± 2.8 μs, 0.95x+ Read/write contention with 100 iterations and 64 threads+ Counter: OK (0.28s)+ 568 μs ± 13 μs+ IORef inconsistent: OK (0.32s)+ 284 μs ± 2.9 μs, 0.50x+ IORef atomic: OK (0.21s)+ 2.62 ms ± 95 μs, 4.62x+ MVar: OK (2.37s)+ 18.1 ms ± 688 μs, 31.81x+ TMVar: OK (0.15s)+ 6.24 ms ± 173 μs, 10.98x+ TVar: OK (5.00s)+ 6.30 ms ± 162 μs, 11.08x+ Addr: OK (2.06s)+ 555 μs ± 7.4 μs, 0.98x+ Read/write contention with 1000 iterations and 64 threads+ Counter: OK (0.59s)+ 5.80 ms ± 111 μs+ IORef inconsistent: OK (2.10s)+ 2.49 ms ± 96 μs, 0.43x+ IORef atomic: OK (2.36s)+ 75.8 ms ± 396 μs, 13.06x+ MVar: OK (2.88s)+ 188 ms ± 4.8 ms, 32.41x+ TMVar: OK (5.95s)+ 61.7 ms ± 176 μs, 10.63x+ TVar: OK (1.52s)+ 63.8 ms ± 2.4 ms, 11.00x+ Addr: OK (0.28s)+ 5.52 ms ± 167 μs, 0.95x+ Read/write contention with 10000 iterations and 64 threads+ Counter: OK (2.74s)+ 56.6 ms ± 2.2 ms+ IORef inconsistent: OK (10.26s)+ 26.0 ms ± 746 μs, 0.46x+ IORef atomic: OK (100.70s)+ 3.018 s ± 328 ms, 53.35x+ MVar: OK (30.56s)+ 1.994 s ± 16 ms, 35.25x+ TMVar: OK (0.36s)+ 665 ms ± 3.4 ms, 11.76x+ TVar: OK (1.79s)+ 653 ms ± 2.4 ms, 11.55x+ Addr: OK (11.06s)+ 57.0 ms ± 546 μs, 1.01x+ Read/write contention with 10 iterations and 128 threads+ Counter: OK (0.67s)+ 240 μs ± 5.9 μs+ IORef inconsistent: OK (1.43s)+ 247 μs ± 8.5 μs, 1.03x+ IORef atomic: OK (1.38s)+ 642 μs ± 6.2 μs, 2.68x+ MVar: OK (1.92s)+ 3.83 ms ± 34 μs, 15.94x+ TMVar: OK (16.12s)+ 1.11 ms ± 23 μs, 4.63x+ TVar: OK (4.27s)+ 1.15 ms ± 36 μs, 4.78x+ Addr: OK (0.16s)+ 230 μs ± 6.9 μs, 0.96x+ Read/write contention with 100 iterations and 128 threads+ Counter: OK (0.26s)+ 1.15 ms ± 42 μs+ IORef inconsistent: OK (0.31s)+ 560 μs ± 18 μs, 0.49x+ IORef atomic: OK (0.31s)+ 7.80 ms ± 274 μs, 6.81x+ MVar: OK (0.55s)+ 36.2 ms ± 1.2 ms, 31.54x+ TMVar: OK (19.40s)+ 12.4 ms ± 20 μs, 10.78x+ TVar: OK (0.62s)+ 13.1 ms ± 411 μs, 11.39x+ Addr: OK (0.13s)+ 1.12 ms ± 24 μs, 0.98x+ Read/write contention with 1000 iterations and 128 threads+ Counter: OK (0.15s)+ 8.93 ms ± 264 μs+ IORef inconsistent: OK (0.52s)+ 5.28 ms ± 104 μs, 0.59x+ IORef atomic: OK (80.71s)+ 310 ms ± 57 ms, 34.68x+ MVar: OK (5.86s)+ 378 ms ± 3.1 ms, 42.38x+ TMVar: OK (1.51s)+ 131 ms ± 3.5 ms, 14.62x+ TVar: OK (3.07s)+ 129 ms ± 981 μs, 14.44x+ Addr: OK (4.47s)+ 11.3 ms ± 139 μs, 1.27x+ Read/write contention with 10000 iterations and 128 threads+ Counter: OK (1.37s)+ 112 ms ± 3.3 ms+ IORef inconsistent: OK (5.13s)+ 53.3 ms ± 571 μs, 0.47x+ IORef atomic: OK (11.14s)+ 6.952 s ± 54 ms, 61.84x+ MVar: OK (12.70s)+ 4.075 s ± 154 ms, 36.25x+ TMVar: OK (3.51s)+ 1.268 s ± 5.7 ms, 11.27x+ TVar: OK (0.71s)+ 1.306 s ± 50 ms, 11.62x+ Addr: OK (11.18s)+ 117 ms ± 1.7 ms, 1.04x++All 309 tests passed (2397.33s) ``` # Memory overhead
atomic-counter.cabal view
@@ -5,7 +5,7 @@ name: atomic-counter version:- 0.1+ 0.1.1 synopsis: Mutable counters that can be modified with atomic operatinos @@ -38,11 +38,21 @@ homepage: https://github.com/sergv/atomic-counter+bug-reports:+ https://github.com/sergv/atomic-counter/issues source-repository head type: git location: https://github.com/sergv/atomic-counter.git +flag dev+ description:+ Enable development flags+ default:+ False+ manual:+ True+ common ghc-options default-language: Haskell2010@@ -78,6 +88,14 @@ src build-depends: , base >= 4.14 && < 5+ if impl(ghc >= 9.4) && !arch(javascript)+ cmm-sources:+ Counter.cmm+ cpp-options:+ -DUSE_CMM+ if flag(dev)+ ghc-options:+ -dcmm-lint library test-utils import: ghc-options@@ -125,8 +143,8 @@ , base >= 4.14 , primitive , stm- , tasty- , tasty-bench+ , tasty >= 1.4.2+ , tasty-bench >= 0.3.2 , tasty-quickcheck , test-utils ghc-options:@@ -134,4 +152,3 @@ -threaded "-with-rtsopts=-N -A32M" -main-is BenchMain-
bench/BenchMain.hs view
@@ -26,6 +26,7 @@ import Test.QuickCheck import Test.Tasty import Test.Tasty.Bench+import Test.Tasty.Patterns.Printer import Test.Tasty.QuickCheck qualified as QC import Test.Tasty.Runners qualified as Tasty @@ -110,9 +111,9 @@ g === expected ] - let benchmarks =+ let benchmarks = map (mapLeafBenchmarks addCompare) [ bgroup ("Read/write contention with " ++ show (unIterations n) ++ " iterations and " ++ show (length threads) ++ " threads")- [ bench "Counter" $+ [ bench counterBenchName $ whnfIO (spawnAndCall threads (C.new 0) (\ref _ -> callN n (incrementCounter ref 1))) , bench "IORef inconsistent" $ whnfIO (spawnAndCall threads (newIORef 0) (\ref _ -> callN n (incrementIORefInconsistent ref 1)))@@ -143,3 +144,12 @@ localOption (Tasty.NumThreads 1) $ testGroup "All" $ tests ++ benchmarks++counterBenchName :: String+counterBenchName = "Counter"++addCompare :: [String] -> Benchmark -> Benchmark+addCompare (name : path)+ | name /= counterBenchName+ = bcompare (printAwkExpr (locateBenchmark (counterBenchName : path)))+addCompare _ = id
src/Control/Concurrent/Counter/Lifted/ST.hs view
@@ -75,7 +75,7 @@ -> Int -> ST s () set (Counter c) (I# x) = ST $ \s1 -> case Unlifted.set c x s1 of- s2 -> (# s2, () #)+ (# s2 #) -> (# s2, () #) {-# INLINE add #-}
src/Control/Concurrent/Counter/Unlifted.hs view
@@ -16,10 +16,13 @@ -- 'Control.Concurrent.Counter.Counter' module. ---------------------------------------------------------------------------- -{-# LANGUAGE CPP #-}-{-# LANGUAGE MagicHash #-}-{-# LANGUAGE UnboxedTuples #-}-{-# LANGUAGE UnliftedNewtypes #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE GHCForeignImportPrim #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE UnboxedTuples #-}+{-# LANGUAGE UnliftedFFITypes #-}+{-# LANGUAGE UnliftedNewtypes #-} module Control.Concurrent.Counter.Unlifted ( Counter@@ -56,7 +59,55 @@ #define ADD_HASH(x) x# +#if defined(USE_CMM) && SIZEOF_HSINT == 8+ -- | Memory location that supports select few atomic operations.+newtype Counter s = Counter (Any :: UnliftedType)++-- | Create new counter with initial value.+foreign import prim "stg_newCounterzh"+ new :: Int# -> State# s -> (# State# s, Counter s #)++-- | Atomically read the counter's value.+foreign import prim "stg_atomicGetCounterzh"+ get :: Counter s -> State# s -> (# State# s, Int# #)++-- | Atomically assign new value to the counter.+foreign import prim "stg_atomicSetCounterzh"+ set :: Counter s -> Int# -> State# s -> (# State# s #)++-- | Atomically add an amount to the counter and return its old value.+foreign import prim "stg_atomicAddCounterzh"+ add :: Counter s -> Int# -> State# s -> (# State# s, Int# #)++-- | Atomically subtract an amount from the counter and return its old value.+foreign import prim "stg_atomicSubCounterzh"+ sub :: Counter s -> Int# -> State# s -> (# State# s, Int# #)++-- | Atomically combine old value with a new one via bitwise and. Returns old counter value.+foreign import prim "stg_atomicAndCounterzh"+ and :: Counter s -> Int# -> State# s -> (# State# s, Int# #)++-- | Atomically combine old value with a new one via bitwise or. Returns old counter value.+foreign import prim "stg_atomicOrCounterzh"+ or :: Counter s -> Int# -> State# s -> (# State# s, Int# #)++-- | Atomically combine old value with a new one via bitwise xor. Returns old counter value.+foreign import prim "stg_atomicXorCounterzh"+ xor :: Counter s -> Int# -> State# s -> (# State# s, Int# #)++-- | Atomically combine old value with a new one via bitwise nand. Returns old counter value.+foreign import prim "stg_atomicNandCounterzh"+ nand :: Counter s -> Int# -> State# s -> (# State# s, Int# #)++-- | Compare the underlying pointers of two counters.+sameCounter :: Counter s -> Counter s -> Bool+sameCounter (Counter x) (Counter y) =+ isTrue# (reallyUnsafePtrEquality# x y)++#else++-- | Memory location that supports select few atomic operations. newtype Counter s = Counter (MutableByteArray# s) {-# INLINE new #-}@@ -75,8 +126,9 @@ {-# INLINE set #-} -- | Atomically assign new value to the counter.-set :: Counter s -> Int# -> State# s -> State# s-set (Counter arr) = atomicWriteIntArray# arr 0#+set :: Counter s -> Int# -> State# s -> (# State# s #)+set (Counter arr) n = \s1 -> case atomicWriteIntArray# arr 0# n s1 of+ s2 -> (# s2 #) {-# INLINE add #-}@@ -115,3 +167,5 @@ sameCounter :: Counter s -> Counter s -> Bool sameCounter (Counter x) (Counter y) = isTrue# (sameMutableByteArray# x y)++#endif