From c43de34d92924f2323e6d413eccb1f42c06822d2 Mon Sep 17 00:00:00 2001 From: August Vi Date: Sun, 18 Oct 2026 12:44:10 -0700 Subject: [PATCH 1/2] Add a benchmarks page measured on 2026-10-12 with the suite that produces it, and label the 2019 pages as the old benchmarks. --- CHANGELOG.md | 1 + README.md | 17 +- docs/_static/benchmarks/bench-classic.png | Bin 0 -> 47063 bytes docs/_static/benchmarks/bench-django.png | Bin 0 -> 41644 bytes docs/_static/benchmarks/bench-sizes.png | Bin 0 -> 134095 bytes docs/_static/benchmarks/bench-throughput.png | Bin 0 -> 48391 bytes docs/benchmarks.rst | 1116 ++++++++++++++++++ docs/cache-benchmarks.rst | 12 +- docs/comparisons.rst | 2 +- docs/development.rst | 13 +- docs/djangocache-benchmarks.rst | 12 +- docs/index.rst | 5 +- tests/benchmarks/django_bench.py | 173 +++ tests/benchmarks/driver.py | 282 +++++ tests/benchmarks/report.py | 914 ++++++++++++++ tests/benchmarks/run_overnight.sh | 22 + tests/benchmarks/runner.py | 373 ++++++ tests/benchmarks/stores.py | 440 +++++++ tox.ini | 1 + 19 files changed, 3369 insertions(+), 14 deletions(-) create mode 100644 docs/_static/benchmarks/bench-classic.png create mode 100644 docs/_static/benchmarks/bench-django.png create mode 100644 docs/_static/benchmarks/bench-sizes.png create mode 100644 docs/_static/benchmarks/bench-throughput.png create mode 100644 docs/benchmarks.rst create mode 100644 tests/benchmarks/django_bench.py create mode 100644 tests/benchmarks/driver.py create mode 100644 tests/benchmarks/report.py create mode 100755 tests/benchmarks/run_overnight.sh create mode 100644 tests/benchmarks/runner.py create mode 100644 tests/benchmarks/stores.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 27b2f92..06c1494 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ diskcache-community forked from DiskCache at upstream commit ebfa37c (3 March 20 ### Docs +- A new benchmarks page, measured on 2026-10-12, compares diskcache-community with diskcache 5.6.3, memcached, Redis, lmdb, sqlitedict, shelve and the Django cache backends, with four charts. The scripts that produce it are in `tests/benchmarks` (`driver.py` runs the stores, `report.py` writes the page and charts from the JSON results). The 2019 pages from upstream stay as the old benchmarks, labelled as such and linked from the new page, and the README header, features and documentation list link to the new page (PR #26). - Shared links show a project card: a social preview image in `.github/`, and Open Graph tags on the documentation through `sphinxext-opengraph` (PR #21). - A logo: the README opens with a centered header (logo, name, tagline, links, badges) in the style of the other community forks, and the documentation shows the logo and a favicon (PR #20). - The README is rewritten in Markdown around install, usage and the fork story; the comparison tables move to a new `comparisons` page in the documentation, which gets its own landing page, and the 2016 testimonials are dropped (PR #19). diff --git a/README.md b/README.md index 6e2ecbd..3f81161 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@

[Tutorial] - [API] +- [Benchmarks] - [Docs] - [Changelog]

@@ -108,9 +109,11 @@ cache = Cache('cache', disk=SafeDisk) - Read-only mode, automatic when the directory is not writable - Opt-in `SafeDisk` for caches whose directory other users can write to - Pure Python on top of the standard library `sqlite3`; no server, no compiler -- Tested on CPython 3.10 to 3.14 on Linux, macOS and Windows, with - [benchmarks](https://diskcache-community.readthedocs.io/en/latest/cache-benchmarks.html) - against the alternatives +- Tested on CPython 3.10 to 3.14 on Linux, macOS and Windows +- Gets in about 2.6 µs and sets in about 19 µs on an Apple M5 Pro in the + [benchmarks](https://diskcache-community.readthedocs.io/en/latest/benchmarks.html) + against memcached, Redis, lmdb, sqlitedict and shelve; sets are 9 to 14 % + faster than in diskcache 5.6.3, and twice as fast for 1 MB values ## Why this fork @@ -171,8 +174,12 @@ The documentation is at - [Tutorial](https://diskcache-community.readthedocs.io/en/latest/tutorial.html) - [API reference](https://diskcache-community.readthedocs.io/en/latest/api.html) -- [Cache benchmarks](https://diskcache-community.readthedocs.io/en/latest/cache-benchmarks.html) - and [DjangoCache benchmarks](https://diskcache-community.readthedocs.io/en/latest/djangocache-benchmarks.html) +- [Benchmarks](https://diskcache-community.readthedocs.io/en/latest/benchmarks.html) + against memcached, Redis, lmdb, sqlitedict and shelve, the Django cache + backends, and diskcache 5.6.3 (measured 2026-10-12) +- Old benchmarks from 2019, measured by upstream for version 4: + [Cache](https://diskcache-community.readthedocs.io/en/latest/cache-benchmarks.html) + and [DjangoCache](https://diskcache-community.readthedocs.io/en/latest/djangocache-benchmarks.html) - [Comparisons](https://diskcache-community.readthedocs.io/en/latest/comparisons.html) with other key-value stores and caching libraries - Case studies: diff --git a/docs/_static/benchmarks/bench-classic.png b/docs/_static/benchmarks/bench-classic.png new file mode 100644 index 0000000000000000000000000000000000000000..bd4e3a2be72c0a63eb06cb898c27ce7e2f23a9e7 GIT binary patch literal 47063 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

~u@Lcl8|K_6vM2FoAn@+49+g!_-s6 z9rL$D9R`?Y*%t0z=q$};(mt7M8O^ZBmRiLa6p^NeLhb_ox@gPzcL*bjGF@xwFFYxI zAh_3or4Hq?&z363T*zEohjPfqouAbi!9@BGU&&puUAhcz5v!Ico}Z2kPuiT?q$nMv zj}V=jEs0^ko1ocQtOD>D+DhOf@e}; z#*J6mi@}T(HP3?k(%8MTzaVZbv62cBMeQL9AUSLc)c&%GIduK~^pFAt&)q5ryNeyJ z0+5S7T+ZlY9jHM%T;N@)i8p$o)Yw7sLZ-_j=D(p03~vf$i7oH^9Le41vjraoZyz?a z^EaKyT#63GlC4eZXT#pjO2Ry*ZW8k zZc*5GeOZ04NWJhB5Jf?Euj*>_GJNI^-F{WHYPIoVSkoKRp+YVG9)xR>q~J>7aoyL$ z1e|jAVuEGAkfO)Q@fs;>>6?05X>Hoh$n9b(dC{Ryho4~!+8B8>X^oGY;ezg$`vt2~ z!+ub4O~{zkdV5T5q-9EDh`oxggPE42hPDdi`UlRQ%MiXZY?8o8R9G zzE*q89hF=?c&^|OkTqC=%ucZB=FE{t@^L2FHp}a{TXiPKQq_7D>f%OBXYn z9F##M>f6Yx0Z~G(P+BPg~{_0UQ1Oi-Mg%e`ZLGw*p>J+B~5-vZZd}uh`uOZNoLBGr%$@Q9X zC4A>w_=IHalr8xu1&fj-M~)5S2C^DwxK-lR7ua2oj7#ua@+^&g@rB0;>Qj7I4QM+; zqT-ju``X@!k61CxJZXZ3dX%fITqJK2rdu4`Z5@SyzQ`fZUG3Vy{R-u=t-g8RzZue2Sy3Fpsho4M=YerFx~rb}aY5%(p3})KI4)?h z3gS0eol|Acw)Das`poHt^RLEVXI;&yXXrCT8Ts^&anamhe6n+a*Hu=h;eh`w*5LV; z9be$bWp4S_@^_HbN}9&1+L~QYmiAXjy}Bkj3f@jD_m0rnJ4y3jY`hl?znfQm?%T~uiS8{Bn6-YnaYj)B(_%oN0B^!T;D_@UM7G_2 zb_O0~t*VZ2@{y^MZ_j)PmKoP;luulfDi}sHz31~IT9{g_W+gUWw8*&PfSeJI5|0|x zQ(w}|*OY&$kQvTXKW_51_lse89}LH{)8NT4fWxDp;Jl?gi0yz!X|L+5xLiC4=C@7+EUc z`7~%hSF`@1D)~Al4a#`?OmtN5*$podd*U%J8)oT^AHZbM(wC{wP+nOPS>vS0d!FRn zOtd<_dpt^Gy^?zoc3gnT6D=7FoYgnsRo8Hdn~q`mdVgEjh+;#z6qPOGkbF!CRq z{NSP>@#}k=)K&uJ_)^;Y$UCoiOP&euckh+5{rrd3y|+%Wp80u>AQpIMSM&EpQ|yyh z8-g9m4ovPwu8E%~R&p2{<2HCd*n*mq;~y-DvETrG}6RZJ!B;@fH} zoV}ksF^cx_YP6C(j{4K8s}Gj`zM;uYVoY{vao0BeD9f;xmYUv+l-37B9_{%BhVqC? zz5^l;E-`f2QA|5Lx_+XFOS5{l!nSDr`YXMK7OkI#;kw;)FxGy z)>wgjujIx|e8`Dgo0#Cnxrv~=+#`yU5kyB=1$_3zVxDzrinl9z1}!hst?J)aF7 zHi@awoE!m)c()Q6A1;tLWfGWH9(fo%-8T+1ph?KX%?E>Xek6DIrY}*BH!LCamjuZR^4EpPx85TQ5lk z`6s9Z96WK<_iS7KMHcD7H0_$Vb@N+kq78k29F!&pbLl!eC#ND`oKcL}{#UcJtH`nE z<$wIP44d+=ufW%zihcd}7b4QG!nOYI-)WwLE&1=?Wgg~^|MwRnv;yf>|NRBU^9?~< s|M9;aCtZ*HkN<7||Lbp~d1Py+PSl7}bKPH6$Vs3mr*=K>>Yc~`4b;iM?EnA( literal 0 HcmV?d00001 diff --git a/docs/_static/benchmarks/bench-sizes.png b/docs/_static/benchmarks/bench-sizes.png new file mode 100644 index 0000000000000000000000000000000000000000..13f354ae3e9faee252112e4d47bdc454e6990555 GIT binary patch literal 134095 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

s6Fa<@O}3tO9^o}S{{ikD(%;4KFO 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

LV9-Qn*InCT2m2yJVc{lo``2d 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 literal 0 HcmV?d00001 diff --git a/docs/_static/benchmarks/bench-throughput.png b/docs/_static/benchmarks/bench-throughput.png new file mode 100644 index 0000000000000000000000000000000000000000..d7c11de7e32a710853147c54e205c45ea4557f44 GIT binary patch literal 48391 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

2Z`?Sa6#1_DB>0w1q|9Dm1nF>S`lT0q> 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 literal 0 HcmV?d00001 diff --git a/docs/benchmarks.rst b/docs/benchmarks.rst new file mode 100644 index 0000000..3151629 --- /dev/null +++ b/docs/benchmarks.rst @@ -0,0 +1,1116 @@ +DiskCache Benchmarks +==================== + +Measured on 2026-10-12 with the published diskcache-community 5.7.0 wheel. +Every number below comes from the scripts in ``tests/benchmarks`` of the +repository; the last section says how to run them. Treat the results as what +they are: one machine, synthetic workloads, short keys. They show the shape of +the costs, not what your application will see. + +The previous pages, :doc:`cache-benchmarks` and :doc:`djangocache-benchmarks`, +are the old benchmarks: upstream DiskCache measured them for version 4 in +2019, on other hardware and against other versions of every store. They stay +for reference; the classic mix below repeats their workload. + +Each store runs the same seeded operation sequence. Latencies are measured per +operation with the nanosecond performance counter and reported at the 50th, +90th and 99th percentiles and the maximum. Every workload runs once to warm up +and then five times (three for the multi-process and threaded workloads); the +tables show the median run, and the "p50 across runs" column shows how much +the median moved between runs. memcached runs with its item size limit raised +to 4 MB for the 1 MB tier and Redis with persistence off; both are separate +processes on localhost, so their numbers include a round trip over the +loopback interface. Every other store uses its default settings, and the notes +under each section say where a default matters. + +Environment +----------- + +- CPU: Apple M5 Pro, 18 cores +- OS: macOS 27.0.1, arm64 +- Python: CPython 3.14.8 +- SQLite: 3.53.4 +- memcached: memcached 1.6.45 +- Redis: 8.10.2 +- diskcache-community Cache: diskcache-community 5.7.0 +- diskcache-community FanoutCache(8): diskcache-community 5.7.0 +- diskcache 5.6.3 Cache: diskcache 5.6.3 +- memcached, pymemcache: pymemcache 4.0.0 +- memcached, pylibmc: pylibmc 1.6.3, libmemcached 1.0.18 +- Redis, redis-py: redis-py 8.1.0 with hiredis +- lmdb: py-lmdb 3.0.0, liblmdb 0.9.36 +- sqlitedict: sqlitedict 2.1.0 +- shelve (dbm): shelve over dbm.sqlite3, Python 3.14.8 +- Django 6.0.9 + +diskcache-community and diskcache 5.6.3 +--------------------------------------- + +The same workloads, run against the fork and against the last upstream +release in separate environments on the same machine. For latencies a negative +change favours the fork; for operations per second a positive one does. + +.. list-table:: diskcache-community against diskcache 5.6.3 + :header-rows: 1 + :widths: 34 18 20 10 + + * - Measure + - diskcache 5.6.3 + - diskcache-community + - change + * - Classic mix, get p50 + - 2.6 µs + - 2.6 µs + - +0 % + * - Classic mix, set p50 + - 22.3 µs + - 19.2 µs + - -14 % + * - Classic mix, delete p50 + - 20.5 µs + - 18.4 µs + - -11 % + * - Classic mix, ops per second + - 185562 + - 197044 + - +6 % + * - 100-byte values, set p50 + - 24.0 µs + - 21.7 µs + - -10 % + * - 64 KB values, set p50 + - 250.9 µs + - 229.5 µs + - -9 % + * - 1 MB values, set p50 + - 676.4 µs + - 351.3 µs + - -48 % + * - 1 MB values, get p50 + - 55.0 µs + - 54.4 µs + - -1 % + * - 95 % hits, get p50 + - 2.8 µs + - 3.2 µs + - +12 % + * - 8 processes, set p50 + - 22.8 µs + - 20.3 µs + - -11 % + * - 8 threads, ops per second + - 174210 + - 188028 + - +8 % + * - Django, get p50 + - 3.6 µs + - 3.7 µs + - +2 % + * - Django, set p50 + - 24.1 µs + - 24.8 µs + - +3 % + + +The classic mix +--------------- + +The workload of the original 2016 page, kept so the two can be compared: keys +drawn from a space of 100, values of up to about 600 bytes, 90 % gets, 9 % +sets and 1 % deletes, 100,000 operations after a 1,000-operation warm-up, in +one process. The ~1 % miss rate comes from gets after deletes. + +.. image:: _static/benchmarks/bench-classic.png + +.. list-table:: Get latency, classic mix + :header-rows: 1 + :widths: 30 10 18 10 10 10 8 + + * - Store + - p50 + - p50 across runs + - p90 + - p99 + - max + - misses + * - diskcache-community Cache + - 2.6 µs + - 2.6 µs to 2.6 µs + - 3.4 µs + - 3.8 µs + - 36.6 µs + - 8714 + * - diskcache-community FanoutCache(8) + - 2.8 µs + - 2.8 µs to 2.8 µs + - 3.3 µs + - 4.0 µs + - 26.8 µs + - 8714 + * - diskcache 5.6.3 Cache + - 2.6 µs + - 2.5 µs to 2.6 µs + - 3.2 µs + - 3.8 µs + - 27.1 µs + - 8714 + * - memcached, pymemcache + - 17.6 µs + - 17.5 µs to 17.7 µs + - 20.8 µs + - 25.4 µs + - 130.4 µs + - 8714 + * - memcached, pylibmc + - 15.7 µs + - 15.6 µs to 15.7 µs + - 19.0 µs + - 24.0 µs + - 72.6 µs + - 8714 + * - Redis, redis-py + - 40.9 µs + - 40.8 µs to 41.3 µs + - 46.6 µs + - 52.8 µs + - 274.2 µs + - 8714 + * - lmdb + - 3.4 µs + - 3.4 µs to 3.5 µs + - 9.8 µs + - 23.6 µs + - 185.4 µs + - 8714 + * - sqlitedict + - 27.2 µs + - 27.1 µs to 27.3 µs + - 31.0 µs + - 38.7 µs + - 109.8 µs + - 8714 + * - shelve (dbm) + - 3.8 µs + - 3.8 µs to 3.8 µs + - 4.6 µs + - 5.9 µs + - 17.8 µs + - 8714 + +.. list-table:: Set latency, classic mix + :header-rows: 1 + :widths: 30 10 18 10 10 10 8 + + * - Store + - p50 + - p50 across runs + - p90 + - p99 + - max + - misses + * - diskcache-community Cache + - 19.2 µs + - 19.2 µs to 19.9 µs + - 25.4 µs + - 37.3 µs + - 623.2 µs + - 0 + * - diskcache-community FanoutCache(8) + - 20.5 µs + - 20.3 µs to 20.9 µs + - 26.5 µs + - 38.1 µs + - 1.02 ms + - 0 + * - diskcache 5.6.3 Cache + - 22.3 µs + - 22.1 µs to 22.4 µs + - 28.0 µs + - 39.4 µs + - 789.1 µs + - 0 + * - memcached, pymemcache + - 18.5 µs + - 18.3 µs to 18.6 µs + - 21.3 µs + - 25.9 µs + - 60.1 µs + - 0 + * - memcached, pylibmc + - 16.0 µs + - 16.0 µs to 16.1 µs + - 19.4 µs + - 24.3 µs + - 37.2 µs + - 0 + * - Redis, redis-py + - 43.6 µs + - 43.4 µs to 43.7 µs + - 48.5 µs + - 55.7 µs + - 133.5 µs + - 0 + * - lmdb + - 5.98 ms + - 5.97 ms to 5.99 ms + - 6.90 ms + - 8.02 ms + - 14.69 ms + - 0 + * - sqlitedict + - 147.0 µs + - 146.5 µs to 148.6 µs + - 170.5 µs + - 217.3 µs + - 1.22 ms + - 0 + * - shelve (dbm) + - 34.3 µs + - 33.8 µs to 35.0 µs + - 46.7 µs + - 62.5 µs + - 2.29 ms + - 0 + +.. list-table:: Delete latency, classic mix + :header-rows: 1 + :widths: 30 10 18 10 10 10 8 + + * - Store + - p50 + - p50 across runs + - p90 + - p99 + - max + - misses + * - diskcache-community Cache + - 18.4 µs + - 18.2 µs to 18.6 µs + - 21.4 µs + - 40.8 µs + - 569.9 µs + - 111 + * - diskcache-community FanoutCache(8) + - 19.3 µs + - 19.1 µs to 19.4 µs + - 27.2 µs + - 40.2 µs + - 897.4 µs + - 111 + * - diskcache 5.6.3 Cache + - 20.5 µs + - 20.5 µs to 20.6 µs + - 22.5 µs + - 37.1 µs + - 518.0 µs + - 111 + * - memcached, pymemcache + - 16.4 µs + - 16.4 µs to 16.7 µs + - 19.8 µs + - 24.5 µs + - 30.5 µs + - 111 + * - memcached, pylibmc + - 15.6 µs + - 15.5 µs to 15.8 µs + - 19.0 µs + - 24.2 µs + - 29.3 µs + - 111 + * - Redis, redis-py + - 41.4 µs + - 41.0 µs to 41.5 µs + - 46.6 µs + - 52.4 µs + - 74.3 µs + - 111 + * - lmdb + - 5.96 ms + - 5.95 ms to 5.97 ms + - 6.90 ms + - 7.96 ms + - 10.96 ms + - 111 + * - sqlitedict + - 164.3 µs + - 163.5 µs to 164.8 µs + - 188.2 µs + - 230.1 µs + - 466.0 µs + - 111 + * - shelve (dbm) + - 29.0 µs + - 28.8 µs to 29.5 µs + - 39.1 µs + - 54.4 µs + - 118.9 µs + - 111 + +.. list-table:: Throughput and footprint, classic mix + :header-rows: 1 + :widths: 30 14 12 20 + + * - Store + - ops per second + - wall time + - on disk after the run + * - diskcache-community Cache + - 197044 + - 0.50 s + - 4 MB + * - diskcache-community FanoutCache(8) + - 182771 + - 0.54 s + - 32 MB + * - diskcache 5.6.3 Cache + - 185562 + - 0.53 s + - 4 MB + * - memcached, pymemcache + - 53497 + - 1.85 s + - server + * - memcached, pylibmc + - 58268 + - 1.70 s + - server + * - Redis, redis-py + - 23519 + - 4.21 s + - server + * - lmdb + - 1592 + - 62.17 s + - 192 KB + * - sqlitedict + - 24246 + - 4.08 s + - 64 KB + * - shelve (dbm) + - 131608 + - 0.75 s + - 4 MB + + +lmdb's writes stand out because its defaults, ``sync`` and ``metasync`` on, +end every write transaction with two full disk flushes (``F_FULLFSYNC`` on +macOS), about 6 ms together on this machine; its reads are the fastest here. +It is the only store in the table that forces each write onto the disk before +returning. DiskCache runs SQLite in WAL mode with ``synchronous`` NORMAL, +which flushes at checkpoints, sqlitedict commits every operation through +SQLite's default rollback journal, and memcached and Redis keep data in +memory. + +Value sizes +----------- + +Sets followed by gets at four value sizes. The 64 KB and 1 MB tiers cross +DiskCache's default 32 KB threshold above which values live in files next to +the database; memcached was started with its item size limit raised to 4 MB +for the 1 MB tier. + +.. image:: _static/benchmarks/bench-sizes.png + +.. list-table:: 100-byte values, 20,000 sets then 20,000 gets over 1,000 keys + :header-rows: 1 + :widths: 30 12 12 12 12 + + * - Store + - set p50 + - set p99 + - get p50 + - get p99 + * - diskcache-community Cache + - 21.7 µs + - 37.0 µs + - 2.5 µs + - 3.3 µs + * - diskcache-community FanoutCache(8) + - 19.2 µs + - 36.4 µs + - 2.6 µs + - 3.4 µs + * - diskcache 5.6.3 Cache + - 24.0 µs + - 40.9 µs + - 2.4 µs + - 3.0 µs + * - memcached, pymemcache + - 16.7 µs + - 24.0 µs + - 13.8 µs + - 23.3 µs + * - memcached, pylibmc + - 15.7 µs + - 22.0 µs + - 15.5 µs + - 21.8 µs + * - Redis, redis-py + - 43.2 µs + - 52.7 µs + - 41.0 µs + - 50.5 µs + * - lmdb + - 6.01 ms + - 8.04 ms + - 1.5 µs + - 3.2 µs + * - sqlitedict + - 149.5 µs + - 232.5 µs + - 26.6 µs + - 36.6 µs + * - shelve (dbm) + - 33.5 µs + - 56.4 µs + - 3.7 µs + - 5.1 µs + +.. list-table:: 8 KB values, 20,000 sets then 20,000 gets over 1,000 keys + :header-rows: 1 + :widths: 30 12 12 12 12 + + * - Store + - set p50 + - set p99 + - get p50 + - get p99 + * - diskcache-community Cache + - 23.2 µs + - 44.0 µs + - 3.2 µs + - 4.5 µs + * - diskcache-community FanoutCache(8) + - 20.1 µs + - 41.2 µs + - 3.2 µs + - 4.7 µs + * - diskcache 5.6.3 Cache + - 25.8 µs + - 48.0 µs + - 2.9 µs + - 4.2 µs + * - memcached, pymemcache + - 18.0 µs + - 25.7 µs + - 19.2 µs + - 25.7 µs + * - memcached, pylibmc + - 16.8 µs + - 23.4 µs + - 17.2 µs + - 23.5 µs + * - Redis, redis-py + - 42.4 µs + - 54.0 µs + - 40.3 µs + - 51.7 µs + * - lmdb + - 6.02 ms + - 7.99 ms + - 1.8 µs + - 6.7 µs + * - sqlitedict + - 174.4 µs + - 292.0 µs + - 28.4 µs + - 39.2 µs + * - shelve (dbm) + - 51.1 µs + - 82.2 µs + - 6.2 µs + - 7.8 µs + +.. list-table:: 64 KB values, 4,000 sets then 4,000 gets over 500 keys + :header-rows: 1 + :widths: 30 12 12 12 12 + + * - Store + - set p50 + - set p99 + - get p50 + - get p99 + * - diskcache-community Cache + - 229.5 µs + - 399.1 µs + - 17.4 µs + - 31.6 µs + * - diskcache-community FanoutCache(8) + - 296.9 µs + - 429.2 µs + - 17.8 µs + - 31.1 µs + * - diskcache 5.6.3 Cache + - 250.9 µs + - 419.8 µs + - 17.2 µs + - 30.8 µs + * - memcached, pymemcache + - 27.7 µs + - 36.2 µs + - 33.0 µs + - 44.3 µs + * - memcached, pylibmc + - 27.6 µs + - 37.8 µs + - 29.2 µs + - 37.5 µs + * - Redis, redis-py + - 57.5 µs + - 67.6 µs + - 53.9 µs + - 65.5 µs + * - lmdb + - 6.02 ms + - 8.05 ms + - 4.2 µs + - 21.1 µs + * - sqlitedict + - 282.2 µs + - 449.3 µs + - 40.8 µs + - 51.9 µs + * - shelve (dbm) + - 110.9 µs + - 1.90 ms + - 21.4 µs + - 27.6 µs + +.. list-table:: 1 MB values, 400 sets then 400 gets over 50 keys + :header-rows: 1 + :widths: 30 12 12 12 12 + + * - Store + - set p50 + - set p99 + - get p50 + - get p99 + * - diskcache-community Cache + - 351.3 µs + - 483.9 µs + - 54.4 µs + - 79.7 µs + * - diskcache-community FanoutCache(8) + - 352.3 µs + - 484.6 µs + - 54.2 µs + - 81.3 µs + * - diskcache 5.6.3 Cache + - 676.4 µs + - 834.5 µs + - 55.0 µs + - 81.7 µs + * - memcached, pymemcache + - 122.2 µs + - 135.9 µs + - 220.2 µs + - 238.4 µs + * - memcached, pylibmc + - 301.8 µs + - 319.7 µs + - 154.8 µs + - 168.2 µs + * - Redis, redis-py + - 161.2 µs + - 180.4 µs + - 166.8 µs + - 192.1 µs + * - lmdb + - 6.99 ms + - 9.04 ms + - 34.7 µs + - 211.6 µs + * - sqlitedict + - 2.14 ms + - 2.99 ms + - 190.1 µs + - 221.6 µs + * - shelve (dbm) + - 1.24 ms + - 4.35 ms + - 264.8 µs + - 307.8 µs + + +Hit ratio +--------- + +Gets over a working set of 100,000 keys with 256-byte values, 100,000 +operations, at a 95 % and a 50 % hit ratio. Misses are requests for keys that +were never stored. + +.. list-table:: Get latency by hit ratio + :header-rows: 1 + :widths: 30 12 12 12 12 12 + + * - Store + - 95 % hits p50 + - 95 % hits p99 + - 50 % hits p50 + - 50 % hits p99 + - on disk + * - diskcache-community Cache + - 3.2 µs + - 4.3 µs + - 2.6 µs + - 4.0 µs + - 68 MB + * - diskcache-community FanoutCache(8) + - 3.1 µs + - 4.4 µs + - 2.8 µs + - 4.3 µs + - 68 MB + * - diskcache 5.6.3 Cache + - 2.8 µs + - 4.0 µs + - 2.5 µs + - 4.0 µs + - 69 MB + * - memcached, pymemcache + - 17.5 µs + - 24.5 µs + - 16.8 µs + - 24.1 µs + - server + * - memcached, pylibmc + - 15.8 µs + - 22.6 µs + - 15.6 µs + - 22.4 µs + - server + * - Redis, redis-py + - 40.0 µs + - 50.6 µs + - 40.7 µs + - 50.8 µs + - server + * - lmdb + - 0.9 µs + - 1.8 µs + - 0.7 µs + - 1.9 µs + - 49 MB + * - sqlitedict + - 27.9 µs + - 42.7 µs + - 27.4 µs + - 41.3 µs + - 30 MB + * - shelve (dbm) + - 5.0 µs + - 6.7 µs + - 3.5 µs + - 6.3 µs + - 33 MB + + +Concurrency +----------- + +The classic mix from 4 and 8 processes sharing one store, and from 8 threads +in one process, each worker running the full 100,000 operations. sqlitedict and +shelve are single-process stores by design and are left out of the process +workloads; shelve's dbm objects are bound to the thread that opened them, so it +is left out of the thread workload too. The memcached clients are not +thread-safe, so each thread gets its own connection, as pymemcache's +PooledClient and pylibmc's ThreadMappedPool hand out. Cells show get p50 / +set p50. + +.. image:: _static/benchmarks/bench-throughput.png + +.. list-table:: Latency under concurrency, get p50 / set p50 + :header-rows: 1 + :widths: 30 16 16 16 16 + + * - Store + - 1 process + - 4 processes + - 8 processes + - 8 threads + * - diskcache-community Cache + - 2.6 µs / 19.2 µs + - 2.6 µs / 20.3 µs + - 2.6 µs / 20.3 µs + - 2.5 µs / 20.1 µs + * - diskcache-community FanoutCache(8) + - 2.8 µs / 20.5 µs + - 3.3 µs / 28.8 µs + - 3.5 µs / 32.9 µs + - 216.6 µs / 1.26 ms + * - diskcache 5.6.3 Cache + - 2.6 µs / 22.3 µs + - 2.5 µs / 23.0 µs + - 2.5 µs / 22.8 µs + - 2.6 µs / 23.8 µs + * - memcached, pymemcache + - 17.6 µs / 18.5 µs + - 23.3 µs / 23.8 µs + - 38.3 µs / 38.6 µs + - 88.8 µs / 87.8 µs + * - memcached, pylibmc + - 15.7 µs / 16.0 µs + - 20.6 µs / 20.8 µs + - 33.3 µs / 33.6 µs + - 45.9 µs / 46.3 µs + * - Redis, redis-py + - 40.9 µs / 43.6 µs + - 51.8 µs / 55.9 µs + - 70.8 µs / 75.1 µs + - 223.1 µs / 224.3 µs + * - lmdb + - 3.4 µs / 5.98 ms + - 3.2 µs / 23.96 ms + - 3.3 µs / 48.03 ms + - 4.9 µs / 49.00 ms + * - sqlitedict + - 27.2 µs / 147.0 µs + - not run + - not run + - 404.8 µs / 525.9 µs + * - shelve (dbm) + - 3.8 µs / 34.3 µs + - not run + - not run + - not run + +.. list-table:: Operations per second, all workers together + :header-rows: 1 + :widths: 30 16 16 16 16 + + * - Store + - 1 process + - 4 processes + - 8 processes + - 8 threads + * - diskcache-community Cache + - 197044 + - 179441 + - 184652 + - 188028 + * - diskcache-community FanoutCache(8) + - 182771 + - 233447 + - 291478 + - 19715 + * - diskcache 5.6.3 Cache + - 185562 + - 173486 + - 176978 + - 174210 + * - memcached, pymemcache + - 53497 + - 152049 + - 181101 + - 77380 + * - memcached, pylibmc + - 58268 + - 169042 + - 211748 + - 146576 + * - Redis, redis-py + - 23519 + - 71765 + - 101483 + - 32954 + * - lmdb + - 1592 + - 1636 + - 1625 + - 1600 + * - sqlitedict + - 24246 + - not run + - not run + - 18344 + * - shelve (dbm) + - 131608 + - not run + - not run + - not run + + +The 8-thread column is dominated by SQLite's behaviour under threads on this +machine: a plain ``SELECT`` through Python's ``sqlite3`` module takes about +2 µs from one thread and about 370 µs from each of eight threads reading the +same database at once, 20,000 queries per second in total, and eight private +``Cache`` objects, one per thread, hit the same wall. The shared ``Cache`` +escapes it because its threads queue on one database's write lock, so fewer +of them are inside SQLite at any instant; ``FanoutCache(8)`` spreads writes +over eight databases, keeps all eight threads querying at once and pays the +full cost, in diskcache 5.6.3 exactly as in the fork. The same +``FanoutCache`` under 8 processes is the fastest DiskCache configuration on +the page. sqlitedict pays through the queue in front of its single +connection; lmdb and the memory stores have no such cliff. + +Django backends +--------------- + +The classic mix through Django's cache API with each backend in turn, which +adds Django's key building and versioning on top of every operation. +``DatabaseCache`` uses SQLite; ``RedisCache`` is Django's own backend. + +.. image:: _static/benchmarks/bench-django.png + +.. list-table:: Get latency, Django backends + :header-rows: 1 + :widths: 30 10 18 10 10 10 8 + + * - Store + - p50 + - p50 across runs + - p90 + - p99 + - max + - misses + * - LocMemCache + - 1.7 µs + - 1.7 µs to 1.7 µs + - 1.8 µs + - 1.9 µs + - 27.1 µs + - 8714 + * - DjangoCache, diskcache-community + - 3.7 µs + - 3.7 µs to 3.7 µs + - 4.2 µs + - 5.1 µs + - 30.5 µs + - 8714 + * - DjangoCache, diskcache 5.6.3 + - 3.6 µs + - 3.5 µs to 3.6 µs + - 4.1 µs + - 5.0 µs + - 36.9 µs + - 8714 + * - PyMemcacheCache + - 26.3 µs + - 26.2 µs to 26.5 µs + - 30.2 µs + - 35.4 µs + - 97.2 µs + - 8714 + * - PyLibMCCache + - 20.7 µs + - 20.7 µs to 20.8 µs + - 23.9 µs + - 28.0 µs + - 70.9 µs + - 8714 + * - RedisCache + - 75.5 µs + - 75.1 µs to 76.0 µs + - 82.5 µs + - 89.1 µs + - 247.8 µs + - 8714 + * - FileBasedCache + - 14.9 µs + - 14.8 µs to 15.1 µs + - 19.6 µs + - 26.8 µs + - 61.0 µs + - 8714 + * - DatabaseCache (SQLite) + - 16.5 µs + - 16.4 µs to 16.6 µs + - 20.9 µs + - 27.3 µs + - 51.0 µs + - 8714 + +.. list-table:: Set latency, Django backends + :header-rows: 1 + :widths: 30 10 18 10 10 10 8 + + * - Store + - p50 + - p50 across runs + - p90 + - p99 + - max + - misses + * - LocMemCache + - 2.0 µs + - 2.0 µs to 2.0 µs + - 2.0 µs + - 2.2 µs + - 20.5 µs + - 0 + * - DjangoCache, diskcache-community + - 24.8 µs + - 24.7 µs to 24.8 µs + - 31.7 µs + - 48.1 µs + - 713.0 µs + - 0 + * - DjangoCache, diskcache 5.6.3 + - 24.1 µs + - 24.1 µs to 24.1 µs + - 31.2 µs + - 46.4 µs + - 1.47 ms + - 0 + * - PyMemcacheCache + - 27.0 µs + - 27.0 µs to 27.1 µs + - 30.9 µs + - 36.2 µs + - 75.5 µs + - 0 + * - PyLibMCCache + - 21.7 µs + - 21.7 µs to 21.7 µs + - 24.5 µs + - 28.8 µs + - 55.3 µs + - 0 + * - RedisCache + - 79.2 µs + - 78.9 µs to 80.0 µs + - 86.2 µs + - 94.0 µs + - 277.0 µs + - 0 + * - FileBasedCache + - 193.8 µs + - 193.7 µs to 196.0 µs + - 231.7 µs + - 309.6 µs + - 1.52 ms + - 0 + * - DatabaseCache (SQLite) + - 223.8 µs + - 222.7 µs to 227.8 µs + - 248.3 µs + - 308.2 µs + - 2.80 ms + - 0 + +.. list-table:: Delete latency, Django backends + :header-rows: 1 + :widths: 30 10 18 10 10 10 8 + + * - Store + - p50 + - p50 across runs + - p90 + - p99 + - max + - misses + * - LocMemCache + - 1.5 µs + - 1.5 µs to 1.6 µs + - 1.6 µs + - 2.0 µs + - 8.3 µs + - 111 + * - DjangoCache, diskcache-community + - 23.2 µs + - 23.0 µs to 23.2 µs + - 32.7 µs + - 46.5 µs + - 756.9 µs + - 111 + * - DjangoCache, diskcache 5.6.3 + - 22.3 µs + - 22.2 µs to 22.4 µs + - 31.9 µs + - 48.5 µs + - 842.8 µs + - 111 + * - PyMemcacheCache + - 25.8 µs + - 25.7 µs to 25.8 µs + - 29.5 µs + - 34.9 µs + - 45.4 µs + - 111 + * - PyLibMCCache + - 20.7 µs + - 20.7 µs to 20.8 µs + - 23.6 µs + - 27.5 µs + - 34.9 µs + - 111 + * - RedisCache + - 74.6 µs + - 74.2 µs to 75.3 µs + - 81.4 µs + - 89.7 µs + - 103.3 µs + - 111 + * - FileBasedCache + - 29.7 µs + - 29.2 µs to 29.8 µs + - 39.3 µs + - 47.9 µs + - 141.4 µs + - 111 + * - DatabaseCache (SQLite) + - 207.7 µs + - 207.7 µs to 208.2 µs + - 236.5 µs + - 289.8 µs + - 522.6 µs + - 111 + +.. list-table:: Throughput and footprint, Django backends + :header-rows: 1 + :widths: 30 14 12 20 + + * - Store + - ops per second + - wall time + - on disk after the run + * - LocMemCache + - 457085 + - 0.22 s + - in memory + * - DjangoCache, diskcache-community + - 147952 + - 0.67 s + - 32 MB + * - DjangoCache, diskcache 5.6.3 + - 150672 + - 0.66 s + - 32 MB + * - PyMemcacheCache + - 35724 + - 2.77 s + - server + * - PyLibMCCache + - 45486 + - 2.18 s + - server + * - RedisCache + - 12930 + - 7.66 s + - server + * - FileBasedCache + - 30159 + - 3.28 s + - 5 KB + * - DatabaseCache (SQLite) + - 25504 + - 3.88 s + - 80 KB + + +Running the benchmarks +---------------------- + +The scripts need memcached and redis-server on the path, a venv with +diskcache-community, pymemcache, pylibmc, redis, sqlitedict, lmdb and Django, +and a second venv with diskcache 5.6.3 and Django for the mainline column:: + + python tests/benchmarks/driver.py --fork-python /bin/python \ + --mainline-python /bin/python --out results/ + python tests/benchmarks/driver.py --django --fork-python /bin/python \ + --mainline-python /bin/python --out results/ + python tests/benchmarks/report.py --results results/ --docs docs/ + +The driver starts and stops the servers, runs one store at a time, and the +report script writes this page and its charts. diff --git a/docs/cache-benchmarks.rst b/docs/cache-benchmarks.rst index f55f5b9..d763ec2 100644 --- a/docs/cache-benchmarks.rst +++ b/docs/cache-benchmarks.rst @@ -1,5 +1,13 @@ -DiskCache Cache Benchmarks -========================== +Old Cache Benchmarks (2019) +=========================== + +.. note:: + + These are the old benchmarks, measured by upstream DiskCache for version 4 + in 2019 with the scripts ``tests/benchmark_core.py`` and + ``tests/benchmark_djangocache.py``. They are kept for reference. The current + measurements, with diskcache-community, diskcache 5.6.3 and today's + versions of the other stores, are on :doc:`benchmarks`. Accurately measuring performance is a difficult task. The benchmarks on this page are synthetic in the sense that they were designed to stress getting, diff --git a/docs/comparisons.rst b/docs/comparisons.rst index b858f0f..f2fbca2 100644 --- a/docs/comparisons.rst +++ b/docs/comparisons.rst @@ -69,7 +69,7 @@ Integrations? Django None None None None **Timings** -These are rough measurements. See :doc:`cache-benchmarks` for more +These are rough measurements. See :doc:`benchmarks` for more rigorous data. ================ ============= ========= ========= ============ ============ diff --git a/docs/development.rst b/docs/development.rst index 0329e8c..50c447c 100644 --- a/docs/development.rst +++ b/docs/development.rst @@ -62,7 +62,7 @@ Install development dependencies with `pip `_:: All packages for running tests and the lint checks will be installed. Additional packages like ``pylibmc`` and ``redis`` along with their server -counterparts are necessary for some benchmarks. +counterparts are necessary for the benchmarks. Testing ------- @@ -140,8 +140,15 @@ let it run overnight. Stress testing will stop at the first failure. Running Benchmarks ------------------ -Running and plotting benchmarks is a two step process. Each is a Python script -in the tests directory. Benchmark scripts are prefixed with ``benchmark_``. For +The :doc:`benchmarks` page is written by the scripts in ``tests/benchmarks``. +``driver.py`` starts memcached and Redis, runs each store in its own process +from a virtual environment with diskcache-community and one with diskcache +5.6.3, and stores JSON results; ``report.py`` turns the results into the page +and its charts. The page's last section lists the commands. + +The scripts below produced the :doc:`old benchmarks (2019) `. +Running and plotting them is a two step process. Each is a Python script in +the tests directory. Benchmark scripts are prefixed with ``benchmark_``. For example: :: diff --git a/docs/djangocache-benchmarks.rst b/docs/djangocache-benchmarks.rst index 6319e31..24352f0 100644 --- a/docs/djangocache-benchmarks.rst +++ b/docs/djangocache-benchmarks.rst @@ -1,5 +1,13 @@ -DiskCache DjangoCache Benchmarks -================================ +Old DjangoCache Benchmarks (2019) +================================= + +.. note:: + + These are the old benchmarks, measured by upstream DiskCache for version 4 + in 2019 with the scripts ``tests/benchmark_core.py`` and + ``tests/benchmark_djangocache.py``. They are kept for reference. The current + measurements, with diskcache-community, diskcache 5.6.3 and today's + versions of the other stores, are on :doc:`benchmarks`. :doc:`DiskCache ` provides a Django-compatible cache API in :class:`diskcache_community.DjangoCache`. A discussion of its options and diff --git a/docs/index.rst b/docs/index.rst index c2fc223..a6c0be2 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -21,13 +21,16 @@ Install it with pip and import ``diskcache_community`` where you imported $ pip install diskcache-community -Start with the :doc:`tutorial` and keep the :doc:`api` at hand. +Start with the :doc:`tutorial` and keep the :doc:`api` at hand. The +:doc:`benchmarks` compare DiskCache with memcached, Redis, lmdb and other +stores, and the fork with diskcache 5.6.3. .. toctree:: :maxdepth: 1 :titlesonly: tutorial + benchmarks cache-benchmarks djangocache-benchmarks case-study-web-crawler diff --git a/tests/benchmarks/django_bench.py b/tests/benchmarks/django_bench.py new file mode 100644 index 0000000..94ad66c --- /dev/null +++ b/tests/benchmarks/django_bench.py @@ -0,0 +1,173 @@ +"""The classic mix through Django's cache API, one backend per process. + + python django_bench.py --backend diskcache --runs 5 --out result.json + +Backends: locmem, filebased, db (SQLite), pymemcache, pylibmc, redis +(Django's own backend), diskcache (diskcache-community if the interpreter holds +it, else upstream diskcache). +""" + +import argparse +import json +import os +import shutil +import sys +import tempfile +import time + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +import runner # noqa: E402 +from stores import diskcache_package # noqa: E402 + + +def configure(backend, workdir): + import django + from django.conf import settings + + caches = { + 'locmem': { + 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', + 'LOCATION': 'bench', + }, + 'filebased': { + 'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', + 'LOCATION': os.path.join(workdir, 'filebased'), + }, + 'db': { + 'BACKEND': 'django.core.cache.backends.db.DatabaseCache', + 'LOCATION': 'bench_cache_table', + }, + 'pymemcache': { + 'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache', + 'LOCATION': '127.0.0.1:11211', + }, + 'pylibmc': { + 'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache', + 'LOCATION': '127.0.0.1:11211', + }, + 'redis': { + 'BACKEND': 'django.core.cache.backends.redis.RedisCache', + 'LOCATION': 'redis://127.0.0.1:6379/2', + }, + 'diskcache': { + 'BACKEND': diskcache_package().__name__ + '.DjangoCache', + 'LOCATION': os.path.join(workdir, 'diskcache'), + }, + } + settings.configure( + CACHES={'default': caches[backend]}, + DATABASES={ + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(workdir, 'db.sqlite3'), + } + }, + INSTALLED_APPS=[], + USE_TZ=True, + ) + django.setup() + if backend == 'db': + from django.core.management import call_command + + call_command('createcachetable', verbosity=0) + from django.core.cache import cache + + return cache + + +class DjangoStore: + """Adapter so the classic mix can drive Django's cache API.""" + + def __init__(self, cache): + self.cache = cache + + def get(self, key): + return self.cache.get(key.decode()) + + def set(self, key, value): + self.cache.set(key.decode(), value) + return True + + def delete(self, key): + return self.cache.delete(key.decode()) + + +def one_run(backend): + workdir = tempfile.mkdtemp(prefix='bench-django-') + cache = configure(backend, workdir) + cache.clear() + store = DjangoStore(cache) + runner.prepopulate(store) + t0 = time.perf_counter() + timings, misses = runner.run_classic(store, seed=0) + elapsed = time.perf_counter() - t0 + footprint = runner.stores.tree_size(workdir) + cache.clear() + try: + cache.close() + except Exception: # noqa: BLE001 + pass + shutil.rmtree(workdir, ignore_errors=True) + return runner.summarise(timings, misses, elapsed, footprint) + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('--backend', required=True) + parser.add_argument('--runs', type=int, default=5) + parser.add_argument('--warmup-runs', type=int, default=1) + parser.add_argument('--out', required=True) + args = parser.parse_args() + # Django can be configured once per process, so every run is a child process. + if os.environ.get('BENCH_DJANGO_CHILD'): + print(json.dumps(one_run(args.backend))) + return + import subprocess + + runs = [] + env = dict(os.environ, BENCH_DJANGO_CHILD='1') + for i in range(args.warmup_runs + args.runs): + proc = subprocess.run( + [ + sys.executable, + '-I', + __file__, + '--backend', + args.backend, + '--out', + args.out, + ], + env=env, + capture_output=True, + text=True, + ) + if proc.returncode != 0: + sys.stderr.write(proc.stderr) + raise SystemExit(proc.returncode) + if i >= args.warmup_runs: + runs.append(json.loads(proc.stdout.strip().splitlines()[-1])) + print('run', i, 'done', file=sys.stderr) + import django + + version = '' + if args.backend == 'diskcache': + diskcache = diskcache_package() + + version = '%s %s' % (diskcache.__title__, diskcache.__version__) + doc = { + 'store': 'django-' + args.backend, + 'store_name': 'Django %s %s' % (django.get_version(), args.backend), + 'store_version': version, + 'workload': 'django', + 'runs': runs, + 'environment': runner.environment(), + 'ops': runner.OPS, + 'warmup': runner.WARMUP, + } + os.makedirs(os.path.dirname(os.path.abspath(args.out)), exist_ok=True) + with open(args.out, 'w') as writer: + json.dump(doc, writer, indent=1) + + +if __name__ == '__main__': + main() diff --git a/tests/benchmarks/driver.py b/tests/benchmarks/driver.py new file mode 100644 index 0000000..e15a40b --- /dev/null +++ b/tests/benchmarks/driver.py @@ -0,0 +1,282 @@ +"""Run the whole benchmark matrix. + + python driver.py --fork-python /bin/python \ + --mainline-python /bin/python \ + --out results/ [--stores ...] [--workloads ...] [--runs 5] + +The fork interpreter needs diskcache-community plus pymemcache, pylibmc, +redis, sqlitedict and lmdb; the mainline interpreter needs diskcache 5.6.3. +memcached and redis-server must be on PATH; they are started on their default +ports for the duration of the run, restarted if they die, and stopped +afterwards. Every item runs in its own process group with a timeout, so a +hung store cannot stall the matrix. Finished items are skipped on rerun. +""" + +import argparse +import os +import signal +import socket +import subprocess +import time + +HERE = os.path.dirname(os.path.abspath(__file__)) +ALL_STORES = [ + 'cache', + 'fanout', + 'cache563', + 'pymemcache', + 'pylibmc', + 'redis', + 'sqlitedict', + 'shelve', + 'lmdb', +] +ALL_WORKLOADS = [ + 'classic', + 'sizes', + 'hit95', + 'hit50', + 'procs4', + 'procs8', + 'threads8', +] +CONCURRENT = {'procs4', 'procs8', 'threads8'} +# single-process stores by design: one writer, no cross-process locking +SINGLE_PROCESS = {'sqlitedict', 'shelve'} +# shelve's dbm objects are bound to the thread that opened them +SINGLE_THREAD = {'shelve'} +SERVER_OF = { + 'pymemcache': 'memcached', + 'pylibmc': 'memcached', + 'redis': 'redis', +} +PORTS = {'memcached': 11211, 'redis': 6379} + + +def log(*parts): + print(time.strftime('%H:%M:%S'), *parts, flush=True) + + +def port_open(port): + with socket.socket() as sock: + sock.settimeout(0.5) + return sock.connect_ex(('127.0.0.1', port)) == 0 + + +def wait_port(port, timeout=15): + deadline = time.time() + timeout + while time.time() < deadline: + if port_open(port): + return True + time.sleep(0.1) + return False + + +class Servers: + COMMANDS = { + # memcached: default memory limit raised and item size raised to 4 MB for the 1 MB tier + 'memcached': ['memcached', '-p', '11211', '-m', '1024', '-I', '4m'], + # redis: no persistence, so the comparison is memory-only like memcached + 'redis': [ + 'redis-server', + '--port', + '6379', + '--save', + '', + '--appendonly', + 'no', + '--loglevel', + 'warning', + ], + } + + def __init__(self): + self.procs = {} + + def start(self, name): + self.procs[name] = subprocess.Popen( + self.COMMANDS[name], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + if not wait_port(PORTS[name]): + raise SystemExit('%s did not start' % name) + + def start_all(self): + for name in self.COMMANDS: + self.start(name) + + def ensure(self, name): + """Restart the server if it died; return True when a restart happened.""" + proc = self.procs.get(name) + if proc is not None and proc.poll() is None and port_open(PORTS[name]): + return False + log( + 'restart', + name, + '(exited with %s)' % (proc.poll() if proc else 'never started'), + ) + self.start(name) + return True + + def stop(self): + for proc in self.procs.values(): + proc.terminate() + for proc in self.procs.values(): + try: + proc.wait(10) + except subprocess.TimeoutExpired: + proc.kill() + self.procs = {} + + +def run_item(cmd, timeout): + """Run one benchmark process in its own process group; kill the group on timeout.""" + proc = subprocess.Popen( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + start_new_session=True, + ) + try: + _out, err = proc.communicate(timeout=timeout) + return proc.returncode, err + except subprocess.TimeoutExpired: + try: + os.killpg(proc.pid, signal.SIGKILL) + except ProcessLookupError: + pass + _out, err = proc.communicate() + return ( + 'timeout', + (err or '') + '\nkilled after %d s timeout\n' % timeout, + ) + + +def run_one(label, name, cmd, out, timeout): + t0 = time.time() + code, err = run_item(cmd, timeout) + status = 'ok' if code == 0 else 'FAILED (%s)' % code + log('%-9s %-20s %-18s %7.1fs' % (label, name, status, time.time() - t0)) + if code != 0: + with open(out + '.err', 'w') as writer: + writer.write(err) + print(err[-600:], flush=True) + + +DJANGO_BACKENDS = [ + 'locmem', + 'filebased', + 'db', + 'pymemcache', + 'pylibmc', + 'redis', + 'diskcache', +] + + +def run_django(args, servers): + jobs = [ + (backend, args.fork_python, 'django__' + backend) + for backend in DJANGO_BACKENDS + ] + jobs.append(('diskcache', args.mainline_python, 'django__diskcache563')) + for backend, python, name in jobs: + out = os.path.join(args.out, name + '.json') + if os.path.exists(out): + log('skip', name, '(exists)') + continue + if backend in SERVER_OF: + servers.ensure(SERVER_OF[backend]) + cmd = [ + python, + '-I', + os.path.join(HERE, 'django_bench.py'), + '--backend', + backend, + '--runs', + str(args.django_runs), + '--out', + out, + ] + run_one('django', name, cmd, out, args.item_timeout) + + +def run_matrix(args, servers): + for workload in args.workloads.split(','): + for store in args.stores.split(','): + if store in SINGLE_PROCESS and workload.startswith('procs'): + log('skip', workload, store, '(single-process store)') + continue + if store in SINGLE_THREAD and workload.startswith('threads'): + log('skip', workload, store, '(single-thread store)') + continue + python = ( + args.mainline_python + if store == 'cache563' + else args.fork_python + ) + out = os.path.join(args.out, '%s__%s.json' % (workload, store)) + if os.path.exists(out): + log('skip', workload, store, '(exists)') + continue + if store in SERVER_OF: + servers.ensure(SERVER_OF[store]) + runs = ( + args.concurrent_runs if workload in CONCURRENT else args.runs + ) + cmd = [ + python, + '-I', + os.path.join(HERE, 'runner.py'), + '--store', + store, + '--workload', + workload, + '--runs', + str(runs), + '--out', + out, + ] + run_one(workload, store, cmd, out, args.item_timeout) + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('--fork-python', required=True) + parser.add_argument('--mainline-python', required=True) + parser.add_argument('--out', required=True) + parser.add_argument('--stores', default=','.join(ALL_STORES)) + parser.add_argument('--workloads', default=','.join(ALL_WORKLOADS)) + parser.add_argument('--runs', type=int, default=5) + parser.add_argument('--concurrent-runs', type=int, default=3) + parser.add_argument( + '--item-timeout', + type=int, + default=7200, + help='seconds before one store/workload is killed', + ) + parser.add_argument( + '--django', + action='store_true', + help='run the Django backend matrix instead', + ) + parser.add_argument('--django-runs', type=int, default=3) + args = parser.parse_args() + os.makedirs(args.out, exist_ok=True) + servers = Servers() + servers.start_all() + log('servers up') + try: + if args.django: + run_django(args, servers) + else: + run_matrix(args, servers) + finally: + servers.stop() + log('servers down') + + +if __name__ == '__main__': + main() diff --git a/tests/benchmarks/report.py b/tests/benchmarks/report.py new file mode 100644 index 0000000..fdc62a9 --- /dev/null +++ b/tests/benchmarks/report.py @@ -0,0 +1,914 @@ +"""Turn the benchmark JSON results into reST tables, charts and the docs page. + +python report.py --results results/ --docs ../../docs +""" + +import argparse +import datetime +import glob +import json +import os +import re +import statistics + +STORE_ORDER = [ + 'cache', + 'fanout', + 'cache563', + 'pymemcache', + 'pylibmc', + 'redis', + 'lmdb', + 'sqlitedict', + 'shelve', +] +LABELS = { + 'cache': 'diskcache-community Cache', + 'fanout': 'diskcache-community FanoutCache(8)', + 'cache563': 'diskcache 5.6.3 Cache', + 'pymemcache': 'memcached, pymemcache', + 'pylibmc': 'memcached, pylibmc', + 'redis': 'Redis, redis-py', + 'lmdb': 'lmdb', + 'sqlitedict': 'sqlitedict', + 'shelve': 'shelve (dbm)', +} +DJANGO_ORDER = [ + 'django-locmem', + 'django-diskcache', + 'django-diskcache563', + 'django-pymemcache', + 'django-pylibmc', + 'django-redis', + 'django-filebased', + 'django-db', +] +DJANGO_LABELS = { + 'django-locmem': 'LocMemCache', + 'django-diskcache': 'DjangoCache, diskcache-community', + 'django-diskcache563': 'DjangoCache, diskcache 5.6.3', + 'django-pymemcache': 'PyMemcacheCache', + 'django-pylibmc': 'PyLibMCCache', + 'django-redis': 'RedisCache', + 'django-filebased': 'FileBasedCache', + 'django-db': 'DatabaseCache (SQLite)', +} +COLORS = { + 'cache': '#0F766E', + 'fanout': '#2DD4BF', + 'cache563': '#94A3B8', + 'pymemcache': '#F59E0B', + 'pylibmc': '#D97706', + 'redis': '#DC2626', + 'lmdb': '#7C3AED', + 'sqlitedict': '#2563EB', + 'shelve': '#64748B', +} + + +def load(results_dir): + """Results keyed by (workload, store) from the file name, which is the + driver's name for the item: the two Django diskcache files share a store + name inside.""" + docs = {} + for path in sorted(glob.glob(os.path.join(results_dir, '*.json'))): + with open(path) as reader: + doc = json.load(reader) + workload, store = os.path.basename(path)[: -len('.json')].split( + '__', 1 + ) + if workload == 'django': + store = 'django-' + store + docs[(workload, store)] = doc + return docs + + +def med(values): + values = [v for v in values if v is not None] + return statistics.median(values) if values else None + + +def stat(doc, action, field, tier=None): + """Median across runs of one statistic.""" + values = [] + for run in doc['runs']: + block = run['tiers'][tier][action] if tier else run['ops'].get(action) + if block: + values.append(block[field]) + return med(values) + + +def spread(doc, action, field): + values = [ + run['ops'][action][field] + for run in doc['runs'] + if action in run['ops'] + ] + return (min(values), max(values)) if values else (None, None) + + +def fmt_ns(value): + if value is None: + return 'n/a' + us = value / 1000 + if us >= 1000: + return '%.2f ms' % (us / 1000) + return '%.1f µs' % us + + +def fmt_bytes(value): + if value is None: + return 'n/a' + if value == 0: + return 'server' + for unit in ('B', 'KB', 'MB', 'GB'): + if value < 1024: + return '%.0f %s' % (value, unit) + value /= 1024 + return '%.1f TB' % value + + +def list_table(title, header, rows, widths=None): + out = ['.. list-table:: %s' % title, ' :header-rows: 1'] + if widths: + out.append(' :widths: %s' % ' '.join(str(w) for w in widths)) + out.append('') + for i, row in enumerate([header] + rows): + for j, cell in enumerate(row): + prefix = ' * - ' if j == 0 else ' - ' + out.append(prefix + str(cell)) + out.append('') + return '\n'.join(out) + + +def op_table(docs, workload, action, title, stores=STORE_ORDER, labels=LABELS): + rows = [] + for store in stores: + doc = docs.get((workload, store)) + if not doc: + continue + p50 = stat(doc, action, 'p50') + if p50 is None: + continue + lo, hi = spread(doc, action, 'p50') + rows.append( + [ + labels[store], + fmt_ns(p50), + '%s to %s' % (fmt_ns(lo), fmt_ns(hi)), + fmt_ns(stat(doc, action, 'p90')), + fmt_ns(stat(doc, action, 'p99')), + fmt_ns(stat(doc, action, 'max')), + '%d' % stat(doc, action, 'misses'), + ] + ) + return list_table( + title, + ['Store', 'p50', 'p50 across runs', 'p90', 'p99', 'max', 'misses'], + rows, + [30, 10, 18, 10, 10, 10, 8], + ) + + +def summary_table(docs, workload, title, stores=STORE_ORDER, labels=LABELS): + rows = [] + for store in stores: + doc = docs.get((workload, store)) + if not doc: + continue + footprint = fmt_bytes(med([r['footprint_bytes'] for r in doc['runs']])) + if store == 'django-locmem': + footprint = 'in memory' + rows.append( + [ + labels[store], + '%.0f' % med([r['throughput_ops_s'] for r in doc['runs']]), + '%.2f s' % med([r['elapsed_s'] for r in doc['runs']]), + footprint, + ] + ) + return list_table( + title, + ['Store', 'ops per second', 'wall time', 'on disk after the run'], + rows, + [30, 14, 12, 20], + ) + + +def sizes_table(docs, tier, title): + rows = [] + for store in STORE_ORDER: + doc = docs.get(('sizes', store)) + if not doc or tier not in doc['runs'][0]['tiers']: + continue + rows.append( + [ + LABELS[store], + fmt_ns(stat(doc, 'set', 'p50', tier)), + fmt_ns(stat(doc, 'set', 'p99', tier)), + fmt_ns(stat(doc, 'get', 'p50', tier)), + fmt_ns(stat(doc, 'get', 'p99', tier)), + ] + ) + return list_table( + title, + ['Store', 'set p50', 'set p99', 'get p50', 'get p99'], + rows, + [30, 12, 12, 12, 12], + ) + + +def hit_table(docs, title): + rows = [] + for store in STORE_ORDER: + d95, d50 = docs.get(('hit95', store)), docs.get(('hit50', store)) + if not (d95 and d50): + continue + rows.append( + [ + LABELS[store], + fmt_ns(stat(d95, 'get', 'p50')), + fmt_ns(stat(d95, 'get', 'p99')), + fmt_ns(stat(d50, 'get', 'p50')), + fmt_ns(stat(d50, 'get', 'p99')), + fmt_bytes(med([r['footprint_bytes'] for r in d95['runs']])), + ] + ) + return list_table( + title, + [ + 'Store', + '95 % hits p50', + '95 % hits p99', + '50 % hits p50', + '50 % hits p99', + 'on disk', + ], + rows, + [30, 12, 12, 12, 12, 12], + ) + + +def concurrency_table(docs, title): + rows = [] + for store in STORE_ORDER: + cells = [LABELS[store]] + found = False + for workload in ('classic', 'procs4', 'procs8', 'threads8'): + doc = docs.get((workload, store)) + if doc: + found = True + cells.append( + '%s / %s' + % ( + fmt_ns(stat(doc, 'get', 'p50')), + fmt_ns(stat(doc, 'set', 'p50')), + ) + ) + else: + cells.append('not run') + if found: + rows.append(cells) + return list_table( + title, + ['Store', '1 process', '4 processes', '8 processes', '8 threads'], + rows, + [30, 16, 16, 16, 16], + ) + + +def throughput_table(docs, title): + rows = [] + for store in STORE_ORDER: + cells = [LABELS[store]] + found = False + for workload in ('classic', 'procs4', 'procs8', 'threads8'): + doc = docs.get((workload, store)) + if doc: + found = True + cells.append( + '%.0f' % med([r['throughput_ops_s'] for r in doc['runs']]) + ) + else: + cells.append('not run') + if found: + rows.append(cells) + return list_table( + title, + ['Store', '1 process', '4 processes', '8 processes', '8 threads'], + rows, + [30, 16, 16, 16, 16], + ) + + +# Rows of the fork-against-5.6.3 table: label, workload, the two store keys, +# what to compare, the action and the size tier. +FORK_ROWS = [ + ( + 'Classic mix, get p50', + 'classic', + 'cache', + 'cache563', + 'p50', + 'get', + None, + ), + ( + 'Classic mix, set p50', + 'classic', + 'cache', + 'cache563', + 'p50', + 'set', + None, + ), + ( + 'Classic mix, delete p50', + 'classic', + 'cache', + 'cache563', + 'p50', + 'delete', + None, + ), + ( + 'Classic mix, ops per second', + 'classic', + 'cache', + 'cache563', + 'throughput', + None, + None, + ), + ( + '100-byte values, set p50', + 'sizes', + 'cache', + 'cache563', + 'p50', + 'set', + '100 B', + ), + ( + '64 KB values, set p50', + 'sizes', + 'cache', + 'cache563', + 'p50', + 'set', + '64 KB', + ), + ( + '1 MB values, set p50', + 'sizes', + 'cache', + 'cache563', + 'p50', + 'set', + '1 MB', + ), + ( + '1 MB values, get p50', + 'sizes', + 'cache', + 'cache563', + 'p50', + 'get', + '1 MB', + ), + ('95 % hits, get p50', 'hit95', 'cache', 'cache563', 'p50', 'get', None), + ( + '8 processes, set p50', + 'procs8', + 'cache', + 'cache563', + 'p50', + 'set', + None, + ), + ( + '8 threads, ops per second', + 'threads8', + 'cache', + 'cache563', + 'throughput', + None, + None, + ), + ( + 'Django, get p50', + 'django', + 'django-diskcache', + 'django-diskcache563', + 'p50', + 'get', + None, + ), + ( + 'Django, set p50', + 'django', + 'django-diskcache', + 'django-diskcache563', + 'p50', + 'set', + None, + ), +] + + +def fork_table(docs, title): + rows = [] + for label, workload, new, old, kind, action, tier in FORK_ROWS: + new_doc, old_doc = docs.get((workload, new)), docs.get((workload, old)) + if not (new_doc and old_doc): + continue + if kind == 'throughput': + values = [ + med([r['throughput_ops_s'] for r in doc['runs']]) + for doc in (old_doc, new_doc) + ] + cells = ['%.0f' % value for value in values] + else: + values = [ + stat(doc, action, kind, tier) for doc in (old_doc, new_doc) + ] + cells = [fmt_ns(value) for value in values] + if None in values or not values[0]: + continue + change = (values[1] - values[0]) / values[0] * 100 + rows.append([label] + cells + ['%+.0f %%' % change]) + return list_table( + title, + ['Measure', 'diskcache 5.6.3', 'diskcache-community', 'change'], + rows, + [34, 18, 20, 10], + ) + + +def measured_date(docs, results_dir): + """The date the results were measured: recorded by the runner, else the + modification date of the newest result file.""" + dates = [doc['environment'].get('date') for doc in docs.values()] + dates = [date for date in dates if date] + if dates: + return max(dates) + paths = glob.glob(os.path.join(results_dir, '*.json')) + newest = max(os.path.getmtime(path) for path in paths) + return datetime.date.fromtimestamp(newest).isoformat() + + +# ---------------------------------------------------------------- charts + + +def chart_classic(docs, out_dir, workload='classic', name='classic'): + import matplotlib + + matplotlib.use('Agg') + import matplotlib.pyplot as plt + + actions = ['get', 'set', 'delete'] + fig, axes = plt.subplots(1, 3, figsize=(13, 4.2), sharey=False) + for ax, action in zip(axes, actions): + stores = [ + s + for s in STORE_ORDER + if (workload, s) in docs + and stat(docs[(workload, s)], action, 'p50') + ] + y = range(len(stores)) + for offset, field, alpha in ( + (-0.27, 'p50', 1.0), + (0.0, 'p90', 0.7), + (0.27, 'p99', 0.45), + ): + values = [ + stat(docs[(workload, s)], action, field) / 1000 for s in stores + ] + ax.barh( + [i + offset for i in y], + values, + height=0.26, + color=[COLORS[s] for s in stores], + alpha=alpha, + label=field, + ) + ax.set_yticks(list(y)) + ax.set_yticklabels( + [LABELS[s] for s in stores] + if ax is axes[0] + else [''] * len(stores), + fontsize=8, + ) + ax.invert_yaxis() + ax.set_xscale('log') + ax.set_xlabel('%s latency, µs (log), p50 dark to p99 light' % action) + ax.grid(axis='x', alpha=0.3) + fig.tight_layout() + path = os.path.join(out_dir, 'bench-%s.png' % name) + fig.savefig(path, dpi=130) + plt.close(fig) + return path + + +def chart_sizes(docs, out_dir): + import matplotlib + + matplotlib.use('Agg') + import matplotlib.pyplot as plt + + tiers = ['100 B', '8 KB', '64 KB', '1 MB'] + fig, axes = plt.subplots(1, 2, figsize=(11, 4.2)) + for ax, action in zip(axes, ('set', 'get')): + for store in STORE_ORDER: + doc = docs.get(('sizes', store)) + if not doc: + continue + values = [stat(doc, action, 'p50', t) for t in tiers] + if any(v is None for v in values): + continue + ax.plot( + tiers, + [v / 1000 for v in values], + marker='o', + color=COLORS[store], + label=LABELS[store], + ) + ax.set_yscale('log') + ax.set_ylabel('%s p50, µs (log)' % action) + ax.set_xlabel('value size') + ax.grid(alpha=0.3) + axes[1].legend(fontsize=7, loc='upper left') + fig.tight_layout() + path = os.path.join(out_dir, 'bench-sizes.png') + fig.savefig(path, dpi=130) + plt.close(fig) + return path + + +def chart_throughput(docs, out_dir): + import matplotlib + + matplotlib.use('Agg') + import matplotlib.pyplot as plt + + workloads = [ + ('classic', '1 process'), + ('procs4', '4 processes'), + ('procs8', '8 processes'), + ('threads8', '8 threads'), + ] + stores = [s for s in STORE_ORDER if ('classic', s) in docs] + fig, ax = plt.subplots(figsize=(11, 4.2)) + width = 0.8 / len(workloads) + for j, (workload, label) in enumerate(workloads): + values = [] + for s in stores: + doc = docs.get((workload, s)) + values.append( + med([r['throughput_ops_s'] for r in doc['runs']]) if doc else 0 + ) + ax.bar( + [i + (j - 1.5) * width for i in range(len(stores))], + values, + width=width, + label=label, + alpha=0.5 + 0.15 * j, + color=[COLORS[s] for s in stores], + ) + ax.set_xticks(range(len(stores))) + ax.set_xticklabels( + [LABELS[s] for s in stores], rotation=25, ha='right', fontsize=8 + ) + ax.set_ylabel('operations per second, all workers') + ax.set_yscale('log') + ax.grid(axis='y', alpha=0.3) + ax.legend(fontsize=8) + fig.tight_layout() + path = os.path.join(out_dir, 'bench-throughput.png') + fig.savefig(path, dpi=130) + plt.close(fig) + return path + + +def chart_django(docs, out_dir): + import matplotlib + + matplotlib.use('Agg') + import matplotlib.pyplot as plt + + backends = [b for b in DJANGO_ORDER if ('django', b) in docs] + fig, axes = plt.subplots(1, 3, figsize=(13, 4.2)) + for ax, action in zip(axes, ('get', 'set', 'delete')): + y = range(len(backends)) + for offset, field, alpha in ( + (-0.27, 'p50', 1.0), + (0.0, 'p90', 0.7), + (0.27, 'p99', 0.45), + ): + values = [ + stat(docs[('django', b)], action, field) / 1000 + for b in backends + ] + ax.barh( + [i + offset for i in y], + values, + height=0.26, + color='#0F766E', + alpha=alpha, + ) + ax.set_yticks(list(y)) + ax.set_yticklabels( + [DJANGO_LABELS[b] for b in backends] + if ax is axes[0] + else [''] * len(backends), + fontsize=8, + ) + ax.invert_yaxis() + ax.set_xscale('log') + ax.set_xlabel('%s latency, µs (log), p50 dark to p99 light' % action) + ax.grid(axis='x', alpha=0.3) + fig.tight_layout() + path = os.path.join(out_dir, 'bench-django.png') + fig.savefig(path, dpi=130) + plt.close(fig) + return path + + +# ------------------------------------------------------------------ page + + +def environment_section(docs): + env = next(iter(docs.values()))['environment'] + versions = [] + for store in STORE_ORDER: + doc = docs.get(('classic', store)) + if doc: + versions.append( + '- %s: %s' + % (LABELS[store], doc['store_version'] or 'standard library') + ) + django = docs.get(('django', 'django-locmem')) + if django: + versions.append('- %s' % django['store_name'].replace(' locmem', '')) + platform = env['platform'] + match = re.match(r'^macOS-([\d.]+)-(\w+)', platform) + if match: + platform = 'macOS %s, %s' % match.groups() + redis = re.search(r'v=([\d.]+)', env['redis'] or '') + return '\n'.join( + [ + '- CPU: %s, %s cores' % (env['cpu'], env['cores']), + '- OS: %s' % platform, + '- Python: %s %s' % (env['implementation'], env['python']), + '- SQLite: %s' % env['sqlite'], + '- memcached: %s' % env['memcached'].splitlines()[0] + if env['memcached'] + else '- memcached: n/a', + '- Redis: %s' % (redis.group(1) if redis else 'n/a'), + ] + + versions + ) + + +PAGE = """DiskCache Benchmarks +==================== + +Measured on {date} with the published diskcache-community {fork_version} wheel. +Every number below comes from the scripts in ``tests/benchmarks`` of the +repository; the last section says how to run them. Treat the results as what +they are: one machine, synthetic workloads, short keys. They show the shape of +the costs, not what your application will see. + +The previous pages, :doc:`cache-benchmarks` and :doc:`djangocache-benchmarks`, +are the old benchmarks: upstream DiskCache measured them for version 4 in +2019, on other hardware and against other versions of every store. They stay +for reference; the classic mix below repeats their workload. + +Each store runs the same seeded operation sequence. Latencies are measured per +operation with the nanosecond performance counter and reported at the 50th, +90th and 99th percentiles and the maximum. Every workload runs once to warm up +and then five times (three for the multi-process and threaded workloads); the +tables show the median run, and the "p50 across runs" column shows how much +the median moved between runs. memcached runs with its item size limit raised +to 4 MB for the 1 MB tier and Redis with persistence off; both are separate +processes on localhost, so their numbers include a round trip over the +loopback interface. Every other store uses its default settings, and the notes +under each section say where a default matters. + +Environment +----------- + +{environment} + +diskcache-community and diskcache 5.6.3 +--------------------------------------- + +The same workloads, run against the fork and against the last upstream +release in separate environments on the same machine. For latencies a negative +change favours the fork; for operations per second a positive one does. + +{fork} + +The classic mix +--------------- + +The workload of the original 2016 page, kept so the two can be compared: keys +drawn from a space of 100, values of up to about 600 bytes, 90 % gets, 9 % +sets and 1 % deletes, 100,000 operations after a 1,000-operation warm-up, in +one process. The ~1 % miss rate comes from gets after deletes. + +.. image:: _static/benchmarks/bench-classic.png + +{classic_get} +{classic_set} +{classic_delete} +{classic_summary} + +lmdb's writes stand out because its defaults, ``sync`` and ``metasync`` on, +end every write transaction with two full disk flushes (``F_FULLFSYNC`` on +macOS), about 6 ms together on this machine; its reads are the fastest here. +It is the only store in the table that forces each write onto the disk before +returning. DiskCache runs SQLite in WAL mode with ``synchronous`` NORMAL, +which flushes at checkpoints, sqlitedict commits every operation through +SQLite's default rollback journal, and memcached and Redis keep data in +memory. + +Value sizes +----------- + +Sets followed by gets at four value sizes. The 64 KB and 1 MB tiers cross +DiskCache's default 32 KB threshold above which values live in files next to +the database; memcached was started with its item size limit raised to 4 MB +for the 1 MB tier. + +.. image:: _static/benchmarks/bench-sizes.png + +{sizes_100b} +{sizes_8kb} +{sizes_64kb} +{sizes_1mb} + +Hit ratio +--------- + +Gets over a working set of 100,000 keys with 256-byte values, 100,000 +operations, at a 95 % and a 50 % hit ratio. Misses are requests for keys that +were never stored. + +{hit} + +Concurrency +----------- + +The classic mix from 4 and 8 processes sharing one store, and from 8 threads +in one process, each worker running the full 100,000 operations. sqlitedict and +shelve are single-process stores by design and are left out of the process +workloads; shelve's dbm objects are bound to the thread that opened them, so it +is left out of the thread workload too. The memcached clients are not +thread-safe, so each thread gets its own connection, as pymemcache's +PooledClient and pylibmc's ThreadMappedPool hand out. Cells show get p50 / +set p50. + +.. image:: _static/benchmarks/bench-throughput.png + +{concurrency} +{throughput} + +The 8-thread column is dominated by SQLite's behaviour under threads on this +machine: a plain ``SELECT`` through Python's ``sqlite3`` module takes about +2 µs from one thread and about 370 µs from each of eight threads reading the +same database at once, 20,000 queries per second in total, and eight private +``Cache`` objects, one per thread, hit the same wall. The shared ``Cache`` +escapes it because its threads queue on one database's write lock, so fewer +of them are inside SQLite at any instant; ``FanoutCache(8)`` spreads writes +over eight databases, keeps all eight threads querying at once and pays the +full cost, in diskcache 5.6.3 exactly as in the fork. The same +``FanoutCache`` under 8 processes is the fastest DiskCache configuration on +the page. sqlitedict pays through the queue in front of its single +connection; lmdb and the memory stores have no such cliff. + +Django backends +--------------- + +The classic mix through Django's cache API with each backend in turn, which +adds Django's key building and versioning on top of every operation. +``DatabaseCache`` uses SQLite; ``RedisCache`` is Django's own backend. + +.. image:: _static/benchmarks/bench-django.png + +{django_get} +{django_set} +{django_delete} +{django_summary} + +Running the benchmarks +---------------------- + +The scripts need memcached and redis-server on the path, a venv with +diskcache-community, pymemcache, pylibmc, redis, sqlitedict, lmdb and Django, +and a second venv with diskcache 5.6.3 and Django for the mainline column:: + + python tests/benchmarks/driver.py --fork-python /bin/python \\ + --mainline-python /bin/python --out results/ + python tests/benchmarks/driver.py --django --fork-python /bin/python \\ + --mainline-python /bin/python --out results/ + python tests/benchmarks/report.py --results results/ --docs docs/ + +The driver starts and stops the servers, runs one store at a time, and the +report script writes this page and its charts. +""" + + +def build_page(docs, date): + fork = docs.get(('classic', 'cache')) + fork_version = fork['store_version'].split()[-1] if fork else '' + + def dj(action, title): + return op_table( + docs, 'django', action, title, DJANGO_ORDER, DJANGO_LABELS + ) + + return PAGE.format( + date=date, + fork_version=fork_version, + environment=environment_section(docs), + fork=fork_table(docs, 'diskcache-community against diskcache 5.6.3'), + classic_get=op_table( + docs, 'classic', 'get', 'Get latency, classic mix' + ), + classic_set=op_table( + docs, 'classic', 'set', 'Set latency, classic mix' + ), + classic_delete=op_table( + docs, 'classic', 'delete', 'Delete latency, classic mix' + ), + classic_summary=summary_table( + docs, 'classic', 'Throughput and footprint, classic mix' + ), + sizes_100b=sizes_table( + docs, + '100 B', + '100-byte values, 20,000 sets then 20,000 gets over 1,000 keys', + ), + sizes_8kb=sizes_table( + docs, + '8 KB', + '8 KB values, 20,000 sets then 20,000 gets over 1,000 keys', + ), + sizes_64kb=sizes_table( + docs, + '64 KB', + '64 KB values, 4,000 sets then 4,000 gets over 500 keys', + ), + sizes_1mb=sizes_table( + docs, '1 MB', '1 MB values, 400 sets then 400 gets over 50 keys' + ), + hit=hit_table(docs, 'Get latency by hit ratio'), + concurrency=concurrency_table( + docs, 'Latency under concurrency, get p50 / set p50' + ), + throughput=throughput_table( + docs, 'Operations per second, all workers together' + ), + django_get=dj('get', 'Get latency, Django backends'), + django_set=dj('set', 'Set latency, Django backends'), + django_delete=dj('delete', 'Delete latency, Django backends'), + django_summary=summary_table( + docs, + 'django', + 'Throughput and footprint, Django backends', + DJANGO_ORDER, + DJANGO_LABELS, + ), + ) + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('--results', required=True) + parser.add_argument('--docs', required=True) + parser.add_argument( + '--date', help='measurement date (default: from the results)' + ) + args = parser.parse_args() + docs = load(args.results) + charts_dir = os.path.join(args.docs, '_static', 'benchmarks') + os.makedirs(charts_dir, exist_ok=True) + chart_classic(docs, charts_dir) + chart_sizes(docs, charts_dir) + chart_throughput(docs, charts_dir) + if any(w == 'django' for w, _ in docs): + chart_django(docs, charts_dir) + page = build_page(docs, args.date or measured_date(docs, args.results)) + with open(os.path.join(args.docs, 'benchmarks.rst'), 'w') as writer: + writer.write(page) + print( + 'wrote', + os.path.join(args.docs, 'benchmarks.rst'), + 'and charts in', + charts_dir, + ) + + +if __name__ == '__main__': + main() diff --git a/tests/benchmarks/run_overnight.sh b/tests/benchmarks/run_overnight.sh new file mode 100755 index 0000000..89e22fc --- /dev/null +++ b/tests/benchmarks/run_overnight.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +# Run the whole benchmark matrix unattended on a quiet machine. +# +# tests/benchmarks/run_overnight.sh +# +# Keeps the Mac from idle-sleeping while it runs (the lid must stay open or +# an external display attached), logs to /driver.log, and writes +# /DONE when finished. Safe to rerun: finished stores are skipped. +set -uo pipefail +FORK=${1:?fork venv python} +MAIN=${2:?mainline venv python} +OUT=${3:?results dir} +HERE="$(cd "$(dirname "$0")" && pwd)" +mkdir -p "$OUT" +rm -f "$OUT/DONE" +{ + echo "start $(date '+%Y-%m-%d %H:%M:%S')" + caffeinate -i -s python3 "$HERE/driver.py" --fork-python "$FORK" --mainline-python "$MAIN" --out "$OUT" --runs 5 --concurrent-runs 3 + caffeinate -i -s python3 "$HERE/driver.py" --django --fork-python "$FORK" --mainline-python "$MAIN" --out "$OUT" --django-runs 3 + echo "end $(date '+%Y-%m-%d %H:%M:%S')" +} >> "$OUT/driver.log" 2>&1 +touch "$OUT/DONE" diff --git a/tests/benchmarks/runner.py b/tests/benchmarks/runner.py new file mode 100644 index 0000000..b3b6ae4 --- /dev/null +++ b/tests/benchmarks/runner.py @@ -0,0 +1,373 @@ +"""Run one store through one workload and write a JSON result. + + python runner.py --store cache --workload classic --runs 5 --out result.json + +Workloads + classic the 2016 mix: keys from a space of 100, values up to ~600 B, + 90 % get, 9 % set, 1 % delete, 100k ops, 1k warm-up + sizes set then get at 100 B, 8 KB, 64 KB, 1 MB + hit95 gets over a 100k-key working set with 95 % hits (the set is + loaded in bulk, unmeasured) + hit50 the same with 50 % hits + procs4 the classic mix from 4 processes on one shared store + procs8 the classic mix from 8 processes on one shared store + threads8 the classic mix from 8 threads in one process (memcached + clients get one connection per thread, as their pools do) + +Every measured operation records its latency in nanoseconds; the result +holds the percentiles, the throughput, the miss rate and the disk footprint +of every run, plus the environment. +""" + +import argparse +import datetime +import json +import multiprocessing +import os +import platform +import random +import shutil +import sqlite3 +import subprocess +import sys +import tempfile +import threading +import time + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +import stores # noqa: E402 + +OPS = int(os.environ.get('BENCH_OPS', 100_000)) +WARMUP = min(1_000, OPS // 10) +RANGE = 100 +SIZES = [ + ('100 B', 100, 20_000, 1_000), + ('8 KB', 8 * 1024, 20_000, 1_000), + ('64 KB', 64 * 1024, 4_000, 500), + ('1 MB', 1024 * 1024, 400, 50), +] +HIT_KEYS = int(os.environ.get('BENCH_HIT_KEYS', 100_000)) + + +def percentiles(values): + if not values: + return None + values = sorted(values) + n = len(values) + + def pct(p): + return values[min(n - 1, max(0, int(n * p) - 1))] + + return { + 'count': n, + 'p50': pct(0.50), + 'p90': pct(0.90), + 'p99': pct(0.99), + 'max': values[-1], + 'total_ns': sum(values), + } + + +def classic_ops(seed, ops=OPS): + """The 2016 operation sequence, one deterministic stream per seed.""" + rnd = random.Random(seed) + for count in range(ops): + key = str(rnd.randrange(RANGE)).encode() + value = str(count).encode() * rnd.randrange(1, 100) + choice = rnd.random() + yield count, key, value, ( + 'get' if choice < 0.9 else 'set' if choice < 0.99 else 'delete' + ) + + +def run_classic(store, seed, ops=OPS, warmup=WARMUP): + timings = {'get': [], 'set': [], 'delete': []} + misses = {'get': 0, 'set': 0, 'delete': 0} + perf = time.perf_counter_ns + for count, key, value, action in classic_ops(seed, ops): + if action == 'get': + start = perf() + result = store.get(key) + end = perf() + miss = result is None + elif action == 'set': + start = perf() + result = store.set(key, value) + end = perf() + miss = result is False + else: + start = perf() + result = store.delete(key) + end = perf() + miss = result is False + if count >= warmup: + timings[action].append(end - start) + if miss: + misses[action] += 1 + return timings, misses + + +def prepopulate(store): + for key in range(RANGE): + store.set(str(key).encode(), str(key).encode()) + + +def workload_classic(cls, workdir): + store = cls(workdir) + prepopulate(store) + t0 = time.perf_counter() + timings, misses = run_classic(store, seed=0) + elapsed = time.perf_counter() - t0 + footprint = store.footprint() + store.close() + return summarise(timings, misses, elapsed, footprint) + + +def summarise(timings, misses, elapsed, footprint, extra=None): + out = {'ops': {}, 'elapsed_s': elapsed, 'footprint_bytes': footprint} + total = 0 + for action, values in timings.items(): + if not values: + continue + stats = percentiles(values) + stats['misses'] = misses.get(action, 0) + out['ops'][action] = stats + total += len(values) + out['throughput_ops_s'] = total / elapsed if elapsed else None + if extra: + out.update(extra) + return out + + +def workload_sizes(cls, workdir): + store = cls(workdir) + rnd = random.Random(1) + result = {'tiers': {}} + perf = time.perf_counter_ns + t0 = time.perf_counter() + for label, size, ops, keys in SIZES: + payload = os.urandom(size) + key_list = [ + ('k%d-%s' % (i, label.replace(' ', ''))).encode() + for i in range(keys) + ] + set_t, get_t, misses = [], [], 0 + for i in range(ops): + key = key_list[i % keys] + start = perf() + store.set(key, payload) + set_t.append(perf() - start) + for i in range(ops): + key = key_list[rnd.randrange(keys)] + start = perf() + value = store.get(key) + get_t.append(perf() - start) + if value is None: + misses += 1 + result['tiers'][label] = { + 'size': size, + 'set': percentiles(set_t), + 'get': percentiles(get_t), + 'get_misses': misses, + } + store.clear() + elapsed = time.perf_counter() - t0 + result['elapsed_s'] = elapsed + result['footprint_bytes'] = store.footprint() + store.close() + return result + + +def workload_hit(cls, workdir, hit_ratio): + store = cls(workdir) + payload = os.urandom(256) + keys = [('h%d' % i).encode() for i in range(HIT_KEYS)] + # setup is not measured, so load the working set in bulk + store.set_many([(key, payload) for key in keys]) + rnd = random.Random(2) + perf = time.perf_counter_ns + get_t, misses = [], 0 + t0 = time.perf_counter() + for i in range(OPS): + if rnd.random() < hit_ratio: + key = keys[rnd.randrange(HIT_KEYS)] + else: + key = ('miss%d' % rnd.randrange(HIT_KEYS)).encode() + start = perf() + value = store.get(key) + get_t.append(perf() - start) + if value is None: + misses += 1 + elapsed = time.perf_counter() - t0 + footprint = store.footprint() + store.close() + return summarise( + {'get': get_t}, + {'get': misses}, + elapsed, + footprint, + {'hit_ratio_target': hit_ratio, 'working_set_keys': HIT_KEYS}, + ) + + +def _proc_worker(store_key, workdir, seed, queue): + cls = stores.STORES[store_key] + time.sleep(0.05) + store = cls(workdir) + timings, misses = run_classic(store, seed=seed) + store.close() + queue.put((timings, misses)) + + +def workload_procs(cls, workdir, nprocs, store_key): + store = cls(workdir) + prepopulate(store) + store.close() + ctx = multiprocessing.get_context('spawn') + queue = ctx.Queue() + procs = [ + ctx.Process( + target=_proc_worker, args=(store_key, workdir, seed, queue) + ) + for seed in range(nprocs) + ] + t0 = time.perf_counter() + for p in procs: + p.start() + results = [queue.get() for _ in procs] + for p in procs: + p.join() + elapsed = time.perf_counter() - t0 + timings = {'get': [], 'set': [], 'delete': []} + misses = {'get': 0, 'set': 0, 'delete': 0} + for t, m in results: + for action in timings: + timings[action].extend(t[action]) + misses[action] += m[action] + store = cls(workdir) + footprint = store.footprint() + store.close() + return summarise( + timings, misses, elapsed, footprint, {'processes': nprocs} + ) + + +def workload_threads(cls, workdir, nthreads=8): + store = cls(workdir) + prepopulate(store) + results = [None] * nthreads + errors = [] + + def worker(ident): + try: + handle = store.for_thread() + try: + results[ident] = run_classic(handle, seed=ident) + finally: + if handle is not store: + handle.close() + except BaseException as exc: # noqa: BLE001 + errors.append((ident, exc)) + + threads = [ + threading.Thread(target=worker, args=(i,)) for i in range(nthreads) + ] + t0 = time.perf_counter() + for t in threads: + t.start() + for t in threads: + t.join() + elapsed = time.perf_counter() - t0 + if errors: + ident, exc = errors[0] + raise RuntimeError('thread %d failed: %r' % (ident, exc)) from exc + timings = {'get': [], 'set': [], 'delete': []} + misses = {'get': 0, 'set': 0, 'delete': 0} + for t, m in results: + for action in timings: + timings[action].extend(t[action]) + misses[action] += m[action] + footprint = store.footprint() + store.close() + return summarise( + timings, misses, elapsed, footprint, {'threads': nthreads} + ) + + +WORKLOADS = { + 'classic': lambda cls, d, k: workload_classic(cls, d), + 'sizes': lambda cls, d, k: workload_sizes(cls, d), + 'hit95': lambda cls, d, k: workload_hit(cls, d, 0.95), + 'hit50': lambda cls, d, k: workload_hit(cls, d, 0.50), + 'procs4': lambda cls, d, k: workload_procs(cls, d, 4, k), + 'procs8': lambda cls, d, k: workload_procs(cls, d, 8, k), + 'threads8': lambda cls, d, k: workload_threads(cls, d, 8), +} + + +def environment(): + def cmd(*args): + try: + return subprocess.run( + args, capture_output=True, text=True, timeout=10 + ).stdout.strip() + except Exception: # noqa: BLE001 + return '' + + return { + 'date': datetime.date.today().isoformat(), + 'python': sys.version.split()[0], + 'implementation': platform.python_implementation(), + 'platform': platform.platform(), + 'machine': platform.machine(), + 'cpu': cmd('sysctl', '-n', 'machdep.cpu.brand_string') + or platform.processor(), + 'cores': os.cpu_count(), + 'sqlite': sqlite3.sqlite_version, + 'memcached': cmd('memcached', '-V'), + 'redis': cmd('redis-server', '--version'), + } + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument( + '--store', required=True, choices=sorted(stores.STORES) + ) + parser.add_argument('--workload', required=True, choices=sorted(WORKLOADS)) + parser.add_argument('--runs', type=int, default=5) + parser.add_argument('--warmup-runs', type=int, default=1) + parser.add_argument('--out', required=True) + args = parser.parse_args() + cls = stores.STORES[args.store] + workload = WORKLOADS[args.workload] + runs = [] + for i in range(args.warmup_runs + args.runs): + workdir = tempfile.mkdtemp(prefix='bench-%s-' % args.store) + if cls.server: + cls(workdir).clear() + try: + result = workload(cls, workdir, args.store) + finally: + shutil.rmtree(workdir, ignore_errors=True) + if i >= args.warmup_runs: + runs.append(result) + print('run', i, 'done', file=sys.stderr) + doc = { + 'store': args.store, + 'store_name': cls.name, + 'store_version': cls.version(), + 'workload': args.workload, + 'runs': runs, + 'environment': environment(), + 'ops': OPS, + 'warmup': WARMUP, + } + os.makedirs(os.path.dirname(os.path.abspath(args.out)), exist_ok=True) + with open(args.out, 'w') as writer: + json.dump(doc, writer, indent=1) + + +if __name__ == '__main__': + multiprocessing.freeze_support() + main() diff --git a/tests/benchmarks/stores.py b/tests/benchmarks/stores.py new file mode 100644 index 0000000..a53588c --- /dev/null +++ b/tests/benchmarks/stores.py @@ -0,0 +1,440 @@ +"""Store adapters with one small interface: get, set, delete, close, clear, +footprint, version. Keys and values are bytes. + +Each adapter wraps the library the way its own documentation shows, with +default settings unless the comment says otherwise. +""" + +import os +import sys + + +def diskcache_package(): + """Return the diskcache under test: the fork if installed, else upstream. + + The driver runs the same store in two interpreters, one holding + diskcache-community (imported as diskcache_community) and one holding + only diskcache 5.6.3 (imported as diskcache). + """ + try: + import diskcache_community as package + except ImportError: + import diskcache as package + return package + + +def chunks(items, size): + items = list(items) + for start in range(0, len(items), size): + yield items[start : start + size] + + +def tree_size(path): + total = 0 + for root, _dirs, files in os.walk(path): + for name in files: + try: + total += os.path.getsize(os.path.join(root, name)) + except OSError: + pass + return total + + +class Store: + name = 'store' + server = None # 'memcached' or 'redis' when a server process is needed + needs_fork_venv = False # True for the diskcache-community adapters + + def __init__(self, workdir): + self.workdir = workdir + + def get(self, key): + raise NotImplementedError + + def set(self, key, value): + raise NotImplementedError + + def set_many(self, items): + """Bulk load for unmeasured setup; the default is one set per item.""" + for key, value in items: + self.set(key, value) + + def delete(self, key): + raise NotImplementedError + + def close(self): + pass + + def clear(self): + raise NotImplementedError + + def footprint(self): + return tree_size(self.workdir) + + def for_thread(self): + """The handle a worker thread should use; the shared store by default. + + Clients that are not thread-safe return a fresh connection, which is + what their pooled variants hand out per thread. + """ + return self + + @classmethod + def version(cls): + return '' + + +# ------------------------------------------------------------ diskcache + + +class DiskCacheCache(Store): + name = 'diskcache.Cache' + + def __init__(self, workdir): + diskcache = diskcache_package() + + super().__init__(workdir) + self.cache = diskcache.Cache(workdir) + + def get(self, key): + return self.cache.get(key) + + def set(self, key, value): + return self.cache.set(key, value) + + def set_many(self, items): + with self.cache.transact(): + for key, value in items: + self.cache.set(key, value) + + def delete(self, key): + return self.cache.delete(key) + + def close(self): + self.cache.close() + + def clear(self): + self.cache.clear() + + @classmethod + def version(cls): + diskcache = diskcache_package() + + return '%s %s' % (diskcache.__title__, diskcache.__version__) + + +class DiskCacheFanout(DiskCacheCache): + name = 'diskcache.FanoutCache(shards=8, timeout=0.010)' + + def __init__(self, workdir): + diskcache = diskcache_package() + + Store.__init__(self, workdir) + self.cache = diskcache.FanoutCache(workdir, shards=8, timeout=0.010) + + +# ------------------------------------------------------------ memcached + + +class PyMemcache(Store): + name = 'memcached (pymemcache)' + server = 'memcached' + + def __init__(self, workdir): + import pymemcache.client.base + + super().__init__(workdir) + # wait for the server's reply on every command, as the other clients do + self.client = pymemcache.client.base.Client( + ('127.0.0.1', 11211), no_delay=True, default_noreply=False + ) + + def get(self, key): + return self.client.get(key) + + def set(self, key, value): + return self.client.set(key, value) + + def set_many(self, items): + for chunk in chunks(items, 1000): + self.client.set_many(dict(chunk), noreply=False) + + def for_thread(self): + # pymemcache.Client is not thread-safe; PooledClient would do the same + return PyMemcache(self.workdir) + + def delete(self, key): + return self.client.delete(key, noreply=False) + + def close(self): + self.client.close() + + def clear(self): + self.client.flush_all() + + def footprint(self): + return 0 + + @classmethod + def version(cls): + import pymemcache + + return 'pymemcache %s' % pymemcache.__version__ + + +class PyLibMC(Store): + name = 'memcached (pylibmc)' + server = 'memcached' + + def __init__(self, workdir): + import pylibmc + + super().__init__(workdir) + # the settings the 2016 benchmark used + self.client = pylibmc.Client( + ['127.0.0.1'], + binary=True, + behaviors={'tcp_nodelay': True, 'ketama': True}, + ) + + def get(self, key): + return self.client.get(key) + + def set(self, key, value): + return self.client.set(key, value) + + def set_many(self, items): + for chunk in chunks(items, 1000): + self.client.set_multi(dict(chunk)) + + def for_thread(self): + # pylibmc.Client is not thread-safe; ThreadMappedPool clones one per thread + return PyLibMC(self.workdir) + + def delete(self, key): + return self.client.delete(key) + + def close(self): + self.client.disconnect_all() + + def clear(self): + self.client.flush_all() + + def footprint(self): + return 0 + + @classmethod + def version(cls): + import pylibmc + + return 'pylibmc %s, libmemcached %s' % ( + pylibmc.__version__, + pylibmc.libmemcached_version, + ) + + +# ---------------------------------------------------------------- redis + + +class Redis(Store): + name = 'redis (redis-py)' + server = 'redis' + + def __init__(self, workdir): + import redis + + super().__init__(workdir) + self.client = redis.Redis(host='127.0.0.1', port=6379, db=1) + + def get(self, key): + return self.client.get(key) + + def set(self, key, value): + return self.client.set(key, value) + + def set_many(self, items): + for chunk in chunks(items, 10000): + self.client.mset(dict(chunk)) + + def delete(self, key): + return bool(self.client.delete(key)) + + def close(self): + self.client.close() + + def clear(self): + self.client.flushdb() + + def footprint(self): + return 0 + + @classmethod + def version(cls): + import redis + + hiredis = ( + 'with hiredis' + if redis.utils.HIREDIS_AVAILABLE + else 'without hiredis' + ) + return 'redis-py %s %s' % (redis.__version__, hiredis) + + +# ------------------------------------------------------ embedded stores + + +class SqliteDict(Store): + name = 'sqlitedict (autocommit)' + + def __init__(self, workdir): + import sqlitedict + + super().__init__(workdir) + os.makedirs(workdir, exist_ok=True) + self.db = sqlitedict.SqliteDict( + os.path.join(workdir, 'store.sqlite'), autocommit=True + ) + + def get(self, key): + # UserDict.get checks membership first, so a concurrent delete from + # another thread can surface as KeyError here + try: + return self.db[key.decode()] + except KeyError: + return None + + def set(self, key, value): + self.db[key.decode()] = value + return True + + def set_many(self, items): + self.db.update((key.decode(), value) for key, value in items) + + def delete(self, key): + try: + del self.db[key.decode()] + except KeyError: + return False + return True + + def close(self): + self.db.close() + + def clear(self): + self.db.clear() + + @classmethod + def version(cls): + import sqlitedict + + return 'sqlitedict %s' % sqlitedict.__version__ + + +class Shelve(Store): + name = 'shelve (dbm)' + + def __init__(self, workdir): + import shelve + + super().__init__(workdir) + os.makedirs(workdir, exist_ok=True) + self.db = shelve.open(os.path.join(workdir, 'shelf')) + + def get(self, key): + return self.db.get(key.decode()) + + def set(self, key, value): + self.db[key.decode()] = value + return True + + def delete(self, key): + try: + del self.db[key.decode()] + except KeyError: + return False + return True + + def close(self): + self.db.close() + + def clear(self): + self.db.clear() + + @classmethod + def version(cls): + import dbm + import shelve + import shutil + import tempfile + + # open a throwaway shelf to see which dbm module this Python picks + probe = tempfile.mkdtemp(prefix='bench-shelve-probe-') + try: + shelve.open(os.path.join(probe, 'shelf')).close() + backend = dbm.whichdb(os.path.join(probe, 'shelf')) or 'unknown' + finally: + shutil.rmtree(probe, ignore_errors=True) + return 'shelve over %s, Python %s' % (backend, sys.version.split()[0]) + + +class Lmdb(Store): + name = 'lmdb (py-lmdb)' + + def __init__(self, workdir): + import lmdb + + super().__init__(workdir) + os.makedirs(workdir, exist_ok=True) + self.env = lmdb.open(workdir, map_size=4 * 2**30) + + def get(self, key): + with self.env.begin() as txn: + return txn.get(key) + + def set(self, key, value): + with self.env.begin(write=True) as txn: + return txn.put(key, value) + + def set_many(self, items): + with self.env.begin(write=True) as txn: + for key, value in items: + txn.put(key, value) + + def delete(self, key): + with self.env.begin(write=True) as txn: + return txn.delete(key) + + def close(self): + self.env.close() + + def clear(self): + with self.env.begin(write=True) as txn: + txn.drop(self.env.open_db(), delete=False) + + def footprint(self): + # the data file is sparse up to map_size; count the used pages + info = self.env.info() + stat = self.env.stat() + return stat['psize'] * (info['last_pgno'] + 1) + + @classmethod + def version(cls): + import lmdb + + return 'py-lmdb %s, liblmdb %s' % ( + lmdb.__version__, + '.'.join(map(str, lmdb.version())), + ) + + +STORES = { + 'cache': DiskCacheCache, + 'fanout': DiskCacheFanout, + 'cache563': DiskCacheCache, # run under the venv holding diskcache 5.6.3 + 'pymemcache': PyMemcache, + 'pylibmc': PyLibMC, + 'redis': Redis, + 'sqlitedict': SqliteDict, + 'shelve': Shelve, + 'lmdb': Lmdb, +} diff --git a/tox.ini b/tox.ini index cdd093b..982e604 100644 --- a/tox.ini +++ b/tox.ini @@ -73,6 +73,7 @@ addopts= --ignore docs/case-study-web-crawler.rst --ignore docs/sf-python-2017-meetup-talk.rst --ignore tests/benchmark_core.py + --ignore tests/benchmarks --ignore tests/benchmark_djangocache.py --ignore tests/benchmark_glob.py --ignore tests/issue_85.py From 9eccc791dc75023b7d5914071d9f4a3722fa77fc Mon Sep 17 00:00:00 2001 From: August Vi Date: Sun, 18 Oct 2026 12:51:46 -0700 Subject: [PATCH 2/2] Open the benchmarks page with a short computed summary instead of the method notes, and keep only the store versions in the environment list. --- docs/benchmarks.rst | 60 +++--------- tests/benchmarks/report.py | 187 ++++++++++++++++++++++--------------- 2 files changed, 126 insertions(+), 121 deletions(-) diff --git a/docs/benchmarks.rst b/docs/benchmarks.rst index 3151629..7f267c6 100644 --- a/docs/benchmarks.rst +++ b/docs/benchmarks.rst @@ -1,35 +1,23 @@ DiskCache Benchmarks ==================== -Measured on 2026-10-12 with the published diskcache-community 5.7.0 wheel. -Every number below comes from the scripts in ``tests/benchmarks`` of the -repository; the last section says how to run them. Treat the results as what -they are: one machine, synthetic workloads, short keys. They show the shape of -the costs, not what your application will see. - -The previous pages, :doc:`cache-benchmarks` and :doc:`djangocache-benchmarks`, -are the old benchmarks: upstream DiskCache measured them for version 4 in -2019, on other hardware and against other versions of every store. They stay -for reference; the classic mix below repeats their workload. - -Each store runs the same seeded operation sequence. Latencies are measured per -operation with the nanosecond performance counter and reported at the 50th, -90th and 99th percentiles and the maximum. Every workload runs once to warm up -and then five times (three for the multi-process and threaded workloads); the -tables show the median run, and the "p50 across runs" column shows how much -the median moved between runs. memcached runs with its item size limit raised -to 4 MB for the 1 MB tier and Redis with persistence off; both are separate -processes on localhost, so their numbers include a round trip over the -loopback interface. Every other store uses its default settings, and the notes -under each section say where a default matters. +- **Reads:** 2.6 µs median in the classic mix, the fastest store there, 6 times + faster than memcached and 16 times faster than Redis on the same machine. +- **Large working sets:** lmdb reads faster over 100,000 keys (0.9 µs against + 3.2 µs at 95 % hits). +- **Writes:** 19.2 µs median, on par with memcached (16.0 µs to 18.5 µs) and 2 + times faster than Redis. +- **Against diskcache 5.6.3:** reads within 0.6 µs, writes 9 to 14 % faster, + 48 % faster for 1 MB values. +- **Concurrency:** FanoutCache(8) is the fastest setup across processes, + 291,478 operations per second from 8 processes; for 8 threads in one process + use a single Cache (188,028 against 19,715). +- **lmdb** writes take 5.98 ms because its defaults flush to disk on every + write. Environment ----------- -- CPU: Apple M5 Pro, 18 cores -- OS: macOS 27.0.1, arm64 -- Python: CPython 3.14.8 -- SQLite: 3.53.4 - memcached: memcached 1.6.45 - Redis: 8.10.2 - diskcache-community Cache: diskcache-community 5.7.0 @@ -393,15 +381,6 @@ one process. The ~1 % miss rate comes from gets after deletes. - 4 MB -lmdb's writes stand out because its defaults, ``sync`` and ``metasync`` on, -end every write transaction with two full disk flushes (``F_FULLFSYNC`` on -macOS), about 6 ms together on this machine; its reads are the fastest here. -It is the only store in the table that forces each write onto the disk before -returning. DiskCache runs SQLite in WAL mode with ``synchronous`` NORMAL, -which flushes at checkpoints, sqlitedict commits every operation through -SQLite's default rollback journal, and memcached and Redis keep data in -memory. - Value sizes ----------- @@ -831,19 +810,6 @@ set p50. - not run -The 8-thread column is dominated by SQLite's behaviour under threads on this -machine: a plain ``SELECT`` through Python's ``sqlite3`` module takes about -2 µs from one thread and about 370 µs from each of eight threads reading the -same database at once, 20,000 queries per second in total, and eight private -``Cache`` objects, one per thread, hit the same wall. The shared ``Cache`` -escapes it because its threads queue on one database's write lock, so fewer -of them are inside SQLite at any instant; ``FanoutCache(8)`` spreads writes -over eight databases, keeps all eight threads querying at once and pays the -full cost, in diskcache 5.6.3 exactly as in the fork. The same -``FanoutCache`` under 8 processes is the fastest DiskCache configuration on -the page. sqlitedict pays through the queue in front of its single -connection; lmdb and the memory stores have no such cliff. - Django backends --------------- diff --git a/tests/benchmarks/report.py b/tests/benchmarks/report.py index fdc62a9..e15da79 100644 --- a/tests/benchmarks/report.py +++ b/tests/benchmarks/report.py @@ -4,12 +4,12 @@ """ import argparse -import datetime import glob import json import os import re import statistics +import textwrap STORE_ORDER = [ 'cache', @@ -445,18 +445,6 @@ def fork_table(docs, title): ) -def measured_date(docs, results_dir): - """The date the results were measured: recorded by the runner, else the - modification date of the newest result file.""" - dates = [doc['environment'].get('date') for doc in docs.values()] - dates = [date for date in dates if date] - if dates: - return max(dates) - paths = glob.glob(os.path.join(results_dir, '*.json')) - newest = max(os.path.getmtime(path) for path in paths) - return datetime.date.fromtimestamp(newest).isoformat() - - # ---------------------------------------------------------------- charts @@ -637,6 +625,114 @@ def chart_django(docs, out_dir): # ------------------------------------------------------------------ page +def summary_section(docs): + """The conclusions at the top of the page, each computed from the results + and left out when its results are missing.""" + + def p50(workload, store, action, tier=None): + doc = docs.get((workload, store)) + return stat(doc, action, 'p50', tier) if doc else None + + def ops(workload, store): + doc = docs.get((workload, store)) + if not doc: + return None + return med([r['throughput_ops_s'] for r in doc['runs']]) + + lines = [] + get, put = p50('classic', 'cache', 'get'), p50('classic', 'cache', 'set') + memcached_get = [ + p50('classic', s, 'get') for s in ('pymemcache', 'pylibmc') + ] + memcached_set = [ + p50('classic', s, 'set') for s in ('pymemcache', 'pylibmc') + ] + redis_get = p50('classic', 'redis', 'get') + redis_set = p50('classic', 'redis', 'set') + lmdb_set = p50('classic', 'lmdb', 'set') + if get and None not in memcached_get and redis_get: + others = [p50('classic', s, 'get') for s in STORE_ORDER[3:]] + fastest = all(other is None or get <= other for other in others) + lines.append( + '- **Reads:** %s median in the classic mix%s, %.0f times faster' + ' than memcached and %.0f times faster than Redis on the same' + ' machine.' + % ( + fmt_ns(get), + ', the fastest store there' if fastest else '', + min(memcached_get) / get, + redis_get / get, + ) + ) + hit_cache, hit_lmdb = p50('hit95', 'cache', 'get'), p50( + 'hit95', 'lmdb', 'get' + ) + if hit_cache and hit_lmdb and hit_lmdb < hit_cache: + lines.append( + '- **Large working sets:** lmdb reads faster over 100,000 keys' + ' (%s against %s at 95 %% hits).' + % (fmt_ns(hit_lmdb), fmt_ns(hit_cache)) + ) + if put and None not in memcached_set and redis_set: + lines.append( + '- **Writes:** %s median, on par with memcached (%s to %s) and %.0f' + ' times faster than Redis.' + % ( + fmt_ns(put), + fmt_ns(min(memcached_set)), + fmt_ns(max(memcached_set)), + redis_set / put, + ) + ) + reads, writes = [], [] + for _label, workload, new, old, kind, action, tier in FORK_ROWS: + if kind != 'p50': + continue + values = ( + p50(workload, old, action, tier), + p50(workload, new, action, tier), + ) + if None in values: + continue + if action == 'get': + reads.append(abs(values[1] - values[0])) + elif workload != 'django': + writes.append(((values[0] - values[1]) / values[0] * 100, tier)) + if reads and writes: + large = [change for change, tier in writes if tier == '1 MB'] + small = [change for change, tier in writes if tier != '1 MB'] + text = ( + '- **Against diskcache 5.6.3:** reads within %s, writes %.0f to %.0f %%' + ' faster' % (fmt_ns(max(reads)), min(small), max(small)) + ) + if large: + text += ', %.0f %% faster for 1 MB values' % large[0] + lines.append(text + '.') + fanout8, cache_threads, fanout_threads = ( + ops('procs8', 'fanout'), + ops('threads8', 'cache'), + ops('threads8', 'fanout'), + ) + if fanout8 and cache_threads and fanout_threads: + lines.append( + '- **Concurrency:** FanoutCache(8) is the fastest setup across' + ' processes, {:,.0f} operations per second from 8 processes; for 8' + ' threads in one process use a single Cache ({:,.0f} against' + ' {:,.0f}).'.format(fanout8, cache_threads, fanout_threads) + ) + if lmdb_set: + lines.append( + '- **lmdb** writes take %s because its defaults flush to disk on' + ' every write.' % fmt_ns(lmdb_set) + ) + return '\n'.join( + textwrap.fill( + line.replace(' %', '\u00a0%'), 79, subsequent_indent=' ' + ) + for line in lines + ) + + def environment_section(docs): env = next(iter(docs.values()))['environment'] versions = [] @@ -650,17 +746,9 @@ def environment_section(docs): django = docs.get(('django', 'django-locmem')) if django: versions.append('- %s' % django['store_name'].replace(' locmem', '')) - platform = env['platform'] - match = re.match(r'^macOS-([\d.]+)-(\w+)', platform) - if match: - platform = 'macOS %s, %s' % match.groups() redis = re.search(r'v=([\d.]+)', env['redis'] or '') return '\n'.join( [ - '- CPU: %s, %s cores' % (env['cpu'], env['cores']), - '- OS: %s' % platform, - '- Python: %s %s' % (env['implementation'], env['python']), - '- SQLite: %s' % env['sqlite'], '- memcached: %s' % env['memcached'].splitlines()[0] if env['memcached'] else '- memcached: n/a', @@ -673,27 +761,7 @@ def environment_section(docs): PAGE = """DiskCache Benchmarks ==================== -Measured on {date} with the published diskcache-community {fork_version} wheel. -Every number below comes from the scripts in ``tests/benchmarks`` of the -repository; the last section says how to run them. Treat the results as what -they are: one machine, synthetic workloads, short keys. They show the shape of -the costs, not what your application will see. - -The previous pages, :doc:`cache-benchmarks` and :doc:`djangocache-benchmarks`, -are the old benchmarks: upstream DiskCache measured them for version 4 in -2019, on other hardware and against other versions of every store. They stay -for reference; the classic mix below repeats their workload. - -Each store runs the same seeded operation sequence. Latencies are measured per -operation with the nanosecond performance counter and reported at the 50th, -90th and 99th percentiles and the maximum. Every workload runs once to warm up -and then five times (three for the multi-process and threaded workloads); the -tables show the median run, and the "p50 across runs" column shows how much -the median moved between runs. memcached runs with its item size limit raised -to 4 MB for the 1 MB tier and Redis with persistence off; both are separate -processes on localhost, so their numbers include a round trip over the -loopback interface. Every other store uses its default settings, and the notes -under each section say where a default matters. +{summary} Environment ----------- @@ -724,15 +792,6 @@ def environment_section(docs): {classic_delete} {classic_summary} -lmdb's writes stand out because its defaults, ``sync`` and ``metasync`` on, -end every write transaction with two full disk flushes (``F_FULLFSYNC`` on -macOS), about 6 ms together on this machine; its reads are the fastest here. -It is the only store in the table that forces each write onto the disk before -returning. DiskCache runs SQLite in WAL mode with ``synchronous`` NORMAL, -which flushes at checkpoints, sqlitedict commits every operation through -SQLite's default rollback journal, and memcached and Redis keep data in -memory. - Value sizes ----------- @@ -774,19 +833,6 @@ def environment_section(docs): {concurrency} {throughput} -The 8-thread column is dominated by SQLite's behaviour under threads on this -machine: a plain ``SELECT`` through Python's ``sqlite3`` module takes about -2 µs from one thread and about 370 µs from each of eight threads reading the -same database at once, 20,000 queries per second in total, and eight private -``Cache`` objects, one per thread, hit the same wall. The shared ``Cache`` -escapes it because its threads queue on one database's write lock, so fewer -of them are inside SQLite at any instant; ``FanoutCache(8)`` spreads writes -over eight databases, keeps all eight threads querying at once and pays the -full cost, in diskcache 5.6.3 exactly as in the fork. The same -``FanoutCache`` under 8 processes is the fastest DiskCache configuration on -the page. sqlitedict pays through the queue in front of its single -connection; lmdb and the memory stores have no such cliff. - Django backends --------------- @@ -819,18 +865,14 @@ def environment_section(docs): """ -def build_page(docs, date): - fork = docs.get(('classic', 'cache')) - fork_version = fork['store_version'].split()[-1] if fork else '' - +def build_page(docs): def dj(action, title): return op_table( docs, 'django', action, title, DJANGO_ORDER, DJANGO_LABELS ) return PAGE.format( - date=date, - fork_version=fork_version, + summary=summary_section(docs), environment=environment_section(docs), fork=fork_table(docs, 'diskcache-community against diskcache 5.6.3'), classic_get=op_table( @@ -887,9 +929,6 @@ def main(): parser = argparse.ArgumentParser() parser.add_argument('--results', required=True) parser.add_argument('--docs', required=True) - parser.add_argument( - '--date', help='measurement date (default: from the results)' - ) args = parser.parse_args() docs = load(args.results) charts_dir = os.path.join(args.docs, '_static', 'benchmarks') @@ -899,7 +938,7 @@ def main(): chart_throughput(docs, charts_dir) if any(w == 'django' for w, _ in docs): chart_django(docs, charts_dir) - page = build_page(docs, args.date or measured_date(docs, args.results)) + page = build_page(docs) with open(os.path.join(args.docs, 'benchmarks.rst'), 'w') as writer: writer.write(page) print(