Benchmarks
Measured against Redis, on the same machine, from the records in the repository
Each run starts both servers on one host, drives them with the same load generator in alternating rounds, and stores the raw numbers in a JSON file next to the site. The charts below are drawn from those files when the site is built.
Two machines
Where each command lands on both machines
Each dot is one command: its ratio to Redis on Apple M4 Pro across, on AMD EPYC, 2 vCPU up. The shaded corner holds the commands that are faster than Redis on both. Dots near the faint diagonal behave the same on ARM and x86.
Reading it
The two machines are very different: a 12-core laptop chip with no pinning, and a 2 vCPU cloud guest with the server and the load generator pinned to separate CPUs. A command that stays top-right on both is fast because of how Ruvio runs it, not because of the hardware.
Sources: benchmark/records/mac-2026-10-14.json, benchmark/records/linux-hostinger-2026-10-14.json
Distribution
All 60 commands on one axis
The same results as a swarm: one dot per command, stacked where they crowd. Most of the mass sits right of the dashed Redis line on both machines.
Under load
Throughput and tail latency together
Each line joins Redis (hollow) to Ruvio (filled). In the first column further right is more requests per second; in the second, further left is a lower p99. Rows cover client counts, pipelining, fsync on every write, and more Ruvio shards.
No pipelining
Pipeline 16
fsync on every write (always)
Source: benchmark/records/x86-azure-2026-10-11.json · AMD EPYC 7763, 8 vCPU (Azure VM) · 2026-10-11, commit 5520164 · server and load generator share the VM, so compare the two servers with each other, not with other hosts. Un-pipelined rows are limited by the load generator.
Whole machine
8 shards against a Redis Cluster of 8 and a single Redis
One redis-benchmark per shard, pinned to its own core, sends each command to that shard's port with the keys inside a hash tag the shard owns, so nothing crosses shards. The same load goes to each node of a Redis Cluster on the same 8 cores, and to one Redis process on one core. Rates are summed over the shards.
| Command | Ruvio 8 shards | Redis Cluster 8 | One Redis | vs cluster | per core vs one Redis |
|---|---|---|---|---|---|
| DUMP (64 B string) | 6.97M | 2.92M | 372K | 2.39× | 2.34× |
| LINSERT (pivot missing, scans 100) | 7.05M | 4.67M | 593K | 1.51× | 1.49× |
| HGET (10-field hash) | 4.81M | 3.24M | 538K | 1.48× | 1.12× |
| LREM (value missing, scans 100) | 7.04M | 4.77M | 607K | 1.48× | 1.45× |
| COPY ... REPLACE (64 B string) | 9.20M | 6.31M | 877K | 1.46× | 1.31× |
| GEOSEARCH (radius 50 km, COUNT 10) | 151K | 105K | 13K | 1.44× | 1.44× |
| SETEX | 8.89M | 6.33M | 840K | 1.40× | 1.32× |
| HSET (10-field hash, existing field) | 4.32M | 3.14M | 500K | 1.37× | 1.08× |
| GEOADD | 8.14M | 5.96M | 772K | 1.37× | 1.32× |
| MSET (3 keys) | 8.14M | 5.97M | 787K | 1.36× | 1.29× |
| PUBLISH (no subscribers) | 2.98M | 2.19M | 630K | 1.36× | 0.59× |
| HDEL (10-field hash, missing field) | 4.35M | 3.28M | 549K | 1.33× | 0.99× |
| LMOVE (rotate a 100-element list) | 8.54M | 6.53M | 843K | 1.31× | 1.27× |
| LPOS (100 elements, hit in the middle) | 6.56M | 5.17M | 661K | 1.27× | 1.24× |
| HGETALL (10-field hash) | 3.12M | 2.48M | 314K | 1.26× | 1.24× |
| GETEX (set a 100 s deadline) | 8.91M | 7.14M | 952K | 1.25× | 1.17× |
| INCRBYFLOAT | 5.72M | 4.62M | 613K | 1.24× | 1.17× |
| EXPIRE (key with TTL) | 9.13M | 7.38M | 977K | 1.24× | 1.17× |
| ZADD (existing member, same score) | 9.07M | 7.82M | 1.04M | 1.16× | 1.09× |
| BITFIELD INCRBY u8 (always writes) | 8.26M | 7.20M | 958K | 1.15× | 1.08× |
| HSET (existing field) | 9.13M | 8.00M | 1.05M | 1.14× | 1.08× |
| LINDEX (20-item list) | 8.30M | 7.47M | 568K | 1.11× | 1.83× |
| LSET (20-item list) | 9.05M | 8.16M | 1.07M | 1.11× | 1.06× |
| MGET (3 keys) | 7.74M | 7.00M | 943K | 1.11× | 1.03× |
| BITFIELD GET u8 | 8.76M | 7.99M | 1.06M | 1.10× | 1.03× |
| SET | 8.78M | 8.06M | 1.07M | 1.09× | 1.02× |
| RENAME (same key) | 9.19M | 8.59M | 1.17M | 1.07× | 0.98× |
| SETRANGE | 9.20M | 8.60M | 1.14M | 1.07× | 1.01× |
| ZSCORE | 9.14M | 8.56M | 1.12M | 1.07× | 1.02× |
| SETBIT (1 KiB bitmap, same bit) | 9.29M | 8.76M | 1.16M | 1.06× | 1.00× |
| SADD (existing member) | 9.32M | 8.85M | 1.17M | 1.05× | 0.99× |
| HSTRLEN | 9.19M | 8.74M | 1.15M | 1.05× | 1.00× |
| SRANDMEMBER | 9.24M | 8.80M | 1.17M | 1.05× | 0.99× |
| HGET | 9.10M | 8.68M | 1.15M | 1.05× | 0.99× |
| HMGET (2 fields) | 8.31M | 7.95M | 1.03M | 1.05× | 1.00× |
| PFADD | 9.11M | 8.72M | 1.15M | 1.04× | 0.99× |
| GETRANGE | 9.07M | 8.69M | 1.15M | 1.04× | 0.99× |
| LSET (100 elements) | 7.84M | 7.54M | 968K | 1.04× | 1.01× |
| SISMEMBER | 9.19M | 8.90M | 1.19M | 1.03× | 0.97× |
| MEMORY USAGE | 9.28M | 9.00M | 1.19M | 1.03× | 0.97× |
| PFCOUNT | 9.29M | 9.02M | 1.17M | 1.03× | 1.00× |
| OBJECT ENCODING | 9.16M | 8.94M | 1.20M | 1.03× | 0.95× |
| LPUSH | 8.28M | 8.07M | 1.06M | 1.02× | 0.98× |
| GETBIT (1 KiB bitmap) | 9.29M | 9.14M | 1.17M | 1.02× | 1.00× |
| PERSIST (key without TTL) | 9.24M | 9.13M | 1.18M | 1.01× | 0.98× |
| BITCOUNT (1 KiB bitmap) | 8.99M | 8.89M | 1.18M | 1.01× | 0.95× |
| APPEND (1 byte) | 8.70M | 8.71M | 1.15M | 1.00× | 0.95× |
| ZINCRBY | 5.99M | 6.00M | 783K | 1.00× | 0.96× |
| TOUCH | 9.34M | 9.37M | 1.16M | 1.00× | 1.00× |
| TTL | 9.25M | 9.29M | 1.21M | 0.99× | 0.96× |
| LLEN | 9.25M | 9.32M | 1.18M | 0.99× | 0.98× |
| EXISTS | 9.25M | 9.32M | 1.17M | 0.99× | 0.99× |
| PTTL | 9.20M | 9.28M | 1.18M | 0.99× | 0.97× |
| TYPE | 9.03M | 9.16M | 1.16M | 0.99× | 0.97× |
| EXPIRETIME | 9.23M | 9.39M | 1.16M | 0.98× | 0.99× |
| LRANGE 0 2 | 7.89M | 8.02M | 1.03M | 0.98× | 0.95× |
| GET | 8.68M | 9.02M | 1.13M | 0.96× | 0.96× |
| DEL (missing key) | 8.89M | 9.31M | 1.18M | 0.96× | 0.94× |
| LRANGE 0 9 (20-item list) | 5.56M | 5.82M | 407K | 0.96× | 1.71× |
| FILLGET (value present) | 8.49M | 9.29M | 1.15M | 0.91× | 0.92× |
| Data shape (8 copies on the 8-shard systems) | Ruvio 8 shards | Redis Cluster 8 | One Redis, one copy | per shard vs one Redis |
|---|---|---|---|---|
| 20,000 lists of 20 elements | 46.6 MiB | 42.3 MiB | 5.2 MiB | 1.13× |
| 5,000 HyperLogLogs of 100 elements | 15.9 MiB | 14.3 MiB | 2.0 MiB | 1.01× |
| 20,000 bitmaps of 128 B (SETBIT) | 31.4 MiB | 27.4 MiB | 3.4 MiB | 1.14× |
| 100,000 BITFIELD counters (u16) | 27.6 MiB | 41.6 MiB | 5.1 MiB | 0.67× |
| 100,000 geo points in one key | 47.8 MiB | 47.7 MiB | 5.7 MiB | 1.04× |
| 100,000 strings of 16 B | 45.0 MiB | 54.0 MiB | 6.7 MiB | 0.84× |
| 100,000 strings of 128 B | 139.3 MiB | 157.6 MiB | 19.6 MiB | 0.89× |
| 100,000 strings of 16 B with TTL | 79.2 MiB | 77.6 MiB | 9.9 MiB | 1.00× |
| 100,000 one-element lists | 42.2 MiB | 53.4 MiB | 6.7 MiB | 0.79× |
| 50,000 small hashes (3 fields) | 27.1 MiB | 28.3 MiB | 3.2 MiB | 1.07× |
| 50,000 small sets (4 members) | 24.6 MiB | 25.3 MiB | 3.0 MiB | 1.01× |
| 50,000 small sorted sets (3 members) | 23.7 MiB | 24.6 MiB | 2.8 MiB | 1.07× |
Sources: benchmark/records/x86-azure-2026-10-16-8-shards-final.json · benchmark/records/x86-azure-2026-10-16-8-shards-p64-after.json · benchmark/records/x86-azure-2026-10-16-8-shards-everysec-after.json · Azure F16as_v6, AMD EPYC 9V74 (virtualized), 16 cores without SMT; servers on cpus 8-15, load generators on cpus 0-7, loopback · 2026-10-16, Ruvio per-shard forwarding branch at 7376885: lean dispatch in per-shard mode, non-blocking PUBLISH, cycle-counter SLOWLOG timing, six more commands in the WAL group, no record for a no-op SADD · the record with the one-shard control run and the profiles is in the docs.
Every row
Full results
Median requests per second over the rounds, Ruvio and Redis side by side. The whisker spans the worst and best pairing of rounds, so a wide whisker means a noisy row.
| Command | Ruvio ops/s | Redis ops/s | 0.5×1×2.5× | Ratio |
|---|---|---|---|---|
| GEOSEARCH (radius 50 km, COUNT 10) | 44K | 18K | 2.48× | |
| SETEX | 2.50M | 1.25M | 2.00× | |
| MSET (3 keys) | 2.20M | 1.10M | 2.00× | |
| GEOADD | 1.94M | 1.11M | 1.74× | |
| GETEX (set a 100 s deadline) | 2.37M | 1.38M | 1.71× | |
| LINSERT (pivot missing, scans 100) | 1.29M | 841K | 1.53× | |
| COPY ... REPLACE (64 B string) | 2.09M | 1.38M | 1.51× | |
| LMOVE (rotate a 100-element list) | 2.05M | 1.36M | 1.50× | |
| SET | 2.81M | 1.88M | 1.50× | |
| LREM (value missing, scans 100) | 1.30M | 874K | 1.49× | |
| LPOS (100 elements, hit in the middle) | 1.58M | 1.07M | 1.47× | |
| ZADD (existing member, same score) | 2.14M | 1.53M | 1.40× | |
| SETBIT (1 KiB bitmap, same bit) | 2.57M | 1.84M | 1.40× | |
| GETRANGE | 2.57M | 1.88M | 1.37× | |
| BITFIELD GET u8 | 2.20M | 1.61M | 1.37× | |
| HSET (existing field) | 2.50M | 1.84M | 1.36× | |
| EXPIRE (key with TTL) | 2.31M | 1.70M | 1.36× | |
| BITFIELD INCRBY u8 (always writes) | 1.88M | 1.38M | 1.35× | |
| HGET | 2.57M | 1.91M | 1.34× | |
| HGET (10-field hash) | 2.31M | 1.73M | 1.33× | |
| LSET (20-item list) | 2.20M | 1.67M | 1.32× | |
| OBJECT ENCODING | 2.73M | 2.09M | 1.30× | |
| HSET (10-field hash, existing field) | 1.91M | 1.48M | 1.30× | |
| HSTRLEN | 2.50M | 1.96M | 1.28× | |
| RENAME (same key) | 2.50M | 1.96M | 1.28× | |
| SETRANGE | 2.43M | 1.91M | 1.27× | |
| ZSCORE | 2.25M | 1.80M | 1.25× | |
| DUMP (64 B string) | 1.96M | 1.61M | 1.22× | |
| SRANDMEMBER | 2.43M | 2.00M | 1.22× | |
| MGET (3 keys) | 1.84M | 1.53M | 1.20× | |
| HMGET (2 fields) | 2.00M | 1.67M | 1.20× | |
| PFADD | 2.43M | 2.05M | 1.19× | |
| LPUSH | 2.09M | 1.76M | 1.19× | |
| HDEL (10-field hash, missing field) | 1.96M | 1.67M | 1.17× | |
| PERSIST (key without TTL) | 2.50M | 2.14M | 1.17× | |
| MEMORY USAGE | 2.43M | 2.09M | 1.16× | |
| APPEND (1 byte) | 2.43M | 2.09M | 1.16× | |
| LSET (100 elements) | 1.88M | 1.64M | 1.15× | |
| PUBLISH (no subscribers) | 2.57M | 2.25M | 1.14× | |
| ZINCRBY | 1.58M | 1.38M | 1.14× | |
| EXISTS | 2.50M | 2.20M | 1.14× | |
| SADD (existing member) | 2.37M | 2.09M | 1.13× | |
| LRANGE 0 2 | 1.88M | 1.67M | 1.13× | |
| INCRBYFLOAT | 1.34M | 1.20M | 1.12× | |
| LINDEX (20-item list) | 2.14M | 1.91M | 1.12× | |
| SISMEMBER | 2.50M | 2.25M | 1.11× | |
| TTL | 2.37M | 2.14M | 1.11× | |
| GETBIT (1 KiB bitmap) | 2.25M | 2.09M | 1.08× | |
| EXPIRETIME | 2.50M | 2.37M | 1.06× | |
| PFCOUNT | 2.43M | 2.31M | 1.05× | |
| DEL (missing key) | 2.37M | 2.25M | 1.05× | |
| PTTL | 2.25M | 2.14M | 1.05× | |
| TYPE | 2.37M | 2.31M | 1.03× | |
| LLEN | 2.31M | 2.31M | 1.00× | |
| LRANGE 0 9 (20-item list) | 1.18M | 1.18M | 1.00× | |
| GET | 2.14M | 2.20M | 0.98× | |
| BITCOUNT (1 KiB bitmap) | 2.25M | 2.31M | 0.98× | |
| HGETALL (10-field hash) | 763K | 789K | 0.97× | |
| TOUCH | 2.31M | 2.50M | 0.92× | |
| FILLGET (value present) | 1.96M | 2.31M | 0.85× |
Source: benchmark/records/mac-2026-10-14.json · 2026-10-14, commit eb1281c
| Command | Ruvio ops/s | Redis ops/s | 0.5×1×2.5× | Ratio |
|---|---|---|---|---|
| DUMP (64 B string) | 726K | 338K | 2.15× | |
| LINSERT (pivot missing, scans 100) | 789K | 421K | 1.88× | |
| LREM (value missing, scans 100) | 789K | 474K | 1.67× | |
| LPOS (100 elements, hit in the middle) | 643K | 457K | 1.41× | |
| GEOSEARCH (radius 50 km, COUNT 10) | 15K | 11K | 1.40× | |
| GEOADD | 789K | 577K | 1.37× | |
| COPY ... REPLACE (64 B string) | 849K | 625K | 1.36× | |
| EXPIRE (key with TTL) | 769K | 596K | 1.29× | |
| APPEND (1 byte) | 588K | 471K | 1.25× | |
| SET | 874K | 703K | 1.24× | |
| SETEX | 841K | 682K | 1.23× | |
| LMOVE (rotate a 100-element list) | 763K | 625K | 1.22× | |
| ZINCRBY | 672K | 577K | 1.16× | |
| SISMEMBER | 818K | 703K | 1.16× | |
| MSET (3 keys) | 726K | 625K | 1.16× | |
| BITFIELD INCRBY u8 (always writes) | 796K | 709K | 1.12× | |
| HGET (10-field hash) | 796K | 709K | 1.12× | |
| HSET (existing field) | 833K | 756K | 1.10× | |
| RENAME (same key) | 818K | 744K | 1.10× | |
| ZADD (existing member, same score) | 857K | 783K | 1.10× | |
| LPUSH | 657K | 600K | 1.09× | |
| HSTRLEN | 818K | 756K | 1.08× | |
| MGET (3 keys) | 692K | 643K | 1.08× | |
| PERSIST (key without TTL) | 738K | 687K | 1.07× | |
| GETRANGE | 811K | 756K | 1.07× | |
| SETRANGE | 865K | 811K | 1.07× | |
| HGET | 720K | 677K | 1.06× | |
| INCRBYFLOAT | 492K | 464K | 1.06× | |
| HSET (10-field hash, existing field) | 709K | 672K | 1.06× | |
| LRANGE 0 9 (20-item list) | 517K | 492K | 1.05× | |
| HMGET (2 fields) | 647K | 616K | 1.05× | |
| LINDEX (20-item list) | 865K | 833K | 1.04× | |
| SADD (existing member) | 714K | 692K | 1.03× | |
| ZSCORE | 818K | 796K | 1.03× | |
| LSET (20-item list) | 811K | 789K | 1.03× | |
| LLEN | 687K | 672K | 1.02× | |
| GET | 841K | 826K | 1.02× | |
| EXPIRETIME | 833K | 818K | 1.02× | |
| HDEL (10-field hash, missing field) | 662K | 657K | 1.01× | |
| BITCOUNT (1 KiB bitmap) | 732K | 732K | 1.00× | |
| OBJECT ENCODING | 738K | 738K | 1.00× | |
| PTTL | 826K | 826K | 1.00× | |
| SRANDMEMBER | 818K | 818K | 1.00× | |
| LRANGE 0 2 | 634K | 638K | 0.99× | |
| LSET (100 elements) | 687K | 692K | 0.99× | |
| TYPE | 709K | 714K | 0.99× | |
| PFCOUNT | 826K | 841K | 0.98× | |
| BITFIELD GET u8 | 726K | 744K | 0.98× | |
| SETBIT (1 KiB bitmap, same bit) | 826K | 849K | 0.97× | |
| GETEX (set a 100 s deadline) | 634K | 657K | 0.96× | |
| PUBLISH (no subscribers) | 738K | 769K | 0.96× | |
| MEMORY USAGE | 709K | 744K | 0.95× | |
| PFADD | 703K | 744K | 0.95× | |
| TTL | 750K | 796K | 0.94× | |
| FILLGET (value present) | 672K | 720K | 0.93× | |
| EXISTS | 738K | 804K | 0.92× | |
| DEL (missing key) | 756K | 826K | 0.92× | |
| GETBIT (1 KiB bitmap) | 763K | 849K | 0.90× | |
| TOUCH | 709K | 796K | 0.89× | |
| HGETALL (10-field hash) | 398K | 457K | 0.87× |
Source: benchmark/records/linux-hostinger-2026-10-14.json · 2026-10-14, commit eb1281c
Memory
Memory added per data shape
Resident memory after loading minus resident memory at idle, for each server. Left is less, on a log axis.
12 of 12 shapes below Redis · total 67.6 MiB against 91.6 MiB · benchmark/records/mac-2026-10-14.json
11 of 12 shapes below Redis · total 67.0 MiB against 82.8 MiB · benchmark/records/linux-hostinger-2026-10-14.json
Method
How the numbers are made
- Apple M4 Pro
- Machine
- Apple M4 Pro, 12 cores, 24 GiB, macOS 26.6.2 arm64
- Redis
- 8.10.2
- Load
- 50 clients, pipeline 16, 3 alternating rounds, median
- Ruvio
- memory profile, 1 shard, release build, commit eb1281c
- Pinning
- none
- AMD EPYC, 2 vCPU
- Machine
- AMD EPYC 9354P 32-Core Processor (KVM guest), 2 vCPU, Linux 6.8.0-146-generic x86_64
- Redis
- 8.10.2
- Load
- 50 clients, pipeline 16, 3 alternating rounds, median
- Ruvio
- memory profile, 1 shard, release build, commit eb1281c
- Pinning
- server cpu 0, client cpu 1
The whole-machine grids come from scripts/shard_compare.py: 8 shards in one Ruvio process (port_mode = "per-shard"), a Redis Cluster of 8 masters created with redis-cli --cluster create, and one Redis process, each driven by one redis-benchmark per shard with hash-tagged keys. The 8-shard systems hold 8 copies of each data shape, so a shard holds what the single process holds.
The single-machine rows come from scripts/redis_compare_features.py, which runs redis-benchmark against each server in turn. Absolute numbers describe one host; the ratio between the two servers is the result. Shared cloud machines vary from run to run, so a row within a few percent of 1× is a tie.