aboutsummaryrefslogtreecommitdiffstats
path: root/research/experiments/005-storage-edit-latency
diff options
context:
space:
mode:
Diffstat (limited to 'research/experiments/005-storage-edit-latency')
-rw-r--r--research/experiments/005-storage-edit-latency/2026-09-24-check-hybrid.log.gzbin0 -> 6376 bytes
-rw-r--r--research/experiments/005-storage-edit-latency/2026-09-24-ext4-linux-aarch64.json463
-rw-r--r--research/experiments/005-storage-edit-latency/2026-09-24-sequence-ext4-linux-aarch64.json862
-rw-r--r--research/experiments/005-storage-edit-latency/copy-memory.json43
-rw-r--r--research/experiments/005-storage-edit-latency/flat-memory.json43
-rw-r--r--research/experiments/005-storage-edit-latency/measure.py99
-rw-r--r--research/experiments/005-storage-edit-latency/paged-memory.json43
-rw-r--r--research/experiments/005-storage-edit-latency/sequence.py85
8 files changed, 1638 insertions, 0 deletions
diff --git a/research/experiments/005-storage-edit-latency/2026-09-24-check-hybrid.log.gz b/research/experiments/005-storage-edit-latency/2026-09-24-check-hybrid.log.gz
new file mode 100644
index 0000000..e4c0174
--- /dev/null
+++ b/research/experiments/005-storage-edit-latency/2026-09-24-check-hybrid.log.gz
Binary files differ
diff --git a/research/experiments/005-storage-edit-latency/2026-09-24-ext4-linux-aarch64.json b/research/experiments/005-storage-edit-latency/2026-09-24-ext4-linux-aarch64.json
new file mode 100644
index 0000000..f9decda
--- /dev/null
+++ b/research/experiments/005-storage-edit-latency/2026-09-24-ext4-linux-aarch64.json
@@ -0,0 +1,463 @@
+{
+ "profile": "native-cli-durable-one-mib-document-last-byte-edit",
+ "platform": "Linux-6.18.39+rpt-rpi-2712-aarch64-with-glibc2.41",
+ "filesystem": "ext4 /dev/mmcblk0p2[/home/eunhhu/work/pure]",
+ "trials_per_variant": 18,
+ "final_body_sha256": "06cb5e32505f66804f6e8c79e3230105ca4769444e1389ee36f6f11fd61f2d3a",
+ "scope": "Each sample starts a new CLI process; create and edit each include program parsing, store initialization, file and directory synchronization. Show verifies the final revision but is outside the timed operations. The host page cache, filesystem and device remain uncontrolled.",
+ "variants": {
+ "flat": {
+ "binary_sha256": "23ebc0ce1df9c49216511bee658fd5b9e373509e296bd2c2f8e76e6d3081cded",
+ "median_create_ms": 83.556,
+ "median_edit_ms": 78.805,
+ "samples": [
+ {
+ "trial": 0,
+ "create_ms": 93.308,
+ "edit_ms": 86.265,
+ "store_files": 2,
+ "store_file_bytes": 1048600,
+ "store_allocated_bytes": 1052672
+ },
+ {
+ "trial": 1,
+ "create_ms": 66.85,
+ "edit_ms": 81.315,
+ "store_files": 2,
+ "store_file_bytes": 1048600,
+ "store_allocated_bytes": 1052672
+ },
+ {
+ "trial": 2,
+ "create_ms": 83.243,
+ "edit_ms": 26.218,
+ "store_files": 2,
+ "store_file_bytes": 1048600,
+ "store_allocated_bytes": 1052672
+ },
+ {
+ "trial": 3,
+ "create_ms": 25.734,
+ "edit_ms": 81.636,
+ "store_files": 2,
+ "store_file_bytes": 1048600,
+ "store_allocated_bytes": 1052672
+ },
+ {
+ "trial": 4,
+ "create_ms": 80.359,
+ "edit_ms": 22.923,
+ "store_files": 2,
+ "store_file_bytes": 1048600,
+ "store_allocated_bytes": 1052672
+ },
+ {
+ "trial": 5,
+ "create_ms": 84.855,
+ "edit_ms": 77.624,
+ "store_files": 2,
+ "store_file_bytes": 1048600,
+ "store_allocated_bytes": 1052672
+ },
+ {
+ "trial": 6,
+ "create_ms": 83.516,
+ "edit_ms": 24.882,
+ "store_files": 2,
+ "store_file_bytes": 1048600,
+ "store_allocated_bytes": 1052672
+ },
+ {
+ "trial": 7,
+ "create_ms": 100.653,
+ "edit_ms": 65.551,
+ "store_files": 2,
+ "store_file_bytes": 1048600,
+ "store_allocated_bytes": 1052672
+ },
+ {
+ "trial": 8,
+ "create_ms": 87.547,
+ "edit_ms": 88.306,
+ "store_files": 2,
+ "store_file_bytes": 1048600,
+ "store_allocated_bytes": 1052672
+ },
+ {
+ "trial": 9,
+ "create_ms": 100.781,
+ "edit_ms": 89.489,
+ "store_files": 2,
+ "store_file_bytes": 1048600,
+ "store_allocated_bytes": 1052672
+ },
+ {
+ "trial": 10,
+ "create_ms": 29.332,
+ "edit_ms": 79.987,
+ "store_files": 2,
+ "store_file_bytes": 1048600,
+ "store_allocated_bytes": 1052672
+ },
+ {
+ "trial": 11,
+ "create_ms": 80.05,
+ "edit_ms": 26.629,
+ "store_files": 2,
+ "store_file_bytes": 1048600,
+ "store_allocated_bytes": 1052672
+ },
+ {
+ "trial": 12,
+ "create_ms": 79.751,
+ "edit_ms": 24.165,
+ "store_files": 2,
+ "store_file_bytes": 1048600,
+ "store_allocated_bytes": 1052672
+ },
+ {
+ "trial": 13,
+ "create_ms": 85.309,
+ "edit_ms": 23.163,
+ "store_files": 2,
+ "store_file_bytes": 1048600,
+ "store_allocated_bytes": 1052672
+ },
+ {
+ "trial": 14,
+ "create_ms": 83.597,
+ "edit_ms": 82.171,
+ "store_files": 2,
+ "store_file_bytes": 1048600,
+ "store_allocated_bytes": 1052672
+ },
+ {
+ "trial": 15,
+ "create_ms": 97.909,
+ "edit_ms": 85.934,
+ "store_files": 2,
+ "store_file_bytes": 1048600,
+ "store_allocated_bytes": 1052672
+ },
+ {
+ "trial": 16,
+ "create_ms": 25.638,
+ "edit_ms": 82.514,
+ "store_files": 2,
+ "store_file_bytes": 1048600,
+ "store_allocated_bytes": 1052672
+ },
+ {
+ "trial": 17,
+ "create_ms": 86.699,
+ "edit_ms": 23.813,
+ "store_files": 2,
+ "store_file_bytes": 1048600,
+ "store_allocated_bytes": 1052672
+ }
+ ]
+ },
+ "copy": {
+ "binary_sha256": "52c2b828b0cba3564a37e3874f60bec793d1dd7031aff77ba850cee7683977f9",
+ "median_create_ms": 81.672,
+ "median_edit_ms": 77.908,
+ "samples": [
+ {
+ "trial": 0,
+ "create_ms": 29.767,
+ "edit_ms": 78.001,
+ "store_files": 2,
+ "store_file_bytes": 1048600,
+ "store_allocated_bytes": 1052672
+ },
+ {
+ "trial": 1,
+ "create_ms": 89.789,
+ "edit_ms": 91.286,
+ "store_files": 2,
+ "store_file_bytes": 1048600,
+ "store_allocated_bytes": 1052672
+ },
+ {
+ "trial": 2,
+ "create_ms": 83.106,
+ "edit_ms": 77.868,
+ "store_files": 2,
+ "store_file_bytes": 1048600,
+ "store_allocated_bytes": 1052672
+ },
+ {
+ "trial": 3,
+ "create_ms": 84.346,
+ "edit_ms": 47.881,
+ "store_files": 2,
+ "store_file_bytes": 1048600,
+ "store_allocated_bytes": 1052672
+ },
+ {
+ "trial": 4,
+ "create_ms": 29.06,
+ "edit_ms": 78.142,
+ "store_files": 2,
+ "store_file_bytes": 1048600,
+ "store_allocated_bytes": 1052672
+ },
+ {
+ "trial": 5,
+ "create_ms": 29.801,
+ "edit_ms": 52.17,
+ "store_files": 2,
+ "store_file_bytes": 1048600,
+ "store_allocated_bytes": 1052672
+ },
+ {
+ "trial": 6,
+ "create_ms": 81.026,
+ "edit_ms": 77.948,
+ "store_files": 2,
+ "store_file_bytes": 1048600,
+ "store_allocated_bytes": 1052672
+ },
+ {
+ "trial": 7,
+ "create_ms": 30.027,
+ "edit_ms": 87.974,
+ "store_files": 2,
+ "store_file_bytes": 1048600,
+ "store_allocated_bytes": 1052672
+ },
+ {
+ "trial": 8,
+ "create_ms": 45.522,
+ "edit_ms": 87.955,
+ "store_files": 2,
+ "store_file_bytes": 1048600,
+ "store_allocated_bytes": 1052672
+ },
+ {
+ "trial": 9,
+ "create_ms": 80.228,
+ "edit_ms": 22.863,
+ "store_files": 2,
+ "store_file_bytes": 1048600,
+ "store_allocated_bytes": 1052672
+ },
+ {
+ "trial": 10,
+ "create_ms": 82.319,
+ "edit_ms": 77.564,
+ "store_files": 2,
+ "store_file_bytes": 1048600,
+ "store_allocated_bytes": 1052672
+ },
+ {
+ "trial": 11,
+ "create_ms": 94.951,
+ "edit_ms": 46.186,
+ "store_files": 2,
+ "store_file_bytes": 1048600,
+ "store_allocated_bytes": 1052672
+ },
+ {
+ "trial": 12,
+ "create_ms": 84.607,
+ "edit_ms": 80.2,
+ "store_files": 2,
+ "store_file_bytes": 1048600,
+ "store_allocated_bytes": 1052672
+ },
+ {
+ "trial": 13,
+ "create_ms": 29.82,
+ "edit_ms": 86.258,
+ "store_files": 2,
+ "store_file_bytes": 1048600,
+ "store_allocated_bytes": 1052672
+ },
+ {
+ "trial": 14,
+ "create_ms": 94.36,
+ "edit_ms": 31.893,
+ "store_files": 2,
+ "store_file_bytes": 1048600,
+ "store_allocated_bytes": 1052672
+ },
+ {
+ "trial": 15,
+ "create_ms": 97.289,
+ "edit_ms": 44.737,
+ "store_files": 2,
+ "store_file_bytes": 1048600,
+ "store_allocated_bytes": 1052672
+ },
+ {
+ "trial": 16,
+ "create_ms": 60.805,
+ "edit_ms": 80.635,
+ "store_files": 2,
+ "store_file_bytes": 1048600,
+ "store_allocated_bytes": 1052672
+ },
+ {
+ "trial": 17,
+ "create_ms": 85.887,
+ "edit_ms": 77.182,
+ "store_files": 2,
+ "store_file_bytes": 1048600,
+ "store_allocated_bytes": 1052672
+ }
+ ]
+ },
+ "paged": {
+ "binary_sha256": "e1c12a2f5e736bb86737ceff7849fe95b1a9d13136e8c5eb3f3398cb3971c6f5",
+ "median_create_ms": 88.948,
+ "median_edit_ms": 14.473,
+ "samples": [
+ {
+ "trial": 0,
+ "create_ms": 117.897,
+ "edit_ms": 565.484,
+ "store_files": 3,
+ "store_file_bytes": 1065520,
+ "store_allocated_bytes": 1069056
+ },
+ {
+ "trial": 1,
+ "create_ms": 47.619,
+ "edit_ms": 13.102,
+ "store_files": 3,
+ "store_file_bytes": 1065520,
+ "store_allocated_bytes": 1069056
+ },
+ {
+ "trial": 2,
+ "create_ms": 89.46,
+ "edit_ms": 14.779,
+ "store_files": 3,
+ "store_file_bytes": 1065520,
+ "store_allocated_bytes": 1069056
+ },
+ {
+ "trial": 3,
+ "create_ms": 30.501,
+ "edit_ms": 15.016,
+ "store_files": 3,
+ "store_file_bytes": 1065520,
+ "store_allocated_bytes": 1069056
+ },
+ {
+ "trial": 4,
+ "create_ms": 89.223,
+ "edit_ms": 13.383,
+ "store_files": 3,
+ "store_file_bytes": 1065520,
+ "store_allocated_bytes": 1069056
+ },
+ {
+ "trial": 5,
+ "create_ms": 88.518,
+ "edit_ms": 12.718,
+ "store_files": 3,
+ "store_file_bytes": 1065520,
+ "store_allocated_bytes": 1069056
+ },
+ {
+ "trial": 6,
+ "create_ms": 88.992,
+ "edit_ms": 13.681,
+ "store_files": 3,
+ "store_file_bytes": 1065520,
+ "store_allocated_bytes": 1069056
+ },
+ {
+ "trial": 7,
+ "create_ms": 116.568,
+ "edit_ms": 23.607,
+ "store_files": 3,
+ "store_file_bytes": 1065520,
+ "store_allocated_bytes": 1069056
+ },
+ {
+ "trial": 8,
+ "create_ms": 100.547,
+ "edit_ms": 24.497,
+ "store_files": 3,
+ "store_file_bytes": 1065520,
+ "store_allocated_bytes": 1069056
+ },
+ {
+ "trial": 9,
+ "create_ms": 54.374,
+ "edit_ms": 13.896,
+ "store_files": 3,
+ "store_file_bytes": 1065520,
+ "store_allocated_bytes": 1069056
+ },
+ {
+ "trial": 10,
+ "create_ms": 84.894,
+ "edit_ms": 13.607,
+ "store_files": 3,
+ "store_file_bytes": 1065520,
+ "store_allocated_bytes": 1069056
+ },
+ {
+ "trial": 11,
+ "create_ms": 92.673,
+ "edit_ms": 14.166,
+ "store_files": 3,
+ "store_file_bytes": 1065520,
+ "store_allocated_bytes": 1069056
+ },
+ {
+ "trial": 12,
+ "create_ms": 34.961,
+ "edit_ms": 78.767,
+ "store_files": 3,
+ "store_file_bytes": 1065520,
+ "store_allocated_bytes": 1069056
+ },
+ {
+ "trial": 13,
+ "create_ms": 88.903,
+ "edit_ms": 13.656,
+ "store_files": 3,
+ "store_file_bytes": 1065520,
+ "store_allocated_bytes": 1069056
+ },
+ {
+ "trial": 14,
+ "create_ms": 57.426,
+ "edit_ms": 13.968,
+ "store_files": 3,
+ "store_file_bytes": 1065520,
+ "store_allocated_bytes": 1069056
+ },
+ {
+ "trial": 15,
+ "create_ms": 38.675,
+ "edit_ms": 14.937,
+ "store_files": 3,
+ "store_file_bytes": 1065520,
+ "store_allocated_bytes": 1069056
+ },
+ {
+ "trial": 16,
+ "create_ms": 90.664,
+ "edit_ms": 15.344,
+ "store_files": 3,
+ "store_file_bytes": 1065520,
+ "store_allocated_bytes": 1069056
+ },
+ {
+ "trial": 17,
+ "create_ms": 90.055,
+ "edit_ms": 14.926,
+ "store_files": 3,
+ "store_file_bytes": 1065520,
+ "store_allocated_bytes": 1069056
+ }
+ ]
+ }
+ }
+}
diff --git a/research/experiments/005-storage-edit-latency/2026-09-24-sequence-ext4-linux-aarch64.json b/research/experiments/005-storage-edit-latency/2026-09-24-sequence-ext4-linux-aarch64.json
new file mode 100644
index 0000000..5efbf8b
--- /dev/null
+++ b/research/experiments/005-storage-edit-latency/2026-09-24-sequence-ext4-linux-aarch64.json
@@ -0,0 +1,862 @@
+{
+ "profile": "repeated-durable-one-mib-document-last-byte-edit",
+ "platform": "Linux-6.18.39+rpt-rpi-2712-aarch64-with-glibc2.41",
+ "filesystem": "ext4 /dev/mmcblk0p2[/home/eunhhu/work/pure]",
+ "edits_per_variant": 70,
+ "final_body_sha256": "3cfc240f48e8e1b8553f3e2670adf7b81cafe34f089d4372fa4c28787dc02151",
+ "scope": "Same actual source-loaded CLI and editor.pure, two persistent stores, alternating operation order. Timings include each edit process, parser, store init and fsync; creation and final show are not timed. Device state is uncontrolled.",
+ "variants": {
+ "flat": {
+ "binary_sha256": "23ebc0ce1df9c49216511bee658fd5b9e373509e296bd2c2f8e76e6d3081cded",
+ "median_edit_ms": 83.176,
+ "samples": [
+ {
+ "edit": 1,
+ "elapsed_ms": 24.328,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 2,
+ "elapsed_ms": 79.862,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 3,
+ "elapsed_ms": 177.059,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 4,
+ "elapsed_ms": 89.804,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 5,
+ "elapsed_ms": 92.579,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 6,
+ "elapsed_ms": 83.733,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 7,
+ "elapsed_ms": 29.379,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 8,
+ "elapsed_ms": 78.191,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 9,
+ "elapsed_ms": 57.551,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 10,
+ "elapsed_ms": 83.875,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 11,
+ "elapsed_ms": 78.209,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 12,
+ "elapsed_ms": 85.296,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 13,
+ "elapsed_ms": 25.204,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 14,
+ "elapsed_ms": 28.663,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 15,
+ "elapsed_ms": 82.153,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 16,
+ "elapsed_ms": 84.924,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 17,
+ "elapsed_ms": 82.359,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 18,
+ "elapsed_ms": 39.211,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 19,
+ "elapsed_ms": 28.648,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 20,
+ "elapsed_ms": 82.115,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 21,
+ "elapsed_ms": 83.895,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 22,
+ "elapsed_ms": 83.007,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 23,
+ "elapsed_ms": 84.496,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 24,
+ "elapsed_ms": 28.525,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 25,
+ "elapsed_ms": 86.499,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 26,
+ "elapsed_ms": 25.536,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 27,
+ "elapsed_ms": 93.217,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 28,
+ "elapsed_ms": 36.673,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 29,
+ "elapsed_ms": 84.748,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 30,
+ "elapsed_ms": 79.529,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 31,
+ "elapsed_ms": 84.024,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 32,
+ "elapsed_ms": 82.681,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 33,
+ "elapsed_ms": 28.903,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 34,
+ "elapsed_ms": 29.254,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 35,
+ "elapsed_ms": 86.486,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 36,
+ "elapsed_ms": 96.679,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 37,
+ "elapsed_ms": 41.477,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 38,
+ "elapsed_ms": 82.386,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 39,
+ "elapsed_ms": 25.063,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 40,
+ "elapsed_ms": 27.84,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 41,
+ "elapsed_ms": 86.119,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 42,
+ "elapsed_ms": 89.284,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 43,
+ "elapsed_ms": 84.316,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 44,
+ "elapsed_ms": 86.972,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 45,
+ "elapsed_ms": 88.795,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 46,
+ "elapsed_ms": 47.265,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 47,
+ "elapsed_ms": 32.736,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 48,
+ "elapsed_ms": 93.95,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 49,
+ "elapsed_ms": 29.861,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 50,
+ "elapsed_ms": 25.674,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 51,
+ "elapsed_ms": 87.319,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 52,
+ "elapsed_ms": 83.632,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 53,
+ "elapsed_ms": 83.964,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 54,
+ "elapsed_ms": 83.345,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 55,
+ "elapsed_ms": 175.728,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 56,
+ "elapsed_ms": 92.189,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 57,
+ "elapsed_ms": 98.676,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 58,
+ "elapsed_ms": 84.905,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 59,
+ "elapsed_ms": 81.966,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 60,
+ "elapsed_ms": 87.203,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 61,
+ "elapsed_ms": 25.923,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 62,
+ "elapsed_ms": 29.165,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 63,
+ "elapsed_ms": 82.076,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 64,
+ "elapsed_ms": 128.506,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 65,
+ "elapsed_ms": 91.837,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 66,
+ "elapsed_ms": 25.832,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 67,
+ "elapsed_ms": 86.189,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 68,
+ "elapsed_ms": 83.351,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 69,
+ "elapsed_ms": 85.479,
+ "files": 2,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 70,
+ "elapsed_ms": 77.587,
+ "files": 2,
+ "allocated_bytes": 1052672
+ }
+ ]
+ },
+ "paged": {
+ "binary_sha256": "e1c12a2f5e736bb86737ceff7849fe95b1a9d13136e8c5eb3f3398cb3971c6f5",
+ "median_edit_ms": 18.858,
+ "samples": [
+ {
+ "edit": 1,
+ "elapsed_ms": 16.412,
+ "files": 3,
+ "allocated_bytes": 1069056
+ },
+ {
+ "edit": 2,
+ "elapsed_ms": 17.281,
+ "files": 3,
+ "allocated_bytes": 1085440
+ },
+ {
+ "edit": 3,
+ "elapsed_ms": 32.037,
+ "files": 3,
+ "allocated_bytes": 1101824
+ },
+ {
+ "edit": 4,
+ "elapsed_ms": 29.739,
+ "files": 3,
+ "allocated_bytes": 1118208
+ },
+ {
+ "edit": 5,
+ "elapsed_ms": 28.746,
+ "files": 3,
+ "allocated_bytes": 1134592
+ },
+ {
+ "edit": 6,
+ "elapsed_ms": 31.822,
+ "files": 3,
+ "allocated_bytes": 1150976
+ },
+ {
+ "edit": 7,
+ "elapsed_ms": 16.937,
+ "files": 3,
+ "allocated_bytes": 1167360
+ },
+ {
+ "edit": 8,
+ "elapsed_ms": 16.845,
+ "files": 3,
+ "allocated_bytes": 1183744
+ },
+ {
+ "edit": 9,
+ "elapsed_ms": 17.18,
+ "files": 3,
+ "allocated_bytes": 1200128
+ },
+ {
+ "edit": 10,
+ "elapsed_ms": 18.422,
+ "files": 3,
+ "allocated_bytes": 1216512
+ },
+ {
+ "edit": 11,
+ "elapsed_ms": 18.672,
+ "files": 3,
+ "allocated_bytes": 1232896
+ },
+ {
+ "edit": 12,
+ "elapsed_ms": 16.399,
+ "files": 3,
+ "allocated_bytes": 1249280
+ },
+ {
+ "edit": 13,
+ "elapsed_ms": 18.911,
+ "files": 3,
+ "allocated_bytes": 1265664
+ },
+ {
+ "edit": 14,
+ "elapsed_ms": 71.604,
+ "files": 3,
+ "allocated_bytes": 1282048
+ },
+ {
+ "edit": 15,
+ "elapsed_ms": 18.821,
+ "files": 3,
+ "allocated_bytes": 1298432
+ },
+ {
+ "edit": 16,
+ "elapsed_ms": 16.746,
+ "files": 3,
+ "allocated_bytes": 1314816
+ },
+ {
+ "edit": 17,
+ "elapsed_ms": 17.354,
+ "files": 3,
+ "allocated_bytes": 1331200
+ },
+ {
+ "edit": 18,
+ "elapsed_ms": 15.468,
+ "files": 3,
+ "allocated_bytes": 1347584
+ },
+ {
+ "edit": 19,
+ "elapsed_ms": 15.872,
+ "files": 3,
+ "allocated_bytes": 1363968
+ },
+ {
+ "edit": 20,
+ "elapsed_ms": 18.347,
+ "files": 3,
+ "allocated_bytes": 1380352
+ },
+ {
+ "edit": 21,
+ "elapsed_ms": 17.311,
+ "files": 3,
+ "allocated_bytes": 1396736
+ },
+ {
+ "edit": 22,
+ "elapsed_ms": 18.997,
+ "files": 3,
+ "allocated_bytes": 1413120
+ },
+ {
+ "edit": 23,
+ "elapsed_ms": 17.267,
+ "files": 3,
+ "allocated_bytes": 1429504
+ },
+ {
+ "edit": 24,
+ "elapsed_ms": 18.52,
+ "files": 3,
+ "allocated_bytes": 1445888
+ },
+ {
+ "edit": 25,
+ "elapsed_ms": 34.127,
+ "files": 3,
+ "allocated_bytes": 1462272
+ },
+ {
+ "edit": 26,
+ "elapsed_ms": 86.489,
+ "files": 3,
+ "allocated_bytes": 1478656
+ },
+ {
+ "edit": 27,
+ "elapsed_ms": 29.105,
+ "files": 3,
+ "allocated_bytes": 1495040
+ },
+ {
+ "edit": 28,
+ "elapsed_ms": 37.131,
+ "files": 3,
+ "allocated_bytes": 1511424
+ },
+ {
+ "edit": 29,
+ "elapsed_ms": 15.478,
+ "files": 3,
+ "allocated_bytes": 1527808
+ },
+ {
+ "edit": 30,
+ "elapsed_ms": 19.623,
+ "files": 3,
+ "allocated_bytes": 1544192
+ },
+ {
+ "edit": 31,
+ "elapsed_ms": 90.312,
+ "files": 3,
+ "allocated_bytes": 1560576
+ },
+ {
+ "edit": 32,
+ "elapsed_ms": 17.365,
+ "files": 3,
+ "allocated_bytes": 1576960
+ },
+ {
+ "edit": 33,
+ "elapsed_ms": 17.109,
+ "files": 3,
+ "allocated_bytes": 1593344
+ },
+ {
+ "edit": 34,
+ "elapsed_ms": 126.006,
+ "files": 3,
+ "allocated_bytes": 1609728
+ },
+ {
+ "edit": 35,
+ "elapsed_ms": 17.457,
+ "files": 3,
+ "allocated_bytes": 1626112
+ },
+ {
+ "edit": 36,
+ "elapsed_ms": 15.908,
+ "files": 3,
+ "allocated_bytes": 1642496
+ },
+ {
+ "edit": 37,
+ "elapsed_ms": 18.896,
+ "files": 3,
+ "allocated_bytes": 1658880
+ },
+ {
+ "edit": 38,
+ "elapsed_ms": 16.535,
+ "files": 3,
+ "allocated_bytes": 1675264
+ },
+ {
+ "edit": 39,
+ "elapsed_ms": 17.568,
+ "files": 3,
+ "allocated_bytes": 1691648
+ },
+ {
+ "edit": 40,
+ "elapsed_ms": 71.184,
+ "files": 3,
+ "allocated_bytes": 1708032
+ },
+ {
+ "edit": 41,
+ "elapsed_ms": 16.619,
+ "files": 3,
+ "allocated_bytes": 1724416
+ },
+ {
+ "edit": 42,
+ "elapsed_ms": 29.265,
+ "files": 3,
+ "allocated_bytes": 1740800
+ },
+ {
+ "edit": 43,
+ "elapsed_ms": 21.163,
+ "files": 3,
+ "allocated_bytes": 1757184
+ },
+ {
+ "edit": 44,
+ "elapsed_ms": 27.305,
+ "files": 3,
+ "allocated_bytes": 1773568
+ },
+ {
+ "edit": 45,
+ "elapsed_ms": 29.526,
+ "files": 3,
+ "allocated_bytes": 1789952
+ },
+ {
+ "edit": 46,
+ "elapsed_ms": 27.936,
+ "files": 3,
+ "allocated_bytes": 1806336
+ },
+ {
+ "edit": 47,
+ "elapsed_ms": 87.552,
+ "files": 3,
+ "allocated_bytes": 1822720
+ },
+ {
+ "edit": 48,
+ "elapsed_ms": 30.512,
+ "files": 3,
+ "allocated_bytes": 1839104
+ },
+ {
+ "edit": 49,
+ "elapsed_ms": 17.81,
+ "files": 3,
+ "allocated_bytes": 1855488
+ },
+ {
+ "edit": 50,
+ "elapsed_ms": 70.4,
+ "files": 3,
+ "allocated_bytes": 1871872
+ },
+ {
+ "edit": 51,
+ "elapsed_ms": 17.115,
+ "files": 3,
+ "allocated_bytes": 1888256
+ },
+ {
+ "edit": 52,
+ "elapsed_ms": 18.79,
+ "files": 3,
+ "allocated_bytes": 1904640
+ },
+ {
+ "edit": 53,
+ "elapsed_ms": 21.637,
+ "files": 3,
+ "allocated_bytes": 1921024
+ },
+ {
+ "edit": 54,
+ "elapsed_ms": 16.47,
+ "files": 3,
+ "allocated_bytes": 1937408
+ },
+ {
+ "edit": 55,
+ "elapsed_ms": 53.194,
+ "files": 3,
+ "allocated_bytes": 1953792
+ },
+ {
+ "edit": 56,
+ "elapsed_ms": 219.485,
+ "files": 3,
+ "allocated_bytes": 1970176
+ },
+ {
+ "edit": 57,
+ "elapsed_ms": 24.517,
+ "files": 3,
+ "allocated_bytes": 1986560
+ },
+ {
+ "edit": 58,
+ "elapsed_ms": 16.725,
+ "files": 3,
+ "allocated_bytes": 2002944
+ },
+ {
+ "edit": 59,
+ "elapsed_ms": 19.628,
+ "files": 3,
+ "allocated_bytes": 2019328
+ },
+ {
+ "edit": 60,
+ "elapsed_ms": 17.158,
+ "files": 3,
+ "allocated_bytes": 2035712
+ },
+ {
+ "edit": 61,
+ "elapsed_ms": 72.424,
+ "files": 3,
+ "allocated_bytes": 2052096
+ },
+ {
+ "edit": 62,
+ "elapsed_ms": 16.894,
+ "files": 3,
+ "allocated_bytes": 2068480
+ },
+ {
+ "edit": 63,
+ "elapsed_ms": 18.918,
+ "files": 3,
+ "allocated_bytes": 2084864
+ },
+ {
+ "edit": 64,
+ "elapsed_ms": 16.879,
+ "files": 3,
+ "allocated_bytes": 2101248
+ },
+ {
+ "edit": 65,
+ "elapsed_ms": 35.067,
+ "files": 3,
+ "allocated_bytes": 1052672
+ },
+ {
+ "edit": 66,
+ "elapsed_ms": 83.189,
+ "files": 3,
+ "allocated_bytes": 1069056
+ },
+ {
+ "edit": 67,
+ "elapsed_ms": 16.607,
+ "files": 3,
+ "allocated_bytes": 1085440
+ },
+ {
+ "edit": 68,
+ "elapsed_ms": 19.566,
+ "files": 3,
+ "allocated_bytes": 1101824
+ },
+ {
+ "edit": 69,
+ "elapsed_ms": 17.47,
+ "files": 3,
+ "allocated_bytes": 1118208
+ },
+ {
+ "edit": 70,
+ "elapsed_ms": 20.338,
+ "files": 3,
+ "allocated_bytes": 1134592
+ }
+ ]
+ }
+ }
+}
diff --git a/research/experiments/005-storage-edit-latency/copy-memory.json b/research/experiments/005-storage-edit-latency/copy-memory.json
new file mode 100644
index 0000000..969250c
--- /dev/null
+++ b/research/experiments/005-storage-edit-latency/copy-memory.json
@@ -0,0 +1,43 @@
+{
+ "passed": true,
+ "failures": [],
+ "profile": "portable-measurement-no-budget",
+ "variant": "source-program",
+ "scope": "Child-process peak RSS for native CLI document creation, read and one-byte revision edit; includes libc and the selected program form, excludes filesystem cache, kernel buffers, TLS, GUI and other apps. No cross-platform memory ceiling is asserted.",
+ "samples": {
+ "small_edit_rss_kib": 1056,
+ "one_mib_edit_rss_kib": 2080,
+ "one_mib_show_rss_kib": 1104,
+ "one_mib_splice_rss_kib": 1056
+ },
+ "raw_samples": {
+ "small_edit_rss_kib": {
+ "exit_code": 0,
+ "max_rss_kib": 1056,
+ "raw_max_rss": 1056,
+ "raw_unit": "KiB"
+ },
+ "one_mib_edit_rss_kib": {
+ "exit_code": 0,
+ "max_rss_kib": 2080,
+ "raw_max_rss": 2080,
+ "raw_unit": "KiB"
+ },
+ "one_mib_show_rss_kib": {
+ "exit_code": 0,
+ "max_rss_kib": 1104,
+ "raw_max_rss": 1104,
+ "raw_unit": "KiB"
+ },
+ "one_mib_splice_rss_kib": {
+ "exit_code": 0,
+ "max_rss_kib": 1056,
+ "raw_max_rss": 1056,
+ "raw_unit": "KiB"
+ }
+ },
+ "budget_sha256": null,
+ "binary_sha256": "52c2b828b0cba3564a37e3874f60bec793d1dd7031aff77ba850cee7683977f9",
+ "binary_bytes": 178824,
+ "platform": "Linux-6.18.39+rpt-rpi-2712-aarch64-with-glibc2.41"
+}
diff --git a/research/experiments/005-storage-edit-latency/flat-memory.json b/research/experiments/005-storage-edit-latency/flat-memory.json
new file mode 100644
index 0000000..3f45eb1
--- /dev/null
+++ b/research/experiments/005-storage-edit-latency/flat-memory.json
@@ -0,0 +1,43 @@
+{
+ "passed": true,
+ "failures": [],
+ "profile": "portable-measurement-no-budget",
+ "variant": "source-program",
+ "scope": "Child-process peak RSS for native CLI document creation, read and one-byte revision edit; includes libc and the selected program form, excludes filesystem cache, kernel buffers, TLS, GUI and other apps. No cross-platform memory ceiling is asserted.",
+ "samples": {
+ "small_edit_rss_kib": 1056,
+ "one_mib_edit_rss_kib": 2080,
+ "one_mib_show_rss_kib": 1056,
+ "one_mib_splice_rss_kib": 1104
+ },
+ "raw_samples": {
+ "small_edit_rss_kib": {
+ "exit_code": 0,
+ "max_rss_kib": 1056,
+ "raw_max_rss": 1056,
+ "raw_unit": "KiB"
+ },
+ "one_mib_edit_rss_kib": {
+ "exit_code": 0,
+ "max_rss_kib": 2080,
+ "raw_max_rss": 2080,
+ "raw_unit": "KiB"
+ },
+ "one_mib_show_rss_kib": {
+ "exit_code": 0,
+ "max_rss_kib": 1056,
+ "raw_max_rss": 1056,
+ "raw_unit": "KiB"
+ },
+ "one_mib_splice_rss_kib": {
+ "exit_code": 0,
+ "max_rss_kib": 1104,
+ "raw_max_rss": 1104,
+ "raw_unit": "KiB"
+ }
+ },
+ "budget_sha256": null,
+ "binary_sha256": "23ebc0ce1df9c49216511bee658fd5b9e373509e296bd2c2f8e76e6d3081cded",
+ "binary_bytes": 178336,
+ "platform": "Linux-6.18.39+rpt-rpi-2712-aarch64-with-glibc2.41"
+}
diff --git a/research/experiments/005-storage-edit-latency/measure.py b/research/experiments/005-storage-edit-latency/measure.py
new file mode 100644
index 0000000..b390c92
--- /dev/null
+++ b/research/experiments/005-storage-edit-latency/measure.py
@@ -0,0 +1,99 @@
+#!/usr/bin/env python3
+"""Time the same durable one-MiB document creation and one-byte revision edit."""
+import argparse
+import hashlib
+import json
+from pathlib import Path
+import platform
+import statistics
+import subprocess
+import tempfile
+import time
+
+HERE = Path(__file__).resolve().parents[3]
+PROGRAM = HERE / 'prototypes/connections/examples/editor.pure'
+INITIAL = b'A' * 1_048_576
+EXPECTED = INITIAL[:-1] + b'Z'
+
+
+def invoke(command):
+ started = time.monotonic_ns()
+ result = subprocess.run([str(arg) for arg in command], capture_output=True, timeout=30)
+ elapsed_ms = (time.monotonic_ns() - started) / 1_000_000
+ if result.returncode:
+ raise AssertionError((command, result.returncode, result.stderr[-1000:]))
+ return result, elapsed_ms
+
+
+def one(binary, root, initial, replacement):
+ base = [binary, '--program', PROGRAM, '--store', root]
+ created, create_ms = invoke([*base, 'edit', 'document', 0, 0, 0, initial])
+ assert json.loads(created.stdout)['revision'] == '1'
+ edited, edit_ms = invoke([*base, 'edit', 'document', 1, 1_048_575, 1, replacement])
+ assert json.loads(edited.stdout)['revision'] == '2'
+ shown, _ = invoke([*base, 'show', 'document'])
+ assert shown.stdout == EXPECTED
+ assert json.loads(shown.stderr)['revision'] == '2'
+ files = [path for path in root.iterdir() if path.is_file()]
+ return {'create_ms': round(create_ms, 3), 'edit_ms': round(edit_ms, 3),
+ 'store_files': len(files),
+ 'store_file_bytes': sum(path.stat().st_size for path in files),
+ 'store_allocated_bytes': sum(path.stat().st_blocks * 512 for path in files)}
+
+
+def main():
+ parser = argparse.ArgumentParser(description=__doc__)
+ parser.add_argument('--flat', type=Path, required=True)
+ parser.add_argument('--copy', type=Path, required=True)
+ parser.add_argument('--paged', type=Path, required=True)
+ parser.add_argument('--scratch-parent', type=Path, required=True)
+ parser.add_argument('--trials', type=int, default=18)
+ parser.add_argument('--output', type=Path, required=True)
+ args = parser.parse_args()
+ if args.trials < 3 or args.trials % 3 or args.output.exists():
+ parser.error('trials must be a positive multiple of three and output must be new')
+ variants = {'flat': args.flat.resolve(), 'copy': args.copy.resolve(),
+ 'paged': args.paged.resolve()}
+ for binary in variants.values():
+ if not binary.is_file():
+ parser.error(f'missing binary: {binary}')
+ args.scratch_parent.mkdir(parents=True, exist_ok=True)
+ with tempfile.TemporaryDirectory(prefix='pure-storage-timing-',
+ dir=args.scratch_parent.resolve()) as name:
+ root = Path(name)
+ (root / 'initial').write_bytes(INITIAL)
+ (root / 'replacement').write_bytes(b'Z')
+ filesystem = subprocess.run(['findmnt', '-T', str(root), '-no', 'FSTYPE,SOURCE'],
+ capture_output=True, text=True, check=True).stdout.strip()
+ samples = {name: [] for name in variants}
+ names = list(variants)
+ for trial in range(args.trials):
+ for name in names[trial % 3:] + names[:trial % 3]:
+ sample = one(variants[name], root / f'{name}-{trial}',
+ root / 'initial', root / 'replacement')
+ samples[name].append({'trial': trial, **sample})
+ result = {'profile': 'native-cli-durable-one-mib-document-last-byte-edit',
+ 'platform': platform.platform(), 'filesystem': filesystem,
+ 'trials_per_variant': args.trials,
+ 'final_body_sha256': hashlib.sha256(EXPECTED).hexdigest(),
+ 'scope': ('Each sample starts a new CLI process; create and edit each '
+ 'include program parsing, store initialization, file and '
+ 'directory synchronization. Show verifies the final revision '
+ 'but is outside the timed operations. The host page cache, '
+ 'filesystem and device remain uncontrolled.'),
+ 'variants': {name: {'binary_sha256': hashlib.sha256(binary.read_bytes()).hexdigest(),
+ 'median_create_ms': round(statistics.median(
+ row['create_ms'] for row in samples[name]), 3),
+ 'median_edit_ms': round(statistics.median(
+ row['edit_ms'] for row in samples[name]), 3),
+ 'samples': samples[name]}
+ for name, binary in variants.items()}}
+ args.output.parent.mkdir(parents=True, exist_ok=True)
+ args.output.write_text(json.dumps(result, indent=2) + '\n')
+ print(json.dumps({name: {key: value for key, value in entry.items()
+ if key.startswith('median_')}
+ for name, entry in result['variants'].items()}, indent=2))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/research/experiments/005-storage-edit-latency/paged-memory.json b/research/experiments/005-storage-edit-latency/paged-memory.json
new file mode 100644
index 0000000..a9bef53
--- /dev/null
+++ b/research/experiments/005-storage-edit-latency/paged-memory.json
@@ -0,0 +1,43 @@
+{
+ "passed": true,
+ "failures": [],
+ "profile": "portable-measurement-no-budget",
+ "variant": "source-program",
+ "scope": "Child-process peak RSS for native CLI document creation, read and one-byte revision edit; includes libc and the selected program form, excludes filesystem cache, kernel buffers, TLS, GUI and other apps. No cross-platform memory ceiling is asserted.",
+ "samples": {
+ "small_edit_rss_kib": 1088,
+ "one_mib_edit_rss_kib": 2112,
+ "one_mib_show_rss_kib": 1088,
+ "one_mib_splice_rss_kib": 1056
+ },
+ "raw_samples": {
+ "small_edit_rss_kib": {
+ "exit_code": 0,
+ "max_rss_kib": 1088,
+ "raw_max_rss": 1088,
+ "raw_unit": "KiB"
+ },
+ "one_mib_edit_rss_kib": {
+ "exit_code": 0,
+ "max_rss_kib": 2112,
+ "raw_max_rss": 2112,
+ "raw_unit": "KiB"
+ },
+ "one_mib_show_rss_kib": {
+ "exit_code": 0,
+ "max_rss_kib": 1088,
+ "raw_max_rss": 1088,
+ "raw_unit": "KiB"
+ },
+ "one_mib_splice_rss_kib": {
+ "exit_code": 0,
+ "max_rss_kib": 1056,
+ "raw_max_rss": 1056,
+ "raw_unit": "KiB"
+ }
+ },
+ "budget_sha256": null,
+ "binary_sha256": "e1c12a2f5e736bb86737ceff7849fe95b1a9d13136e8c5eb3f3398cb3971c6f5",
+ "binary_bytes": 200704,
+ "platform": "Linux-6.18.39+rpt-rpi-2712-aarch64-with-glibc2.41"
+}
diff --git a/research/experiments/005-storage-edit-latency/sequence.py b/research/experiments/005-storage-edit-latency/sequence.py
new file mode 100644
index 0000000..57ff835
--- /dev/null
+++ b/research/experiments/005-storage-edit-latency/sequence.py
@@ -0,0 +1,85 @@
+#!/usr/bin/env python3
+"""Measure repeated durable edits and page-segment rebuilds on one real document."""
+import argparse
+import hashlib
+import json
+from pathlib import Path
+import platform
+import statistics
+import subprocess
+import tempfile
+
+from measure import INITIAL, PROGRAM, invoke
+
+
+def main():
+ parser = argparse.ArgumentParser(description=__doc__)
+ parser.add_argument('--flat', type=Path, required=True)
+ parser.add_argument('--paged', type=Path, required=True)
+ parser.add_argument('--scratch-parent', type=Path, required=True)
+ parser.add_argument('--edits', type=int, default=70)
+ parser.add_argument('--output', type=Path, required=True)
+ args = parser.parse_args()
+ if args.edits < 66 or args.output.exists():
+ parser.error('at least 66 edits are needed to observe the current rebuild boundary; output must be new')
+ binaries = {'flat': args.flat.resolve(), 'paged': args.paged.resolve()}
+ args.scratch_parent.mkdir(parents=True, exist_ok=True)
+ with tempfile.TemporaryDirectory(prefix='pure-storage-sequence-',
+ dir=args.scratch_parent.resolve()) as name:
+ root = Path(name)
+ filesystem = subprocess.run(['findmnt', '-T', str(root), '-no', 'FSTYPE,SOURCE'],
+ capture_output=True, text=True, check=True).stdout.strip()
+ (root / 'initial').write_bytes(INITIAL)
+ patches = {'Y': root / 'Y', 'Z': root / 'Z'}
+ for label, file in patches.items():
+ file.write_bytes(label.encode())
+ bases = {variant: [binary, '--program', PROGRAM, '--store', root / variant]
+ for variant, binary in binaries.items()}
+ for base in bases.values():
+ result, _ = invoke([*base, 'edit', 'document', 0, 0, 0, root / 'initial'])
+ assert json.loads(result.stdout)['revision'] == '1'
+ samples = {variant: [] for variant in binaries}
+ for edit in range(1, args.edits + 1):
+ patch = 'Z' if edit % 2 else 'Y'
+ order = ('flat', 'paged') if edit % 2 else ('paged', 'flat')
+ for variant in order:
+ base = bases[variant]
+ result, elapsed = invoke([*base, 'edit', 'document', edit,
+ len(INITIAL) - 1, 1, patches[patch]])
+ assert json.loads(result.stdout)['revision'] == str(edit + 1)
+ files = [file for file in (root / variant).iterdir() if file.is_file()]
+ samples[variant].append({'edit': edit, 'elapsed_ms': round(elapsed, 3),
+ 'files': len(files),
+ 'allocated_bytes': sum(file.stat().st_blocks * 512
+ for file in files)})
+ expected = INITIAL[:-1] + patches[patch].read_bytes()
+ for base in bases.values():
+ shown, _ = invoke([*base, 'show', 'document'])
+ assert shown.stdout == expected
+ assert json.loads(shown.stderr)['revision'] == str(args.edits + 1)
+ result = {'profile': 'repeated-durable-one-mib-document-last-byte-edit',
+ 'platform': platform.platform(), 'filesystem': filesystem,
+ 'edits_per_variant': args.edits,
+ 'final_body_sha256': hashlib.sha256(expected).hexdigest(),
+ 'scope': ('Same actual source-loaded CLI and editor.pure, two persistent '
+ 'stores, alternating operation order. Timings include each '
+ 'edit process, parser, store init and fsync; creation and '
+ 'final show are not timed. Device state is uncontrolled.'),
+ 'variants': {variant: {
+ 'binary_sha256': hashlib.sha256(binary.read_bytes()).hexdigest(),
+ 'median_edit_ms': round(statistics.median(row['elapsed_ms']
+ for row in samples[variant]), 3),
+ 'samples': samples[variant]}
+ for variant, binary in binaries.items()}}
+ args.output.parent.mkdir(parents=True, exist_ok=True)
+ args.output.write_text(json.dumps(result, indent=2) + '\n')
+ print(json.dumps({variant: {'median_edit_ms': row['median_edit_ms'],
+ 'max_edit_ms': max(sample['elapsed_ms']
+ for sample in row['samples']),
+ 'max_allocated_bytes': max(sample['allocated_bytes']
+ for sample in row['samples'])}
+ for variant, row in result['variants'].items()}, indent=2))
+
+
+if __name__ == '__main__':
+ main()