Pulsify / Benchmarks Recorded November 4, 2026

Performance,
in full.

Creation, edits, synchronization and storage. Explore the complete recorded results for Pulsify, json-joy, Yjs and Loro, with every win and every trade-off in view.

Real workloads. Shared fixtures. Open methodology.

Explore the results
Best WorstCompared within each row · ties share a rank · — unmeasured

Timings and payloads: lower is better. Throughput: higher is better. Unmeasured or interrupted cases are never treated as zero.

01 / CRDT engines

The complete comparison

Report & methodology

The same editable document in every engine: metadata, an atomic string, a nested map and a list of editable objects. All 18 primary scenarios, including the less flattering ones.

Apple M4 · bun-1.4.4 · 5 warmups + 25 measured samples · 2026-11-04

Revision 3bfc576 · clean source Source data

Operation time µs/op · Lower is better
ScenarioPulsifyNative hybrid v3json-joy18.28.0Yjs V213.6.33Loro1.17.1 · Node WASMYjs V113.6.33
Create document10 editable items11.01Bestp95 17.9312.43p95 17.1815.60p95 23.3557.29Worstp95 103.46—Not measured
Create + encode10 editable items13.49p95 17.2312.70Bestp95 20.3220.16p95 24.20103.15Worstp95 147.9216.70p95 24.00
Create document1,000 editable items263.02Bestp95 338.15450.12p95 529.10323.90p95 348.653,073.38Worstp95 3,231.48—Not measured
Create + encode1,000 editable items318.83Bestp95 361.73718.06p95 763.02505.60p95 589.354,741.62Worstp95 5,072.54543.52p95 608.44
First export1,000 editable items76.04Bestp95 137.54293.67p95 342.88210.13p95 312.041,711.96Worstp95 1,857.42265.58p95 373.46
Repeated export1,000 editable items23.87Bestp95 44.21267.42Worstp95 304.33191.25p95 269.0876.79p95 101.58237.58p95 322.54
Read cached field10 editable items0.0188p95 0.02750.0077Bestp95 0.00890.0131p95 0.01440.12Worstp95 0.13—Not measured
Commit scalar edit0 prior edits0.29Bestp95 0.340.39p95 0.451.17p95 1.431.34Worstp95 1.49—Not measured
Commit scalar edit1,000 prior edits0.19Bestp95 0.240.30p95 0.350.94p95 1.011.25Worstp95 1.32—Not measured
Commit scalar edit5,000 prior edits0.16Bestp95 0.210.30p95 0.350.93p95 1.061.26Worstp95 1.35—Not measured
Append editable object10 editable items1.24p95 1.821.06Bestp95 1.351.59p95 2.024.31Worstp95 4.60—Not measured
Edit + encode10 editable items1.64Bestp95 2.392.70p95 3.514.85p95 5.308.39Worstp95 10.312.26p95 2.64
Incremental round trip10 editable items3.45Bestp95 4.044.53p95 5.587.73p95 8.5215.90Worstp95 17.703.95p95 4.83
Incremental round trip1,000 editable items12.42Bestp95 15.75154.69Worstp95 169.1926.67p95 30.6347.15p95 66.7021.85p95 30.44
Load + materialize10 editable items13.35Bestp95 14.8313.93p95 16.0215.55p95 16.8846.24Worstp95 49.0715.97p95 17.18
Load + materialize1,000 editable items367.44Bestp95 418.94659.94p95 730.13547.02p95 592.101,624.77Worstp95 1,791.50635.10p95 709.71
Two-peer offline merge100 offline edits per peer146.00p95 203.5490.54p95 110.7178.63p95 109.04239.29Worstp95 273.8362.50Bestp95 81.37
Concurrent map conflict16 concurrent peers62.54Bestp95 85.2963.37p95 99.3799.87p95 122.71392.75Worstp95 461.2176.46p95 121.58

Medians of per-sample averages; p95 appears below each median. A dash means that variant was not measured. Small differences, especially below 1 µs, can be JIT or scheduling noise.

Transferred payloads

Bytes per measured operation

CPU speed and network cost are different trade-offs. Offline merge counts both deltas; the 16-peer conflict counts the updates imported by one receiver. Network framing is excluded.

Transferred payload size bytes/op · Lower is better
ScenarioPulsifyNative hybrid v3json-joy18.28.0Yjs V213.6.33Loro1.17.1 · Node WASMYjs V113.6.33
Create + encode10 editable items284.00Best439.00449.001,030.00Worst593.00
Create + encode1,000 editable items17,027.00Best36,766.0030,012.0049,733.00Worst47,879.00
First export1,000 editable items17,027.00Best36,766.0030,012.0049,733.00Worst47,879.00
Repeated export1,000 editable items17,027.00Best36,766.0030,012.0049,733.00Worst47,879.00
Edit + encode10 editable items104.00Worst58.7933.4486.5723.54Best
Incremental round trip10 editable items104.00Worst63.8832.9486.0022.96Best
Incremental round trip1,000 editable items104.00Worst70.6034.2088.0025.20Best
Load + materialize10 editable items284.00Best439.00449.001,030.00Worst593.00
Load + materialize1,000 editable items17,027.00Best36,766.0030,012.0049,733.00Worst47,879.00
Two-peer offline merge100 offline edits per peer176.00139.0097.001,624.00Worst75.00Best
Concurrent map conflict16 concurrent peers1,728.00Worst1,024.00512.001,536.00336.00Best

The engines retain different histories. Pulsify retains exact rows at the producer and can send condensed scalar state; json-joy uses structural snapshots and deltas, Yjs uses default GC, and Loro uses history-bearing snapshots and updates.

What does the public API cost?

Same commit and journal path

Native json-joy, Pulsify’s direct setter and Pulsify’s public proxy, measured separately. Local writes do not encode binary data; tests verify identical exported bytes for the two Pulsify paths.

Public API overhead µs/op · Lower is better
Scenariojson-joy18.28.0Pulsify directCoreModel.setPulsify publicPublic proxy
Read cached fieldCached nested field0.008p95 0.00890.0074Bestp95 0.00830.0216Worstp95 0.0262
Commit scalar edit0 prior edits0.31Worstp95 0.350.24Bestp95 0.360.26p95 0.33
Commit scalar edit1,000 prior edits0.29Worstp95 0.340.18p95 0.220.18Bestp95 0.24
Commit scalar edit5,000 prior edits0.28Worstp95 0.310.15Bestp95 0.180.16p95 0.18

Public minus direct includes setter checks and conversions as well as proxy traps. Small or negative differences between runs can reflect measurement noise.

All 95 measurements Iterations, median, p95, min, max, ops/s and payloads

Timing columns show µs/op. The p95 is a percentile of sample averages, not individual-event tail latency. Highlights compare participants in the same scenario and metric.

Complete timing measurements
Scenario / participantIterationsmedianµs/opp95 avg.µs/opminµs/opmaxµs/opOps/sBytes/op
create/10Pulsify2011.0117.937.4518.8390,823—
create/10json-joy2012.4317.189.6320.3780,429—
create/10Yjs V22015.6023.3510.3526.9164,120—
create/10Loro2057.29103.4648.54136.1417,454—
create+encode/10Pulsify2013.4917.2310.7125.4074,143284.00
create+encode/10json-joy2012.7020.3210.8020.4678,766439.00
create+encode/10Yjs V22020.1624.2015.4025.7749,592449.00
create+encode/10Loro20103.15147.9288.38157.179,6941,030.00
create+encode/10Yjs V12016.7024.0012.8326.3259,873593.00
create/1000Pulsify2263.02338.15235.21344.173,802—
create/1000json-joy2450.12529.10432.23606.062,222—
create/1000Yjs V22323.90348.65303.83358.043,087—
create/1000Loro23,073.383,231.482,987.173,518.46325—
create+encode/1000Pulsify2318.83361.73292.08392.103,13617,027.00
create+encode/1000json-joy2718.06763.02691.73778.021,39336,766.00
create+encode/1000Yjs V22505.60589.35471.69608.961,97830,012.00
create+encode/1000Loro24,741.625,072.544,588.085,164.5421149,733.00
create+encode/1000Yjs V12543.52608.44516.83608.831,84047,879.00
export-first/1000Pulsify176.04137.5461.58181.0013,15117,027.00
export-first/1000json-joy1293.67342.88265.21348.383,40536,766.00
export-first/1000Yjs V21210.13312.04185.83312.584,75930,012.00
export-first/1000Loro11,711.961,857.421,658.631,909.4258449,733.00
export-first/1000Yjs V11265.58373.46229.17390.753,76547,879.00
export-repeat/1000Pulsify123.8744.2117.4247.4241,88517,027.00
export-repeat/1000json-joy1267.42304.33234.12408.173,73936,766.00
export-repeat/1000Yjs V21191.25269.08167.17278.215,22930,012.00
export-repeat/1000Loro176.79101.5869.42112.9613,02249,733.00
export-repeat/1000Yjs V11237.58322.54211.58323.674,20947,879.00
read-count/10Pulsify10,0000.01880.02750.01620.051453,074,049—
read-count/10json-joy10,0000.00770.00890.0050.009129,520,257—
read-count/10Yjs V210,0000.01310.01440.01180.014676,603,698—
read-count/10Loro10,0000.120.130.120.138,045,052—
mutate/history-0Pulsify2000.290.340.250.383,501,094—
mutate/history-0json-joy2000.390.450.350.502,564,103—
mutate/history-0Yjs V22001.171.431.061.55853,333—
mutate/history-0Loro2001.341.491.262.53747,896—
mutate/history-1000Pulsify2000.190.240.130.255,351,171—
mutate/history-1000json-joy2000.300.350.250.363,377,865—
mutate/history-1000Yjs V22000.941.010.861.071,067,618—
mutate/history-1000Loro2001.251.321.211.33802,810—
mutate/history-5000Pulsify2000.160.210.140.286,130,268—
mutate/history-5000json-joy2000.300.350.250.423,337,951—
mutate/history-5000Yjs V22000.931.060.861.071,071,191—
mutate/history-5000Loro2001.261.351.201.60792,603—
list-append/10Pulsify1001.241.820.971.86807,539—
list-append/10json-joy1001.061.350.941.48945,626—
list-append/10Yjs V21001.592.021.412.05628,105—
list-append/10Loro1004.314.604.184.90231,951—
edit+encode/10Pulsify1001.642.391.302.60610,687104.00
edit+encode/10json-joy1002.703.512.513.62370,77158.79
edit+encode/10Yjs V21004.855.304.495.64206,27433.44
edit+encode/10Loro1008.3910.317.1313.85119,16086.57
edit+encode/10Yjs V11002.262.642.042.78442,23323.54
delta-roundtrip/10Pulsify503.454.043.094.15289,926104.00
delta-roundtrip/10json-joy504.535.584.065.66220,54763.88
delta-roundtrip/10Yjs V2507.738.527.119.20129,39432.94
delta-roundtrip/10Loro5015.9017.7014.7924.4962,89386.00
delta-roundtrip/10Yjs V1503.954.833.484.83253,11122.96
delta-roundtrip/1000Pulsify512.4215.7510.1516.8680,483104.00
delta-roundtrip/1000json-joy5154.69169.19145.37175.376,46470.60
delta-roundtrip/1000Yjs V2526.6730.6323.7931.1937,48834.20
delta-roundtrip/1000Loro547.1566.7039.6776.8021,20988.00
delta-roundtrip/1000Yjs V1521.8530.4419.6231.2245,76725.20
initial-load+materialize/10Pulsify1013.3514.8310.7014.8674,929284.00
initial-load+materialize/10json-joy1013.9316.0211.2917.3271,813439.00
initial-load+materialize/10Yjs V21015.5516.8814.1318.7764,326449.00
initial-load+materialize/10Loro1046.2449.0742.1149.2921,6261,030.00
initial-load+materialize/10Yjs V11015.9717.1814.4717.6362,598593.00
initial-load+materialize/1000Pulsify2367.44418.94348.98430.482,72217,027.00
initial-load+materialize/1000json-joy2659.94730.13635.13756.311,51536,766.00
initial-load+materialize/1000Yjs V22547.02592.10520.25637.271,82830,012.00
initial-load+materialize/1000Loro21,624.771,791.501,585.461,950.3561549,733.00
initial-load+materialize/1000Yjs V12635.10709.71619.10728.351,57547,879.00
offline-merge/100-per-peerPulsify1146.00203.54104.08222.376,849176.00
offline-merge/100-per-peerjson-joy190.54110.7167.75112.0811,045139.00
offline-merge/100-per-peerYjs V2178.63109.0462.58112.6712,71997.00
offline-merge/100-per-peerLoro1239.29273.83217.42318.334,1791,624.00
offline-merge/100-per-peerYjs V1162.5081.3749.62128.8316,00075.00
upstream-map-conflict/16-peersPulsify162.5485.2941.5898.6315,9891,728.00
upstream-map-conflict/16-peersjson-joy163.3799.3748.21129.1215,7791,024.00
upstream-map-conflict/16-peersYjs V2199.87122.7175.08125.7110,013512.00
upstream-map-conflict/16-peersLoro1392.75461.21368.13480.212,5461,536.00
upstream-map-conflict/16-peersYjs V1176.46121.5865.29127.1213,079336.00
proxy-overhead/read-countjson-joy10,0000.0080.00890.00780.0091125,131,388—
proxy-overhead/read-countPulsify direct10,0000.00740.00830.00720.0083135,060,304—
proxy-overhead/read-countPulsify public10,0000.02160.02620.0210.027246,198,339—
proxy-overhead/mutate-history-0json-joy2000.310.350.290.413,265,306—
proxy-overhead/mutate-history-0Pulsify direct2000.240.360.220.404,181,214—
proxy-overhead/mutate-history-0Pulsify public2000.260.330.230.343,905,563—
proxy-overhead/mutate-history-1000json-joy2000.290.340.270.353,431,003—
proxy-overhead/mutate-history-1000Pulsify direct2000.180.220.130.315,485,614—
proxy-overhead/mutate-history-1000Pulsify public2000.180.240.150.265,633,803—
proxy-overhead/mutate-history-5000json-joy2000.280.310.260.313,617,160—
proxy-overhead/mutate-history-5000Pulsify direct2000.150.180.140.186,629,761—
proxy-overhead/mutate-history-5000Pulsify public2000.160.180.140.196,434,385—
What each scenario measures Timed work and exclusions
create/10
Create an editable 10-item graph; cleanup is outside timing.
create+encode/10
Create an editable 10-item graph and export a transferable initial state; cleanup is outside timing.
create/1000
Create an editable 1000-item graph; cleanup is outside timing.
create+encode/1000
Create an editable 1000-item graph and export a transferable initial state; cleanup is outside timing.
export-first/1000
Export a prepared 1000-item document for the first time; construction, import verification and cleanup are outside timing.
export-repeat/1000
Export a prepared 1000-item document after one untimed export, without any edit; construction, import verification and cleanup are outside timing.
read-count/10
Read a cached editable nested count; initial container lookup is outside timing.
mutate/history-0
One committed count update through each library's default local API; Pulsify retains its native patch without encoding. Encoding is measured separately in edit+encode.
mutate/history-1000
One committed count update through each library's default local API; Pulsify retains its native patch without encoding. Encoding is measured separately in edit+encode.
mutate/history-5000
One committed count update through each library's default local API; Pulsify retains its native patch without encoding. Encoding is measured separately in edit+encode.
list-append/10
Append a new editable two-field object to the list and commit once per append (adapted from B1.8; objects replace upstream's numbers).
edit+encode/10
Capture a version, commit one count edit, encode its incremental update; verify all encoded packets on a peer outside timing.
delta-roundtrip/10
Version-vector capture + edit + delta encoding + peer import + peer count read; synchronous CPU-only, without transport or acknowledgement.
delta-roundtrip/1000
Version-vector capture + edit + delta encoding + peer import + peer count read; synchronous CPU-only, without transport or acknowledgement.
initial-load+materialize/10
Construct an empty replica, import an initial state and materialize a detached JSON view. Export is prepared outside timing; cleanup is outside timing.
initial-load+materialize/1000
Construct an empty replica, import an initial state and materialize a detached JSON view. Export is prepared outside timing; cleanup is outside timing.
offline-merge/100-per-peer
Two initialized peers make 100 committed offline edits each; time vector capture, two-way delta encode/import and materialization. Edits are outside timing.
upstream-map-conflict/16-peers
Adapted B3.1: 16 peers sharing a baseline concurrently update one map key; time importing all deltas and materializing one peer. Setup/encoding and a reference merge are outside timing.
proxy-overhead/read-count
Same cached nested field, native API versus the Pulsify pipeline bypassing proxies versus the public proxy.
proxy-overhead/mutate-history-0
Native json-joy versus identical Pulsify commit/native-journal pipeline with and without proxy traps; no encoding during local writes, encoded rows are verified identical after export.
proxy-overhead/mutate-history-1000
Native json-joy versus identical Pulsify commit/native-journal pipeline with and without proxy traps; no encoding during local writes, encoded rows are verified identical after export.
proxy-overhead/mutate-history-5000
Native json-joy versus identical Pulsify commit/native-journal pipeline with and without proxy traps; no encoding during local writes, encoded rows are verified identical after export.

02 / Protocol & connectors

Beyond the engine

Suite methodology

35 supplementary cases cover frame codecs, operation codecs, local operations, acknowledged synchronization, initial loading and offline reconnects.

Apple M4 · bun-1.4.4 · 5 warmups + 25 measured samples · 2026-11-04

Revision 3bfc576 + working-tree changes Source data

Outer frame codecs µs/op · Lower is better
ScenarioPulsify codecJSON + base64
Encode0 operation rows0.0745Best15 bytes · p95 0.120.24Worst52 bytes · p95 0.25
Decode0 operation rows0.12Best15 bytes · p95 0.380.37Worst52 bytes · p95 0.50
Encode1 operation rows0.18Best173 bytes · p95 0.220.85Worst348 bytes · p95 1.01
Decode1 operation rows0.27Best173 bytes · p95 0.501.47Worst348 bytes · p95 1.97
Encode100 operation rows0.21Best1,056 bytes · p95 0.351.49Worst1,528 bytes · p95 2.44
Decode100 operation rows0.26Best1,056 bytes · p95 0.311.63Worst1,528 bytes · p95 2.42

Outer decoding leaves operation bytes opaque. JSON represents binary leaves as base64 and does not perform Pulsify’s protocol checks. These are task-level wrappers, with different validation work.

Inner operation codecs µs/op · Lower is better
ScenarioPulsify codecJSON + base64
Encode1 operation rows1.06Worst135 bytes · p95 2.080.76Best206 bytes · p95 0.82
Decode1 operation rows1.91Worst135 bytes · p95 2.561.54Best206 bytes · p95 1.96
Encode100 operation rows14.87Worst1,018 bytes · p95 16.9511.04Best3,257 bytes · p95 13.05
Decode100 operation rows19.32Best1,018 bytes · p95 21.6128.31Worst3,257 bytes · p95 29.98
Connector synchronization µs/op · Lower is better
ScenarioPulsifyMedian · p95 below
update · one-way-0ms18.04p95 84.63
update · one-way-5ms12,624.77p95 13,806.02
initial-load · 1053.73p95 110.82
initial-load · 10001,184.21p95 1,970.13
reconnect · 100-offline-edits310.92p95 353.96

Update cases include acknowledgement and convergence; the 5 ms variant delays each direction. Initial loading confirms public data availability. These are different workloads and are not ranked against each other.

Retained heap diagnostics

Pulsify only · not a cross-engine ranking
5,001Retained history rows
1,426,277 BRetained-history JS heap
65,015 BCreate / destroy heap delta

GC runs before and after preparation. These observations are neither RSS measurements nor proof of leak absence. JS heap cannot fairly rank a JavaScript engine against WASM memory. This suite uses the legacy typed-definition API without the explicit application schema of the primary comparison.

All 35 measurements Iterations, median, p95, min, max, ops/s and payloads

Timing columns show µs/op. The p95 is a percentile of sample averages, not individual-event tail latency. Highlights compare participants in the same scenario and metric.

Complete timing measurements
Scenario / participantIterationsmedianµs/opp95 avg.µs/opminµs/opmaxµs/opOps/sBytes/op
protocol/compile-schemaprotocol20017.9924.4816.7226.8655,580—
frame/encode/0codec1,0000.07450.120.06860.6013,422,81915.00
frame/decode/0codec1,0000.120.380.09690.638,105,37015.00
frame/encode/0json1,0000.240.250.230.284,206,09952.00
frame/decode/0json1,0000.370.500.340.832,688,17252.00
frame/encode/1codec1,0000.180.220.160.585,406,633173.00
frame/decode/1codec1,0000.270.500.240.523,742,403173.00
frame/encode/1json1,0000.851.010.811.091,171,018348.00
frame/decode/1json1,0001.471.971.412.16682,419348.00
operations/encode/1operations5001.062.080.932.18944,287135.00
operations/decode/1operations5001.912.561.552.89522,876135.00
operations/encode/1operations-json5000.760.820.731.281,312,191206.00
operations/decode/1operations-json5001.541.961.471.96650,265206.00
frame/encode/100codec2000.210.350.181.714,790,4191,056.00
frame/decode/100codec2000.260.310.241.173,840,0251,056.00
frame/encode/100json2001.492.441.272.57669,1761,528.00
frame/decode/100json2001.632.421.452.44614,2031,528.00
operations/encode/100operations10014.8716.9513.9117.7367,2651,018.00
operations/decode/100operations10019.3221.6118.1821.6151,7641,018.00
operations/encode/100operations-json10011.0413.0510.4413.3290,5593,257.00
operations/decode/100operations-json10028.3129.9826.3130.0035,3293,257.00
local/create/10Pulsify206.9625.355.7231.77143,712—
local/create/10json-joy2010.9639.187.6143.4091,220—
local/create/1000Pulsify2360.69951.83247.461,175.292,772—
local/create/1000json-joy2427.94937.38382.211,214.002,337—
local/read-nestedPulsify10,0000.03420.04260.03370.045829,268,264—
local/mutate/history-0Pulsify2000.160.420.150.436,137,982—
local/mutate/history-1000Pulsify2000.140.160.110.177,174,888—
local/mutate/history-5000Pulsify2000.09190.140.08130.1710,884,354—
local/mutatejson-joy2000.290.760.265.333,478,261—
sync/update/one-way-0mssync1018.0484.6313.84104.3955,440—
sync/update/one-way-5mssync212,624.7713,806.0212,288.0615,715.0679—
sync/initial-load/10sync1053.73110.8244.19214.2418,612—
sync/initial-load/1000sync21,184.211,970.131,054.482,242.79844—
sync/reconnect/100-offline-editssync1310.92353.96260.251,746.373,216—

03 / Durable storage

Every backend. Under load.

Method & configuration

Native Rust clients, real Pulsify histories, concurrent reads and durable writes. Explore every checkpointed cell, the recovery probes and the later mixed-workload tuning campaigns.

957,000Checkpointed engine-campaign requests
584Recorded request errors, retained
18 / 20Main matrices completed
Database successful requests / second req/s · Higher is better
ScenarioPostgreSQL16SQLiteAlloyDB OmniLocal Omni 17.10.0OrioleDBPG 17.11 / beta 19pgrust row0.4-betapgrust columnar0.4-beta
Write full document1,000 requests2,136.58Bestp99 157.31 ms · 0 errors191.87Worstp99 4,392.82 ms · 0 errors2,001.16p99 161.80 ms · 0 errors2,070.77p99 138.77 ms · 0 errors2,023.10p99 344.54 ms · 0 errors1,480.15p99 183.86 ms · 0 errors
Read full history1,000 requests1,250.01p99 241.58 ms · 0 errors778.62Worstp99 278.91 ms · 0 errors1,525.52Bestp99 189.82 ms · 0 errors1,488.32p99 182.14 ms · 0 errors1,487.93p99 210.42 ms · 0 errors979.46p99 274.63 ms · 0 errors
Append patch1,000 requests474.72p99 309.28 ms · 0 errors302.34p99 2,687.09 ms · 0 errors240.86Worstp99 611.26 ms · 0 errors519.94Bestp99 272.67 ms · 0 errors472.49p99 356.61 ms · 0 errors—Not reached · failed series
Lossless compactionAttempts include no-ops7,301.02Best1 useful / 999 no-op2,680.03Worst1 useful / 999 no-op6,922.761 useful / 999 no-op5,844.111 useful / 999 no-op5,952.911 useful / 999 no-op—Not reached · failed series
Read from version vector1,000 requests6,362.02Bestp99 118.81 ms · 0 errors1,296.78Worstp99 161.53 ms · 0 errors5,525.52p99 98.34 ms · 0 errors2,253.13p99 190.59 ms · 0 errors6,042.44p99 71.22 ms · 0 errors—Not reached · failed series
Mixed workload45 / 25 / 10 / 10 / 10 mix638.80p99 700.51 ms · 0 errors459.74p99 1,572.82 ms · 0 errors521.27p99 895.44 ms · 0 errors407.89Worstp99 1,530.14 ms · 0 errors684.13Bestp99 709.08 ms · 0 errors—Not reached · failed series

Highlights compare the same workload within one deployment. Throughput counts successful requests. Compaction-attempt throughput includes no-ops; select useful compactions/s to compare actual work. Docker large runs cap each container at 4 GiB with no swap; medium Docker used the VM defaults.

Mixed-workload operation breakdown Same filters and metric as above

Each operation’s rate is its completed count divided by the whole mixed batch duration, not standalone operation capacity.

Operations within the mixed workload req/s · Higher is better
ScenarioPostgreSQL16SQLiteAlloyDB OmniLocal Omni 17.10.0OrioleDBPG 17.11 / beta 19pgrust row0.4-betapgrust columnar0.4-beta
Write full documentWithin the mixed batch63.88p99 84.60 ms · 0 errors45.97p99 1,978.22 ms · 0 errors52.13p99 58.09 ms · 0 errors40.79Worstp99 111.80 ms · 0 errors68.41Bestp99 85.93 ms · 0 errors—Not reached · failed series
Read full historyWithin the mixed batch63.88p99 75.43 ms · 0 errors45.97p99 121.53 ms · 0 errors52.13p99 81.21 ms · 0 errors40.79Worstp99 109.61 ms · 0 errors68.41Bestp99 97.84 ms · 0 errors—Not reached · failed series
Append patchWithin the mixed batch287.46p99 412.54 ms · 0 errors206.88p99 1,865.58 ms · 0 errors234.57p99 559.47 ms · 0 errors183.55Worstp99 1,450.70 ms · 0 errors307.86Bestp99 393.76 ms · 0 errors—Not reached · failed series
Lossless compactionWithin the mixed batch63.885 useful / 95 no-op45.9750 useful / 50 no-op52.135 useful / 95 no-op40.79Worst6 useful / 94 no-op68.41Best5 useful / 95 no-op—Not reached · failed series
Read from version vectorWithin the mixed batch159.70p99 62.56 ms · 0 errors114.93p99 104.03 ms · 0 errors130.32p99 54.66 ms · 0 errors101.97Worstp99 101.13 ms · 0 errors171.03Bestp99 82.42 ms · 0 errors—Not reached · failed series
Matrix status & original records All checkpoints, including incomplete series
Source records for the selected profile and deployment
EngineCheckpointed cellsRequest errorsStatusEvidence
AlloyDB OmniHover for recorded version24 / 240CompleteRaw JSON (gzip)
OrioleDBHover for recorded version24 / 240CompleteRaw JSON (gzip)
pgrust columnarHover for recorded version9 / 240Incomplete · backend failureRaw JSON (gzip)
pgrust rowHover for recorded version24 / 240CompleteRaw JSON (gzip)
PostgreSQLHover for recorded version24 / 240CompleteRaw JSON (gzip)
SQLiteSQLite 3.51.124 / 240CompleteRaw JSON (gzip)
Mixed-workload optimization results Before / after, with repeated-run medians

The primary large native comparison uses three baseline and three optimized campaigns with identical fixtures. Docker and medium each use one before/after pair, with less evidence about variance. These are separate from the original engine comparison.

Mixed-workload adapter optimization successful req/s · Higher is better
ScenarioBeforeMedian campaign throughputOptimizedMedian campaign throughput
PostgreSQL · 128 clientsNative · spread · burst1,240.72Worst3,613.88Best+191.27% throughput
PostgreSQL · 128 clientsNative · spread · saturation1,526.79Worst3,740.39Best+144.98% throughput
PostgreSQL · 128 clientsNative · hot · burst1,258.74Worst2,513.95Best+99.72% throughput
PostgreSQL · 128 clientsNative · hot · saturation1,190.16Worst2,244.10Best+88.55% throughput
SQLite · 128 clientsNative · spread · burst1,125.47Worst2,921.90Best+159.61% throughput
SQLite · 128 clientsNative · spread · saturation911.73Worst2,760.49Best+202.78% throughput
SQLite · 128 clientsNative · hot · burst922.85Worst1,815.71Best+96.75% throughput
SQLite · 128 clientsNative · hot · saturation924.92Worst1,879.20Best+103.17% throughput
Audited comparisons & source snapshots
Archived sequential storage suite All 81 measurements · PostgreSQL, SQLite & FoundationDB

Historical Docker / Bun suite: 3 warmups + 12 measured samples per case. FoundationDB used a Python JSON/base64 pipe per primitive; its transport costs are included. These scores are separate from the native Rust campaign above.

Historical sequential storage measurements ms/op · Lower is better
ScenarioPostgreSQL16.15 · Bun SQL / TCPSQLite3.53.4 · bun:sqliteFoundationDB7.3.77 · Python pipe
Write full document4.682.59Best9.20Worst
Append patch2.16Best2.219.67Worst
Read full history3.052.34Best601.50Worst
load full9.186.43Best713.89Worst
Read from version vector0.780.70Best87.58Worst
sync delta0.98Best1.0273.22Worst
Lossless compaction127.6918.69Best2,086.53Worst
Read compacted history1.280.87Best30.28Worst
Read compacted delta1.060.84Best17.49Worst
All raw samples & metadata
Production PostgreSQL adapter measurements Correctness-hardened revision · 3 batch sizes

Bun 1.4.4 / PostgreSQL 16.15 over localhost TCP in Docker. Durable logged writes, 128-byte opaque payloads, 2 warmups and 6 measured runs. The statistic is the fourth sorted sample of six (upper middle), rather than the arithmetic median. Batch sizes are different workloads and are not ranked against one another.

Final production adapter revision · milliseconds
Rows per appendAppend, upper middleDelta, upper middleDelta rows returned
12.020.221
1006.870.3210
1,00024.250.6410
All warmup & measured samples · Historical optimization measurements

04 / Read the numbers

Measured carefully. Read in context.

Full methodology
01

One machine, fresh state

Apple M4, 10 cores, ARM64, Bun 1.4.4. The CRDT suite uses 5 warmups and 25 measured samples, fresh replicas and a fixed-seed shuffled participant order. Fixture preparation, schema compilation, GC, verification and cleanup are outside timing.

02

Sample averages, not event tails

The CRDT median and p95 summarize per-sample average operation times. They are not individual-event latency percentiles. Nanosecond-scale differences can reflect noise; these runs do not establish statistical significance or a universal engine ranking.

03

The same task, different histories

Every engine gets editable nested containers. Pulsify uses an explicit compiled application schema. Local edits exclude binary encoding; the encoding and synchronization cases add that work explicitly. Loro is warmed Node WASM; its module load and compilation are excluded.

04

CPU, network and storage are distinct

The CRDT round trip is synchronous and in memory. Connector results include acknowledgement. Database results use finite 1,000-request batches and durable storage; they are not steady-state capacity estimates. Native macOS and Docker Linux are separate deployments.

Reproduce the results

Run on a quiet machine and keep each report. Compare matching runtimes, library versions, hardware, fixtures and iteration counts.

Database setup & commands
From the repository root
# Full CRDT comparison: 5 warmups, 25 samples
bun run bench:crdt --output benchmarks/results/new.json \
  --markdown benchmarks/results/new.md

# Supplementary protocol and connector suite
bun run bench --output benchmarks/results/protocol-new.json

# Native / Docker database campaign
bun run bench:database

05 / Research archive

The work behind the numbers

Browse all records

Use the recorded-run selectors above to explore 11 CRDT campaigns and 7 supplementary campaigns. The database selector also preserves native FoundationDB results, fresh probes and individual tuning runs. These reports document the separate optimization experiments and their limits.

Reports, diagnostics and historical context
ReportWhat it coversOriginal record
Native hybrid v3Current engine, state exchange and remaining limitsRead report
Native coreInitial engine rewrite and the preceding baselineRead report
Grouped transportExact-history batching and encodingRead report
Compiled preparationCreation, import and append A/B diagnosticsRead report
Scalar commitsGeneral vs specialized scalar commit diagnosticsRead report
Offline mergeMatched campaigns and instrumented phase timingsRead report
Scalar-run prototypeHistorical warm-run experiments before integrationRead report
Independent auditFairness review and corrective recheckRead report
Deferred encodingPrevious engine · local writes and memory trade-offsRead report
Runtime simplificationPrevious engine · copies and traversal costsRead report
Validation removalPrevious engine · additional generic validatorRead report
Database incident & recoveryIncomplete matrices, timeouts, OOM and fresh probesRead report