Something went wrong. Try again.
A public mirror for the whole atmosphere hubble.microcosm.blue
Something went wrong. Try again.
45 kB · 1101 lines
HTML
at backup
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><title>rocks space efficiency</title><script src="https://cdn.plot.ly/plotly-2.35.2.min.js"></script><style> * { box-sizing: border-box; margin: 0; padding: 0; } body { font-family: system-ui, -apple-system, sans-serif; background: #f5f5f5; color: #222; padding: 20px; } h1 { margin-bottom: 10px; } .subtitle { color: #666; margin-bottom: 20px; } .chart-container { background: #fff; border-radius: 8px; padding: 16px; margin-bottom: 20px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); } .chart-container h2 { font-size: 16px; margin-bottom: 8px; } .chart-row { display: flex; gap: 20px; flex-wrap: wrap; } .chart-row > .chart-container { flex: 1; min-width: 500px; } table { border-collapse: collapse; width: 100%; font-size: 13px; } th, td { text-align: right; padding: 4px 10px; border-bottom: 1px solid #e0e0e0; } th { background: #f0f0f0; cursor: pointer; user-select: none; position: sticky; top: 0; } th:hover { background: #e0e0e0; } td:first-child, th:first-child { text-align: left; } tr:hover td { background: #f8f8ff; } .table-wrap { max-height: 500px; overflow-y: auto; } .filters { display: flex; gap: 16px; flex-wrap: wrap; margin-bottom: 16px; align-items: center; } .filters label { font-size: 13px; font-weight: 600; } .filters select { font-size: 13px; padding: 2px 6px; } .zero-toggle { float: right; font-size: 12px; font-weight: normal; color: #666; cursor: pointer; user-select: none; } .zero-toggle input { vertical-align: middle; cursor: pointer; } .dl-btn { float: right; font-size: 12px; padding: 2px 10px; cursor: pointer; border: 1px solid #ccc; border-radius: 4px; background: #f8f8f8; color: #444; } .dl-btn:hover { background: #e8e8e8; }</style></head><body>
<h1>rocks records space efficiency</h1><p class="subtitle">atproto records: <span id="total-keys"></span> keys across <span id="total-accounts"></span> accounts. <span id="config-count"></span> rocksdb configs measured in <span id="total-time"></span>.</p>
<div class="chart-row"> <div class="chart-container"> <h2>value size distribution</h2> <div id="chart-value-hist"></div> </div> <div class="chart-container"> <h2>keys per account distribution</h2> <div id="chart-account-hist"></div> </div></div>
<div class="chart-row"> <div class="chart-container"> <h2>size vs block size (dict 16x training) <label class="zero-toggle"><input type="checkbox" data-chart="chart-block-size"> y from 0</label></h2> <div id="chart-block-size"></div> </div> <div class="chart-container"> <h2>size vs block size (dict 128x training) <label class="zero-toggle"><input type="checkbox" data-chart="chart-block-size-128"> y from 0</label></h2> <div id="chart-block-size-128"></div> </div></div>
<div class="chart-row"> <div class="chart-container"> <h2>total size vs compaction time <label class="zero-toggle"><input type="checkbox" data-chart="chart-pareto"> y from 0</label></h2> <div id="chart-pareto"></div> </div> <div class="chart-container"> <h2>dictionary training by block size <label class="zero-toggle"><input type="checkbox" data-chart="chart-dict"> y from 0</label></h2> <div id="chart-dict"></div> </div></div>
<div class="chart-row"> <div class="chart-container"> <h2>dictionary benefit vs block size <label class="zero-toggle"><input type="checkbox" data-chart="chart-dict-benefit"> y from 0</label></h2> <div id="chart-dict-benefit"></div> </div> <div class="chart-container"> <h2>dictionary: size reduction vs compaction time <label class="zero-toggle"><input type="checkbox" data-chart="chart-dict-cost"> y from 0</label></h2> <div id="chart-dict-cost"></div> </div></div>
<div class="chart-row"> <div class="chart-container"> <h2>32k blocks: zstd level × dictionary <label class="zero-toggle"><input type="checkbox" data-chart="chart-32k"> y from 0</label></h2> <div id="chart-32k"></div> </div> <div class="chart-container"> <h2>key restart interval <label class="zero-toggle"><input type="checkbox" data-chart="chart-restart"> y from 0</label></h2> <div id="chart-restart"></div> </div></div>
<div class="chart-row"> <div class="chart-container"> <h2>subcompactions: size <label class="zero-toggle"><input type="checkbox" data-chart="chart-subcomp-size"> y from 0</label></h2> <div id="chart-subcomp-size"></div> </div> <div class="chart-container"> <h2>subcompactions: time <label class="zero-toggle"><input type="checkbox" data-chart="chart-subcomp-time"> y from 0</label></h2> <div id="chart-subcomp-time"></div> </div></div>
<div class="chart-container"> <h2>all data <button class="dl-btn" id="download-csv">download csv</button></h2> <div class="filters"> <label>block size: <select id="f-block"><option value="">all</option></select></label> <label>zstd level: <select id="f-level"><option value="">all</option></select></label> <label>dict bytes: <select id="f-dict"><option value="">all</option></select></label> </div> <div class="table-wrap"> <table id="results-table"> <thead><tr> <th data-col="block_size">block_size</th> <th data-col="zstd_level">zstd_level</th> <th data-col="restart_interval">restart_int</th> <th data-col="dict_bytes">dict_bytes</th> <th data-col="train_mult">train_mult</th> <th data-col="opt_filters">filters</th> <th data-col="subcompactions">subcomp</th> <th data-col="size_mib">size_gib</th> <th data-col="compact_secs">compact_s</th> </tr></thead> <tbody id="results-body"></tbody> </table> </div></div>
<script>// ── embed CSV data ────────────────────────────────────────────────const csvText = `block_size,zstd_level,restart_interval,dict_bytes,train_mult,opt_filters,subcompactions,size_bytes,size_mib,compact_secs4096,0,8,0,,true,10,31395311799,29940,19.84096,1,8,0,,true,10,22116416091,21091,30.74096,1,8,16384,16,true,10,20659329178,19702,42.54096,3,4,0,,true,10,22374836755,21338,51.44096,3,8,0,,true,10,22100643559,21076,41.74096,3,8,1024,,true,10,21177970088,20196,57.04096,3,8,1024,4,true,10,22100668085,21076,42.24096,3,8,1024,8,true,10,22100676471,21076,43.24096,3,8,1024,16,true,10,22100669290,21076,42.94096,3,8,1024,32,true,10,21526349249,20529,55.14096,3,8,1024,64,true,10,21501054074,20505,60.04096,3,8,2048,,true,10,21184609495,20203,59.84096,3,8,2048,4,true,10,22100673247,21076,42.14096,3,8,2048,8,true,10,22100621663,21076,41.24096,3,8,2048,16,true,10,20635793718,19679,52.04096,3,8,2048,32,true,10,20562444599,19609,58.24096,3,8,2048,64,true,10,20508962586,19558,57.64096,3,8,4096,,true,10,21128768715,20149,61.04096,3,8,4096,4,true,10,22100685331,21076,41.44096,3,8,4096,8,true,10,20577552594,19624,51.94096,3,8,4096,16,true,10,20479709404,19530,60.24096,3,8,4096,32,true,10,20403721320,19458,60.94096,3,8,4096,64,true,10,20368652511,19425,62.74096,3,8,8192,,true,10,21057872538,20082,62.94096,3,8,8192,4,true,10,20632375521,19676,54.14096,3,8,8192,8,true,10,20506735960,19556,61.24096,3,8,8192,16,true,10,20390435178,19445,62.14096,3,8,8192,32,true,10,20363765340,19420,61.94096,3,8,8192,64,true,10,20331974936,19390,61.64096,3,8,16384,,true,10,21083190741,20106,65.04096,3,8,16384,4,true,10,20579253867,19625,54.24096,3,8,16384,8,true,10,20455471802,19507,61.64096,3,8,16384,16,true,10,20405638509,19460,63.14096,3,8,16384,32,true,10,20386866978,19442,65.34096,3,8,16384,64,true,10,20367953764,19424,67.54096,3,8,65536,,true,10,20893557686,19925,63.94096,3,8,65536,4,true,10,20409555824,19464,56.44096,3,8,65536,8,true,10,20345337995,19402,64.14096,3,8,65536,16,true,10,20298445731,19358,65.14096,3,8,65536,32,true,10,20261884271,19323,69.04096,3,8,65536,64,true,10,20239324109,19301,74.34096,3,16,0,,true,10,21909626509,20894,48.04096,3,32,0,,true,10,21823083686,20812,39.54096,3,64,0,,true,10,21823091636,20812,40.44096,6,8,0,,true,10,21750720790,20743,65.24096,6,8,16384,16,true,10,20146634422,19213,84.14096,9,8,0,,true,10,21746294515,20738,111.84096,9,8,16384,16,true,10,19849302063,18929,116.68192,0,8,0,,true,10,29379196929,28018,35.08192,1,8,0,,true,10,20005967746,19079,25.58192,1,8,16384,16,true,10,19572338449,18665,34.08192,3,8,0,,false,10,20031898121,19103,36.18192,3,8,0,,true,10,20031920603,19103,31.98192,3,8,1024,,true,10,19622953913,18713,48.98192,3,8,1024,4,true,10,20031917101,19103,33.88192,3,8,1024,8,true,10,20031918966,19103,33.58192,3,8,1024,16,true,10,20031904492,19103,33.58192,3,8,1024,32,true,10,20031915783,19103,33.18192,3,8,1024,64,true,10,20182475006,19247,50.18192,3,8,2048,,true,10,19620130060,18711,52.08192,3,8,2048,4,true,10,20031954949,19103,35.78192,3,8,2048,8,true,10,20031939452,19103,33.78192,3,8,2048,16,true,10,20031949661,19103,33.48192,3,8,2048,32,true,10,20143626622,19210,47.88192,3,8,2048,64,true,10,20120486239,19188,52.38192,3,8,4096,,true,10,19635342683,18725,48.98192,3,8,4096,4,true,10,20031954812,19103,33.28192,3,8,4096,8,true,10,20031942177,19103,33.38192,3,8,4096,16,true,10,19372492949,18475,47.68192,3,8,4096,32,true,10,19313920339,18419,50.48192,3,8,4096,64,true,10,19282505585,18389,52.18192,3,8,8192,,true,10,19635635166,18726,50.98192,3,8,8192,4,true,10,20031928773,19103,35.08192,3,8,8192,8,true,10,19410703858,18511,46.38192,3,8,8192,16,true,10,19341160203,18445,50.98192,3,8,8192,32,true,10,19296132462,18402,50.98192,3,8,8192,64,true,10,19273631354,18380,51.88192,3,8,16384,,true,10,19655701449,18745,54.38192,3,8,16384,4,true,10,19502525484,18599,50.58192,3,8,16384,8,true,10,19419249737,18519,51.38192,3,8,16384,16,true,10,19358437498,18461,52.48192,3,8,16384,32,true,10,19340825956,18444,53.18192,3,8,16384,64,true,10,19329911907,18434,54.78192,3,8,65536,,true,10,19654121838,18743,52.68192,3,8,65536,4,true,10,19436643569,18536,50.18192,3,8,65536,8,true,10,19376580371,18478,54.58192,3,8,65536,16,true,10,19337019622,18441,55.68192,3,8,65536,32,true,10,19318086405,18423,59.08192,3,8,65536,64,true,10,19305582440,18411,64.88192,6,8,0,,true,10,19702719743,18789,62.28192,6,8,16384,16,true,10,19065986792,18182,74.28192,9,8,0,,true,10,19693493308,18781,100.98192,9,8,16384,16,true,10,18747260904,17878,100.716384,0,8,0,,true,10,28042902316,26743,26.016384,1,8,0,,true,10,18751959286,17883,22.916384,1,8,16384,16,true,10,18714866778,17847,30.216384,3,8,0,,true,1,18829928785,17957,174.216384,3,8,0,,true,4,18829973330,17957,44.116384,3,8,0,,true,10,18830038815,17957,27.816384,3,8,16384,16,true,10,18615498021,17753,43.516384,6,8,0,,true,10,18431835527,17577,57.916384,6,8,16384,16,true,10,18267813872,17421,69.316384,9,8,0,,true,10,18408979927,17556,109.116384,9,8,16384,16,true,10,17956014400,17124,95.632768,0,8,0,,true,10,27149041457,25891,22.332768,1,8,0,,true,10,18270567659,17424,21.632768,1,8,16384,16,true,10,18211125411,17367,27.532768,3,4,0,,true,10,18324784539,17475,28.532768,3,8,0,,false,10,18101433642,17262,27.532768,3,8,0,,true,10,18101394905,17262,26.532768,3,8,16384,16,true,10,18076847819,17239,46.532768,3,16,0,,true,10,17971384889,17138,28.132768,3,32,0,,true,10,17900644184,17071,27.132768,3,64,0,,true,10,17859565644,17032,29.232768,6,8,0,,true,10,17875945091,17047,56.932768,6,8,16384,16,true,10,17743235923,16921,72.532768,9,8,0,,true,10,17672322648,16853,76.032768,9,8,16384,16,true,10,17455398843,16646,96.065536,0,8,0,,true,10,26622152522,25388,21.265536,1,8,0,,true,10,17829479045,17003,21.065536,1,8,16384,16,true,10,17829619661,17003,21.465536,3,8,0,,true,10,17676395890,16857,25.565536,3,8,16384,16,true,10,17676353730,16857,26.465536,6,8,0,,true,10,17445895583,16637,53.665536,6,8,16384,16,true,10,17445851139,16637,54.665536,9,8,0,,true,10,17228573735,16430,73.165536,9,8,16384,16,true,10,17228513145,16430,73.9131072,0,8,0,,true,10,26362266962,25141,19.5131072,1,8,0,,true,10,17595617600,16780,20.4131072,1,8,16384,16,true,10,17595914003,16780,20.0131072,3,8,0,,true,10,17381327820,16576,24.9131072,3,8,16384,16,true,10,17381302914,16576,25.5131072,6,8,0,,true,10,17164905178,16369,54.9131072,6,8,16384,16,true,10,17165045658,16369,56.9131072,9,8,0,,true,10,16940090482,16155,76.5131072,9,8,16384,16,true,10,16940006516,16155,76.716384,3,8,16384,64,true,10,18575595180,17715,46.816384,3,8,65536,4,true,10,18720555347,17853,49.616384,3,8,65536,16,true,10,18636700580,17773,52.116384,3,8,262144,1,true,10,20116140943,19184,62.116384,3,8,262144,4,true,10,18718065045,17850,62.132768,3,8,16384,64,true,10,18037187627,17201,55.532768,3,8,65536,4,true,10,18160440220,17319,61.132768,3,8,65536,16,true,10,18108588895,17269,56.732768,3,8,262144,1,true,10,18702653174,17836,59.632768,3,8,262144,4,true,10,18174889606,17332,68.065536,3,8,16384,64,true,10,17666766140,16848,48.365536,3,8,65536,4,true,10,17676764811,16857,27.265536,3,8,65536,16,true,10,17718370930,16897,51.065536,3,8,262144,1,true,10,17676607265,16857,27.565536,3,8,262144,4,true,10,17806456807,16981,65.232768,1,8,16384,,true,10,18145390797,17304,28.832768,1,8,16384,64,true,10,18162942166,17321,28.732768,1,8,65536,,true,10,18113630667,17274,25.432768,1,8,65536,4,true,10,18252364775,17406,25.532768,1,8,65536,16,true,10,18191268324,17348,27.332768,1,8,262144,,true,10,18433074184,17579,25.832768,1,8,262144,1,true,10,18941076049,18063,30.432768,1,8,262144,4,true,10,18630362351,17767,32.432768,3,8,16384,,true,10,18092158457,17254,43.332768,3,8,65536,,true,10,18175762988,17333,50.132768,3,8,262144,,true,10,18237799128,17392,58.532768,6,8,16384,,true,10,17815254359,16989,67.732768,6,8,16384,64,true,10,17700896109,16880,71.032768,6,8,65536,,true,10,17903486595,17074,74.432768,6,8,65536,4,true,10,17835381202,17009,75.632768,6,8,65536,16,true,10,17792150430,16967,79.532768,6,8,262144,,true,10,17617765307,16801,82.032768,6,8,262144,1,true,10,17722853142,16901,85.732768,6,8,262144,4,true,10,17519172262,16707,89.432768,9,8,16384,,true,10,17542440248,16729,93.632768,9,8,16384,64,true,10,17402812286,16596,96.132768,9,8,65536,,true,10,17621780999,16805,103.532768,9,8,65536,4,true,10,17560386951,16746,106.332768,9,8,65536,16,true,10,17513473140,16702,110.732768,9,8,262144,,true,10,17288349711,16487,114.832768,9,8,262144,1,true,10,17736615600,16914,134.232768,9,8,262144,4,true,10,17193958194,16397,126.532768,6,8,16384,128,true,10,17685606257,16866,73.332768,6,8,32768,,true,10,17851433056,17024,69.432768,6,8,32768,16,true,10,17756182902,16933,72.232768,6,8,32768,64,true,10,17701788554,16881,77.032768,6,8,32768,128,true,10,17677204659,16858,80.832768,9,8,16384,128,true,10,17377098336,16572,100.332768,9,8,32768,,true,10,17573743202,16759,97.532768,9,8,32768,16,true,10,17464617794,16655,101.332768,9,8,32768,64,true,10,17397585233,16591,109.232768,9,8,32768,128,true,10,17363679655,16559,114.332768,1,8,262144,16,true,10,18553175636,17693,37.332768,1,8,262144,64,true,10,18447643117,17593,74.432768,1,8,262144,128,true,10,18409274568,17556,119.132768,3,8,262144,16,true,10,18086292553,17248,69.432768,3,8,262144,64,true,10,18006378746,17172,99.832768,3,8,262144,128,true,10,17969550364,17137,143.832768,6,8,262144,16,true,10,17429786477,16622,94.532768,6,8,262144,64,true,10,17327494633,16524,125.332768,6,8,262144,128,true,10,17278557537,16478,167.632768,9,8,262144,16,true,10,17083071008,16291,126.332768,9,8,262144,64,true,10,16952497747,16167,156.132768,9,8,262144,128,true,10,16895545232,16112,193.032768,-1,8,0,,true,10,59684919588,56919,37.332768,6,8,262144,256,true,10,16990198351,16203,401.232768,6,8,262144,512,true,10,17248512734,16449,234.532768,9,8,262144,256,true,10,16867564848,16086,255.432768,9,8,262144,512,true,10,16864619297,16083,256.265536,6,8,32768,128,true,10,17321994366,16519,72.34096,-4,8,0,,true,10,31276586169,29827,26.64096,-4,8,16384,16,true,10,29413605853,28051,28.68192,-4,8,0,,true,10,28921758953,27581,30.68192,-4,8,16384,16,true,10,28356639273,27042,25.616384,-4,8,0,,true,10,27438625209,26167,34.716384,-4,8,16384,16,true,10,27044724061,25791,30.032768,-4,8,0,,true,10,26524603860,25295,30.532768,-4,8,16384,16,true,10,26460703769,25234,33.265536,-4,8,0,,true,10,25888386027,24689,42.665536,-4,8,16384,16,true,10,25888422548,24689,33.5131072,-4,8,0,,true,10,25522283836,24339,42.9131072,-4,8,16384,16,true,10,25522150199,24339,34.24096,-111,8,0,,true,10,29308724103,27950,24.34096,-111,8,16384,16,true,10,27024424562,25772,31.08192,-111,8,0,,true,10,27075213921,25820,32.28192,-111,8,16384,16,true,10,25995904404,24791,27.116384,-111,8,0,,true,10,25659940368,24471,25.316384,-111,8,16384,16,true,10,25228081361,24059,31.832768,-111,8,0,,true,10,24816588205,23666,25.032768,-111,8,16384,16,true,10,24648848645,23506,31.565536,-111,8,0,,true,10,24275747176,23151,32.365536,-111,8,16384,16,true,10,24275932198,23151,31.1131072,-111,8,0,,true,10,24001488454,22889,34.7131072,-111,8,16384,16,true,10,24001621656,22889,37.44096,-111,8,16384,128,true,10,26892815796,25646,33.64096,-4,8,16384,128,true,10,29228607770,27874,42.44096,1,8,16384,128,true,10,20615555450,19660,48.94096,3,8,16384,128,true,10,20312133018,19371,68.04096,6,8,16384,128,true,10,20047865519,19119,101.94096,9,8,16384,128,true,10,19708601087,18795,135.18192,-111,8,16384,128,true,10,25884847544,24685,48.58192,-4,8,16384,128,true,10,28173367997,26868,35.68192,1,8,16384,128,true,10,19509626714,18605,41.78192,3,8,16384,128,true,10,19280054013,18386,60.28192,6,8,16384,128,true,10,18973271175,18094,91.58192,9,8,16384,128,true,10,18629849709,17766,122.516384,-111,8,16384,128,true,10,25116640949,23953,40.216384,-4,8,16384,128,true,10,26864490525,25619,32.916384,1,8,16384,128,true,10,18627072819,17764,36.516384,3,8,16384,128,true,10,18516984774,17659,55.716384,6,8,16384,128,true,10,18165439350,17323,86.716384,9,8,16384,128,true,10,17835053753,17008,116.032768,-111,8,16384,128,true,10,24559256856,23421,34.232768,-4,8,16384,128,true,10,26318803231,25099,30.432768,1,8,16384,128,true,10,18112286602,17273,32.832768,3,8,16384,128,true,10,17974807121,17142,55.865536,-111,8,16384,128,true,10,24119630567,23002,29.465536,-4,8,16384,128,true,10,25663379471,24474,27.765536,1,8,16384,128,true,10,17668150303,16849,29.565536,3,8,16384,128,true,10,17604691252,16789,52.265536,6,8,16384,128,true,10,17290142420,16489,70.765536,9,8,16384,128,true,10,17020107078,16231,97.1131072,-111,8,16384,128,true,10,23923317959,22815,29.6131072,-4,8,16384,128,true,10,25397783168,24221,27.2131072,1,8,16384,128,true,10,17494575402,16684,29.0131072,3,8,16384,128,true,10,17343708103,16540,52.7131072,6,8,16384,128,true,10,17060462497,16270,71.3131072,9,8,16384,128,true,10,16805981961,16027,96.0`;
// ── parse ─────────────────────────────────────────────────────────function parseCSV(text) { const lines = text.trim().split('\n'); const headers = lines[0].split(','); return lines.slice(1).map(line => { const vals = line.split(','); const row = {}; headers.forEach((h, i) => { const v = vals[i]; if (h === 'opt_filters') row[h] = v === 'true'; else if (h === 'train_mult') row[h] = v === '' ? null : +v; else row[h] = +v; }); return row; });}
const data = parseCSV(csvText);document.getElementById('config-count').textContent = data.length;const totalSecs = data.reduce((s, d) => s + d.compact_secs, 0);const totalMins = (totalSecs / 60).toFixed(1);document.getElementById('total-time').textContent = `${totalMins}min`;
// ── data profile stats ───────────────────────────────────────────const stats = { "total_keys": 220118913, "total_accounts": 155536, "value_size_hist": [ [16, 32, 62], [32, 64, 59915], [64, 128, 24326635], [128, 256, 160003952], [256, 512, 27257670], [512, 1024, 7251053], [1024, 2048, 1189568], [2048, 4096, 28324], [4096, 8192, 1495], [8192, 16384, 188], [16384, 32768, 37], [32768, 65536, 11], [65536, 131072, 2], [131072, 262144, 1] ], "keys_per_account_hist": [ [1, 2, 5192], [2, 4, 16553], [4, 8, 13913], [8, 16, 13736], [16, 32, 15006], [32, 64, 15720], [64, 128, 15330], [128, 256, 14137], [256, 512, 12703], [512, 1024, 10464], [1024, 2048, 8027], [2048, 4096, 5631], [4096, 8192, 3899], [8192, 16384, 2536], [16384, 32768, 1463], [32768, 65536, 789], [65536, 131072, 322], [131072, 262144, 92], [262144, 524288, 20], [524288, 1048576, 3] ]};
const fmtNum = n => n.toLocaleString();document.getElementById('total-keys').textContent = fmtNum(stats.total_keys);document.getElementById('total-accounts').textContent = fmtNum(stats.total_accounts);
// ── helpers ───────────────────────────────────────────────────────const gib = d => +(d.size_mib / 1024).toFixed(2);const levelName = l => l === -111 ? 'l-1' : l === -1 ? 'none' : l === 0 ? 'lz4' : `z${l}`;const levelColors = { '-111': '#f90', '-1': '#bbb', '-4': '#26de81', 0: '#888', 1: '#4ecdc4', 3: '#45b7d1', 6: '#f7b731', 9: '#fc5c65' };
const plotLayout = (opts = {}) => ({ margin: { t: 30, b: 50, l: 60, r: 20 }, hovermode: 'closest', font: { family: 'system-ui', size: 12 }, ...opts,});
function groupBy(arr, keyFn) { const groups = {}; arr.forEach(d => { const k = keyFn(d); (groups[k] = groups[k] || []).push(d); }); return groups;}
// ── chart 1: size vs block size (with level slider) ──────────────function chartBlockSize(elementId, trainMult) { // filter to standard configs: ri=8, filters=on, subcomp=10 const base = data.filter(d => d.restart_interval === 8 && d.opt_filters && d.subcompactions === 10 && ((d.dict_bytes === 0 && d.train_mult === null) || (d.dict_bytes === 16384 && d.train_mult === trainMult)) );
const levels = [...new Set(base.map(d => d.zstd_level))].sort((a, b) => a - b);
// build all traces (one per level × dict combo) const traces = []; const traceLevel = []; // which level each trace belongs to for (const level of levels) { for (const dict of [0, 16384]) { const subset = base.filter(d => d.zstd_level === level && d.dict_bytes === dict); if (subset.length === 0) continue; subset.sort((a, b) => a.block_size - b.block_size); traces.push({ x: subset.map(d => d.block_size / 1024 + 'k'), y: subset.map(gib), name: `${levelName(level)} ${dict > 0 ? 'dict=16k' : 'no dict'}`, mode: 'lines+markers', line: { dash: dict > 0 ? 'dash' : 'solid', color: levelColors[level] || '#888', width: 2.5 }, marker: { symbol: dict > 0 ? 'diamond' : 'circle', size: 8 }, text: subset.map(d => `${d.compact_secs}s`), hovertemplate: '%{x} block, ' + levelName(level) + (dict > 0 ? ' +dict' : '') + '<br>%{y:.2f} GiB<br>%{text}<extra></extra>', }); traceLevel.push(level); } }
// per-level steps: highlight one, fade the rest const steps = [{ label: 'all', method: 'restyle', args: [{ 'line.color': traceLevel.map(l => levelColors[l] || '#888'), 'line.width': traceLevel.map(() => 2.5), 'marker.opacity': traceLevel.map(() => 1), 'marker.size': traceLevel.map(() => 8), }], }];
for (const hl of levels) { steps.push({ label: levelName(hl), method: 'restyle', args: [{ 'line.color': traceLevel.map(l => l === hl ? levelColors[l] : (levelColors[l] || '#888') + '30'), 'line.width': traceLevel.map(l => l === hl ? 3.5 : 1), 'marker.opacity': traceLevel.map(l => l === hl ? 1 : 0.2), 'marker.size': traceLevel.map(l => l === hl ? 10 : 5), }], }); }
Plotly.newPlot(elementId, traces, plotLayout({ xaxis: { title: 'Block Size', type: 'category' }, yaxis: { title: 'Size (GiB)' }, legend: { x: 1, xanchor: 'right', y: 1 }, sliders: [{ active: 0, pad: { t: 30 }, currentvalue: { prefix: 'zstd level: ', font: { size: 13 } }, steps: steps, }], }));}
// ── chart 2: pareto (size vs time, with zstd level slider) ───────function chartPareto() { const blockColors = { 4096: '#fc5c65', 8192: '#f7b731', 16384: '#45b7d1', 32768: '#4ecdc4', 65536: '#a55eea', }; const blocks = [...new Set(data.map(d => d.block_size))].sort((a, b) => a - b); const levels = [...new Set(data.map(d => d.zstd_level))].sort((a, b) => a - b);
const traces = []; const traceLevel = [];
for (const bs of blocks) { const subset = data.filter(d => d.block_size === bs && d.subcompactions === 10); traces.push({ x: subset.map(d => d.compact_secs), y: subset.map(gib), name: (bs / 1024) + 'k', mode: 'markers', marker: { size: 7, color: subset.map(() => blockColors[bs] || '#888'), opacity: 1 }, customdata: subset.map(d => d.zstd_level), text: subset.map(d => { const parts = [levelName(d.zstd_level), `ri=${d.restart_interval}`]; if (d.dict_bytes > 0) parts.push(`dict=${d.dict_bytes}`); if (d.train_mult !== null) parts.push(`train=${d.train_mult}x`); return parts.join(', '); }), hovertemplate: '%{text}<br>%{y:.2f} GiB, %{x:.1f}s<extra>%{fullData.name} blocks</extra>', }); traceLevel.push(subset.map(d => d.zstd_level)); }
// slider: highlight by zstd level const steps = [{ label: 'all', method: 'restyle', args: [{ 'marker.opacity': traceLevel.map(levels => levels.map(() => 1)), 'marker.size': traceLevel.map(levels => levels.map(() => 7)), }], }];
for (const hl of levels) { steps.push({ label: levelName(hl), method: 'restyle', args: [{ 'marker.opacity': traceLevel.map(levels => levels.map(l => l === hl ? 1 : 0.12)), 'marker.size': traceLevel.map(levels => levels.map(l => l === hl ? 10 : 5)), }], }); }
Plotly.newPlot('chart-pareto', traces, plotLayout({ xaxis: { title: 'Compaction Time (s)' }, yaxis: { title: 'Size (GiB)' }, legend: { title: { text: 'Block Size' } }, sliders: [{ active: 0, pad: { t: 30 }, currentvalue: { prefix: 'zstd level: ', font: { size: 13 } }, steps: steps, }], }));}
// ── chart 3: dictionary training sweep (with block size slider) ───function chartDict() { const traces = []; const traceBlock = []; const trainColors = { 'None': '#45b7d1', '1': '#888', '4': '#4ecdc4', '8': '#26de81', '16': '#f7b731', '32': '#fd9644', '64': '#fc5c65' };
// find all block sizes that have dict data const blockSizes = [...new Set( data.filter(d => d.dict_bytes > 0 && d.zstd_level === 3 && d.restart_interval === 8) .map(d => d.block_size / 1024) )].sort((a, b) => a - b);
// collect all dict sizes across all block sizes for consistent x-axis const allDictSizes = [...new Set( data.filter(d => d.dict_bytes > 0 && d.zstd_level === 3 && d.restart_interval === 8) .map(d => d.dict_bytes) )].sort((a, b) => a - b); const xLabels = allDictSizes.map(d => d >= 1024 ? (d / 1024) + 'k' : d + '');
for (const blockKb of blockSizes) { const bs = blockKb * 1024; const subset = data.filter(d => d.block_size === bs && d.dict_bytes > 0 && d.zstd_level === 3 && d.restart_interval === 8 ); if (subset.length === 0) continue;
const byTrain = groupBy(subset, d => d.train_mult === null ? 'None' : String(d.train_mult));
for (const [tm, rows] of Object.entries(byTrain)) { rows.sort((a, b) => a.dict_bytes - b.dict_bytes); const label = tm === 'None' ? 'default' : tm + 'x'; traces.push({ x: rows.map(d => d.dict_bytes >= 1024 ? (d.dict_bytes / 1024) + 'k' : d.dict_bytes + ''), y: rows.map(gib), name: `${blockKb}k, train=${label}`, mode: 'lines+markers', line: { color: trainColors[tm] || '#888', width: 2.5 }, marker: { size: 7 }, hovertemplate: `${blockKb}k block, dict=%{x}, train=${label}<br>%{y:.2f} GiB<extra></extra>`, }); traceBlock.push(blockKb); }
// no-dict baseline const baseline = data.find(d => d.block_size === bs && d.dict_bytes === 0 && d.zstd_level === 3 && d.restart_interval === 8 && d.opt_filters && d.subcompactions === 10 ); if (baseline) { traces.push({ x: xLabels, y: Array(xLabels.length).fill(gib(baseline)), name: `${blockKb}k, no dict`, mode: 'lines', line: { color: '#ccc', width: 1 }, hoverinfo: 'skip', }); traceBlock.push(blockKb); } }
// slider const steps = [{ label: 'all', method: 'restyle', args: [{ 'line.width': traceBlock.map(() => 2.5), 'marker.opacity': traceBlock.map(() => 1), 'marker.size': traceBlock.map(() => 7), }], }];
for (const hl of blockSizes) { steps.push({ label: `${hl}k`, method: 'restyle', args: [{ 'line.width': traceBlock.map(b => b === hl ? 3.5 : 1), 'marker.opacity': traceBlock.map(b => b === hl ? 1 : 0.15), 'marker.size': traceBlock.map(b => b === hl ? 9 : 4), }], }); }
Plotly.newPlot('chart-dict', traces, plotLayout({ xaxis: { title: 'Dictionary Size', type: 'category' }, yaxis: { title: 'Size (GiB)' }, margin: { t: 30, b: 50, l: 60, r: 160 }, legend: { x: 1.02, xanchor: 'left', y: 1, font: { size: 10 } }, sliders: [{ active: 0, pad: { t: 30 }, currentvalue: { prefix: 'block size: ', font: { size: 13 } }, steps: steps, }], }));}
// ── chart 3b: dictionary benefit by block size ────────────────────function chartDictBenefit() { // for each block size, find no-dict baseline and best dict config const blocks = [...new Set(data.map(d => d.block_size))].sort((a, b) => a - b); const dictSizes = [16384, 65536, 262144]; const colors = { 16384: '#45b7d1', 65536: '#f7b731', 262144: '#fc5c65' };
const traces = [];
for (const dictBytes of dictSizes) { const xs = []; const ys = []; const texts = [];
for (const bs of blocks) { const baseline = data.find(d => d.block_size === bs && d.dict_bytes === 0 && d.zstd_level === 3 && d.restart_interval === 8 && d.opt_filters && d.subcompactions === 10 ); if (!baseline) continue;
// find best (smallest) result with this dict size at this block size const dictRows = data.filter(d => d.block_size === bs && d.dict_bytes === dictBytes && d.zstd_level === 3 && d.restart_interval === 8 && d.opt_filters && d.subcompactions === 10 ); if (dictRows.length === 0) continue;
const best = dictRows.reduce((a, b) => a.size_bytes < b.size_bytes ? a : b); const pct = ((baseline.size_bytes - best.size_bytes) / baseline.size_bytes * 100);
xs.push(bs / 1024 + 'k'); ys.push(+pct.toFixed(2)); const trainLabel = best.train_mult === null ? 'default' : best.train_mult + 'x'; texts.push(`train=${trainLabel}`); }
if (xs.length > 0) { const dictLabel = dictBytes >= 1024 ? (dictBytes / 1024) + 'k' : dictBytes + ''; traces.push({ x: xs, y: ys, name: `dict=${dictLabel}`, mode: 'lines+markers', line: { color: colors[dictBytes] || '#888' }, marker: { size: 8 }, text: texts, hovertemplate: `%{x} block, dict=${dictLabel}<br>%{y:.1f}% smaller<br>%{text}<extra></extra>`, }); } }
Plotly.newPlot('chart-dict-benefit', traces, plotLayout({ xaxis: { title: 'Block Size', type: 'category' }, yaxis: { title: 'Size Reduction (%)' }, }));}
// ── chart 3c: dictionary cost (size reduction vs compaction time) ─function chartDictCost() { const blockColors = { 4096: '#fc5c65', 8192: '#f7b731', 16384: '#45b7d1', 32768: '#4ecdc4', 65536: '#a55eea', 131072: '#888', }; const blocks = [...new Set(data.map(d => d.block_size))].sort((a, b) => a - b); const traces = [];
for (const bs of blocks) { const baseline = data.find(d => d.block_size === bs && d.dict_bytes === 0 && d.zstd_level === 3 && d.restart_interval === 8 && d.opt_filters && d.subcompactions === 10 ); if (!baseline) continue;
const dictRows = data.filter(d => d.block_size === bs && d.dict_bytes > 0 && d.zstd_level === 3 && d.restart_interval === 8 && d.opt_filters && d.subcompactions === 10 ); if (dictRows.length === 0) continue;
const bsLabel = (bs / 1024) + 'k'; traces.push({ x: dictRows.map(d => (d.compact_secs - baseline.compact_secs) / baseline.compact_secs * 100), y: dictRows.map(d => (baseline.size_bytes - d.size_bytes) / baseline.size_bytes * 100), name: bsLabel, mode: 'markers', marker: { size: 7, color: blockColors[bs] || '#888' }, text: dictRows.map(d => { const dictLabel = d.dict_bytes >= 1024 ? (d.dict_bytes / 1024) + 'k' : d.dict_bytes; const trainLabel = d.train_mult === null ? 'default' : d.train_mult + 'x'; return `dict=${dictLabel}, train=${trainLabel}`; }), hovertemplate: `${bsLabel} block<br>%{text}<br>%{y:.1f}% smaller, +%{x:.0f}% time<extra></extra>`, });
// add the no-dict origin point traces.push({ x: [0], y: [0], name: bsLabel + ' (no dict)', mode: 'markers', marker: { size: 9, color: blockColors[bs], symbol: 'x' }, showlegend: false, hovertemplate: `${bsLabel} block, no dict<extra></extra>`, }); }
Plotly.newPlot('chart-dict-cost', traces, plotLayout({ xaxis: { title: 'Extra Compaction Time (%)' }, yaxis: { title: 'Size Reduction (%)' }, legend: { title: { text: 'Block Size' } }, }));}
// ── chart: 32k block deep dive ────────────────────────────────────function chart32k() { const bs = 32768; const subset = data.filter(d => d.block_size === bs && d.zstd_level > 0 && d.restart_interval === 8 && d.opt_filters && d.subcompactions === 10 );
function dictLabel(d) { if (d.dict_bytes === 0) return 'none'; const dk = d.dict_bytes >= 1024 ? (d.dict_bytes / 1024) + 'k' : d.dict_bytes; const tm = d.train_mult === null ? 'def' : d.train_mult + 'x'; return `${dk}/${tm}`; }
// build globally sorted x-axis: none first, then by dict size, then training const allLabels = [...new Set(subset.map(dictLabel))]; allLabels.sort((a, b) => { if (a === 'none') return -1; if (b === 'none') return 1; const parseDk = s => parseInt(s) * (s.includes('k/') ? 1024 : 1); const parseTm = s => { const m = s.match(/\/(\d+)x$/); return m ? parseInt(m[1]) : -1; }; return parseDk(a) - parseDk(b) || parseTm(a) - parseTm(b); });
const levels = [...new Set(subset.map(d => d.zstd_level))].sort((a, b) => a - b); const traces = [];
for (const level of levels) { const rows = subset.filter(d => d.zstd_level === level); rows.sort((a, b) => allLabels.indexOf(dictLabel(a)) - allLabels.indexOf(dictLabel(b)));
traces.push({ x: rows.map(dictLabel), y: rows.map(gib), name: levelName(level), mode: 'lines+markers', line: { color: levelColors[level] || '#888' }, marker: { size: 7 }, text: rows.map(d => `${d.compact_secs}s`), hovertemplate: `32k, ${levelName(level)}, dict=%{x}<br>%{y:.2f} GiB<br>%{text}<extra></extra>`, }); }
Plotly.newPlot('chart-32k', traces, plotLayout({ xaxis: { title: 'Dictionary Config (size/training)', type: 'category', categoryorder: 'array', categoryarray: allLabels }, yaxis: { title: 'Size (GiB)' }, margin: { t: 30, b: 70, l: 60, r: 20 }, }));}
// ── chart 4: restart interval ─────────────────────────────────────function chartRestart() { const traces = []; const colors = { 4: '#fc5c65', 32: '#45b7d1' };
for (const blockKb of [4, 32]) { const bs = blockKb * 1024; const subset = data.filter(d => d.block_size === bs && d.dict_bytes === 0 && d.zstd_level === 3 && d.opt_filters && d.subcompactions === 10 && [4, 8, 16, 32, 64].includes(d.restart_interval) ).sort((a, b) => a.restart_interval - b.restart_interval);
if (subset.length > 0) { traces.push({ x: subset.map(d => d.restart_interval), y: subset.map(gib), name: `${blockKb}k blocks`, mode: 'lines+markers', marker: { size: 8 }, line: { color: colors[blockKb] }, hovertemplate: `${blockKb}k blocks, restart interval %{x}<br>%{y:.2f} GiB<extra></extra>`, }); } }
Plotly.newPlot('chart-restart', traces, plotLayout({ xaxis: { title: 'Key Restart Interval', type: 'category' }, yaxis: { title: 'Size (GiB)' }, legend: { x: 1, xanchor: 'right', y: 1 }, }));}
// ── chart 5: subcompactions ───────────────────────────────────────function chartSubcomp() { const subData = data.filter(d => d.block_size === 16384 && d.dict_bytes === 0 && d.zstd_level === 3 && d.restart_interval === 8 && d.opt_filters ).sort((a, b) => a.subcompactions - b.subcompactions);
if (subData.length > 0) { Plotly.newPlot('chart-subcomp-size', [{ x: subData.map(d => d.subcompactions), y: subData.map(gib), mode: 'lines+markers', marker: { size: 8 }, line: { color: '#45b7d1' }, hovertemplate: 'subcompactions=%{x}<br>%{y:.2f} GiB<extra></extra>', }], plotLayout({ xaxis: { title: 'Max Subcompactions', type: 'category' }, yaxis: { title: 'Size (GiB)' }, showlegend: false, }));
Plotly.newPlot('chart-subcomp-time', [{ x: subData.map(d => d.subcompactions), y: subData.map(d => d.compact_secs), mode: 'lines+markers', marker: { size: 8 }, line: { color: '#f7b731' }, hovertemplate: 'subcompactions=%{x}<br>%{y:.1f}s<extra></extra>', }], plotLayout({ xaxis: { title: 'Max Subcompactions', type: 'category' }, yaxis: { title: 'Time (s)' }, showlegend: false, })); }}
// ── data table ────────────────────────────────────────────────────function buildTable(filteredData) { const tbody = document.getElementById('results-body'); tbody.innerHTML = filteredData.map(d => `<tr> <td>${d.block_size / 1024}k</td> <td>${levelName(d.zstd_level)}</td> <td>${d.restart_interval}</td> <td>${d.dict_bytes}</td> <td>${d.train_mult === null ? '' : d.train_mult + 'x'}</td> <td>${d.opt_filters ? 'on' : 'off'}</td> <td>${d.subcompactions}</td> <td><strong>${(d.size_mib / 1024).toFixed(2)}</strong></td> <td>${d.compact_secs.toFixed(1)}</td> </tr>`).join('');}
function setupFilters() { const blocks = [...new Set(data.map(d => d.block_size))].sort((a, b) => a - b); const levels = [...new Set(data.map(d => d.zstd_level))].sort((a, b) => a - b); const dicts = [...new Set(data.map(d => d.dict_bytes))].sort((a, b) => a - b);
const fBlock = document.getElementById('f-block'); const fLevel = document.getElementById('f-level'); const fDict = document.getElementById('f-dict');
blocks.forEach(b => { const o = document.createElement('option'); o.value = b; o.textContent = (b/1024) + 'k'; fBlock.appendChild(o); }); levels.forEach(l => { const o = document.createElement('option'); o.value = l; o.textContent = levelName(l); fLevel.appendChild(o); }); dicts.forEach(d => { const o = document.createElement('option'); o.value = d; o.textContent = d; fDict.appendChild(o); });
const applyFilters = () => { let filtered = data; if (fBlock.value) filtered = filtered.filter(d => d.block_size === +fBlock.value); if (fLevel.value) filtered = filtered.filter(d => d.zstd_level === +fLevel.value); if (fDict.value !== '') filtered = filtered.filter(d => d.dict_bytes === +fDict.value); buildTable(filtered); };
[fBlock, fLevel, fDict].forEach(el => el.addEventListener('change', applyFilters));}
// sortable headerslet sortCol = 'size_mib', sortAsc = true;document.querySelectorAll('#results-table th').forEach(th => { th.addEventListener('click', () => { const col = th.dataset.col; if (sortCol === col) sortAsc = !sortAsc; else { sortCol = col; sortAsc = true; } const filtered = getFilteredData(); filtered.sort((a, b) => { const av = a[col] ?? -1, bv = b[col] ?? -1; return sortAsc ? av - bv : bv - av; }); buildTable(filtered); });});
function getFilteredData() { let filtered = [...data]; const fBlock = document.getElementById('f-block').value; const fLevel = document.getElementById('f-level').value; const fDict = document.getElementById('f-dict').value; if (fBlock) filtered = filtered.filter(d => d.block_size === +fBlock); if (fLevel) filtered = filtered.filter(d => d.zstd_level === +fLevel); if (fDict !== '') filtered = filtered.filter(d => d.dict_bytes === +fDict); return filtered;}
// ── CSV download ──────────────────────────────────────────────────document.getElementById('download-csv').addEventListener('click', () => { const blob = new Blob([csvText], { type: 'text/csv' }); const a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = 'sweep-results.csv'; a.click(); URL.revokeObjectURL(a.href);});
// ── zero-Y toggles ────────────────────────────────────────────────document.querySelectorAll('.zero-toggle input').forEach(cb => { cb.checked = false; // override browser autofill on refresh cb.addEventListener('change', () => { const chartId = cb.dataset.chart; const mode = cb.checked ? 'tozero' : 'normal'; Plotly.relayout(chartId, { 'yaxis.rangemode': mode }); });});
// ── data profile histograms ────────────────────────────────────────function fmtBytes(n) { if (n >= 1048576) return (n / 1048576).toFixed(0) + 'M'; if (n >= 1024) return (n / 1024).toFixed(0) + 'k'; return n + '';}
function chartValueHist() { const h = stats.value_size_hist; Plotly.newPlot('chart-value-hist', [{ x: h.map(([lo, hi]) => `${fmtBytes(lo)}-${fmtBytes(hi)}`), y: h.map(([,, c]) => c), type: 'bar', marker: { color: '#45b7d1' }, hovertemplate: '%{x} bytes<br>%{y:,} keys<extra></extra>', }], plotLayout({ xaxis: { title: 'Value Size (bytes)', type: 'category' }, yaxis: { title: 'Keys', type: 'log' }, showlegend: false, }));}
function chartAccountHist() { const h = stats.keys_per_account_hist; Plotly.newPlot('chart-account-hist', [{ x: h.map(([lo, hi]) => `${fmtNum(lo)}-${fmtNum(hi)}`), y: h.map(([,, c]) => c), type: 'bar', marker: { color: '#4ecdc4' }, hovertemplate: '%{x} keys<br>%{y:,} accounts<extra></extra>', }], plotLayout({ xaxis: { title: 'Keys per Account', type: 'category' }, yaxis: { title: 'Accounts', type: 'log' }, showlegend: false, }));}
// ── init ──────────────────────────────────────────────────────────chartValueHist();chartAccountHist();chartBlockSize('chart-block-size', 16);chartBlockSize('chart-block-size-128', 128);chartPareto();chartDict();chartDictBenefit();chartDictCost();chart32k();chartRestart();chartSubcomp();setupFilters();buildTable(data);</script></body></html>