diff options
| author | eunhhu <[email protected]> | 2026-09-30 03:42:36 +0100 |
|---|---|---|
| committer | eunhhu <[email protected]> | 2026-09-30 03:42:36 +0100 |
| commit | 6b3b1c64328fb6b4932dfb67fd05486182657673 (patch) | |
| tree | d81bd1e3bda734c88965aa0d002cdadbaa6496ab | |
| parent | d9e0b6870201d9200b3c225903b43b850475600b (diff) | |
| download | pure-6b3b1c64328fb6b4932dfb67fd05486182657673.tar.gz pure-6b3b1c64328fb6b4932dfb67fd05486182657673.zip | |
Say what the windows and checkpoints cost (013 steps 2 and 3, 104)
013's second step is in (d9e0b68): each transport holds a window of its
output's sends. 104 measures it with the receivers down: once the window
is full a node grows by the outbox alone, 219 and 218 B a send over the
link and HTTP (399 and 534 B before).
The third step is a measurement. A checkpoint holds one list's snapshot
while it writes the image, so what it adds is what the app changed
meanwhile: nothing visible on tmpfs or for a list that fits the card's
fast writes; on the SD card, a 22.4 MB list written for about 8 s while
1,400 items a second were set left +3.0 and +4.4 MiB. The highest
resident memory was twice the state bytes, inside 013's bound (the state
and one snapshot) and its stop criterion (3x).
021's run.py takes --items, --stores (a directory on the device to
measure) and --label, and reports the peak (VmHWM).
5 files changed, 79 insertions, 15 deletions
diff --git a/docs/design/013-memory-budget.md b/docs/design/013-memory-budget.md index fe8736a..97bd8a6 100644 --- a/docs/design/013-memory-budget.md +++ b/docs/design/013-memory-budget.md @@ -1,10 +1,10 @@ # 013 — 앱 전체 메모리 예산 -작성일: 2026-09-30. 상태: 1단계 구현. +작성일: 2026-09-30. 상태: 세 단계 모두 했다(3단계는 측정). [외부 리뷰 004](../outside-feedback/feedback-004.md)의 둘째 권고에서 시작한다. 측정은 [104](../research/104-where-memory-goes.md)에 있다. -지금의 `--budget`은 요청 payload의 접수만 묶는다. 앱의 목록, outbox, 붙들린 스냅숏, 전송의 사본은 아무것도 묶지 않는다. 작은 -기기에서 오래 도는 노드라면 무엇이 얼마나 쓰는지 정해져 있어야 하고, 넘칠 때 무엇을 하는지도 정해져 있어야 한다. +이 설계 전에는 `--budget`이 요청 payload의 접수만 묶었다. 앱의 목록, outbox, 붙들린 스냅숏, 전송의 사본은 아무것도 묶지 +않았다. 작은 기기에서 오래 도는 노드라면 무엇이 얼마나 쓰는지 정해져 있어야 하고, 넘칠 때 무엇을 하는지도 정해져 있어야 한다. ## 결정 @@ -56,15 +56,19 @@ | 무엇 | 상한 | 이유 | |---|---|---| | 붙들린 스냅숏 | 붙든 판마다 상태 한 벌. 붙드는 쪽(연결 스레드, 표면, 체크포인트)이 H이면 최악 (1 + H) × 예산 | 104에서 고쳤다. 전에는 한이 없었다 | -| 전송의 사본 | 링크 ring과 HTTP 큐가 outbox를 한 번 더 든다(한 건에 0.8배, 1.4배) | 2단계에서 창으로 묶는다 | +| 전송의 사본 | 보내는 곳(`--send`)마다 창 하나: 받는 쪽이 가진 것 너머로 `--window` 바이트(4 MiB)와 65,536건 | 2단계. 전에는 outbox를 한 번 더 들었다(한 건에 0.8배, 1.4배) | | 요청 payload | `--budget` | 지금의 접수 예산 | -| 체크포인트 | 이미지를 쓰는 동안 스냅숏 하나 | 붙들린 스냅숏의 한 경우 | +| 체크포인트 | 이미지를 쓰는 동안 스냅숏 하나. 그동안 고친 잎과 바꾼 텍스트만 더 든다 | 붙들린 스냅숏의 한 경우. SD 카드에서 22.4 MB 목록을 8초쯤 쓰는 동안 +3.0–4.4 MiB였다(104) | ## 단계 1. 상태 바이트를 세고, `--memory`로 예산을 두고, 넘칠 때 사건을 거절한다. 전달의 거절은 들은 번호로 세지 않는다. 2. 전송의 창: 링크와 HTTP 전달이 outbox 전체가 아니라 창만큼만 받는다. 전달되면 다음을 받는다. -3. 체크포인트가 쓰는 동안의 메모리를 잰다. + - 소유자가 보내는 곳마다 outbox에서의 자리를 기억해, 새로 넣을 것만 본다. outbox에서 나간 보내기의 수(`sends_gone`)만큼 + 자리를 뒤로 물리므로, 앞의 것이 지워져도 건너뛰는 것이 없다. 저장 뒤와 전달 뒤에 창을 채운다. + - 창이 차도 하나는 넣는다. 창보다 큰 보내기 하나도 간다. +3. 체크포인트가 쓰는 동안의 메모리를 잰다. 잰 값은 104에 있다. 쓰는 동안 고친 것만큼 는다. 예산이 세는 상태의 2배(상태 + 한 벌과 스냅숏 한 벌)를 넘지 않고, 잰 최고는 상태 바이트의 약 2배(상주)였다. ## 검사 @@ -84,7 +88,13 @@ 기다리던 주문이 모두 한 번씩 들어온다. 가득 찬 2초 동안 거절은 7번이다(기다림이 없으면 27,809번). - check.py `server_answers_memory`: 답이 보내기보다 큰 영수증을 남긴다. 영수증이 예산을 채우면 답이 기다리고 주문이 503 `memory`를 받는다. 비우면 기다리던 답이 모두 한 번씩 적용되고, API는 주문마다 한 번씩만 불렸다. +- check.py `server_windows`: 출력 둘(링크 하나, HTTP 하나)의 받는 쪽이 모두 꺼진 채 300건을 보낸다. 창(2,000 B)이 찬다. + 받는 쪽이 뜨면 둘 다 300건을 한 번씩 받는다. 전달 뒤에 창을 채우지 않으면 117건에서 멈춘다. - 021의 run.py `budget`: 상태 4 MB가 4초 남짓에 차고, 상주 메모리가 9.6–9.8 MiB에 멈춘다(예산 없이 30초에 45.9 MiB). +- 021의 run.py `link-down`, `wire-down`(창 4 MiB): 창이 찬 뒤로는 보낸 것 한 건에 상주 219 B, 218 B로 outbox만큼만 는다(30초 + 전체로 전에는 399 B, 534 B). +- 021의 run.py `unheld --items 200000 --stores`(SD 카드): 체크포인트 두 번이 각각 +3.0 MiB, +4.4 MiB를 남겼다. 최고 상주 + 42.6 MiB는 상태 22.4 MB의 약 2배다. ## 중단 기준 diff --git a/docs/research/104-where-memory-goes.md b/docs/research/104-where-memory-goes.md index 03749c7..0828e44 100644 --- a/docs/research/104-where-memory-goes.md +++ b/docs/research/104-where-memory-goes.md @@ -21,9 +21,13 @@ - 보내지 못한 한 건마다 outbox에 221 B가 든다(100 B 글의 주문). - 링크(`pure://`)로 보내면 링크가 따로 사본을 들어 399 B, HTTP로 보내면 534 B다. - 그래서 예산은 "상태의 상한"과 "넘칠 때 거절"로 정해야 한다. 전송의 사본은 줄일 수 있는 상수배다([013](../design/013-memory-budget.md)). + - **전송의 사본은 창만큼이다.** 013의 2단계 뒤, 링크와 HTTP 전달은 받는 쪽이 가진 것 너머로 4 MiB까지만 든다. 창이 찬 + 뒤로는 받는 쪽이 꺼져 있어도 보낸 한 건에 상주 약 220 B, outbox만큼만 는다. - **예산을 주면 멈춘다.** 013의 1단계(`--memory`) 뒤, 받는 쪽 없이 보내는 노드에 상태 4 MB를 주었다. 4초 남짓에 차고, 그 뒤로 보내는 요청은 503 `memory`를 받으며 상주 메모리가 9.6–9.8 MiB에 머문다. 예산이 없으면 30초 동안 45.9 MiB까지 는다. -- 아직 모르는 것: 체크포인트가 쓰는 동안의 메모리는 따로 재지 않았다. + - **체크포인트는 쓰는 동안 고친 만큼 든다.** SD 카드에서 22.4 MB 목록의 이미지를 쓰는 8초쯤 동안 +3.0–4.4 MiB였다. + 최고 상주는 상태 바이트의 약 2배다. +- 아직 모르는 것: 몇 시간, 며칠 도는 노드에서 할당기가 쥐고 돌려주지 않는 메모리(조각남)가 어디까지 가는지는 재지 않았다. ## 방법 @@ -96,3 +100,39 @@ - 세는 비용: 가장 작은 사건(수 하나를 더함, 저장소 없음)이 상태 1개인 앱에서 125 → 136 ns(+8 %), 32개인 앱에서 321 → 349 ns(+9 %)다. 처음에는 사건마다 모든 상태를 두 번 걸어 125 → 146 ns(+17 %), 328 → 454 ns(+38 %)였다. 목록과 텍스트만 비트로 골라 걷게 했다(`883d64c`). 예산이 없으면 비용이 없다. HTTP로 받는 쓰기는 한 건에 수십 µs라 처리량에 보이지 않는다. + +### 창을 둔 뒤 + +013의 2단계(보내는 곳마다 창)를 넣은 뒤, 받는 쪽이 꺼진 두 경우를 다시 쟀다 +(`research/experiments/021-memory/2026-09-30-window-linux-aarch64.jsonl`, 창 4 MiB). + +| 경우 | 보낸 수 | 상주 메모리 | 한 건마다(30초 전체) | 창이 찬 뒤(8–30초) | +|---|---:|---|---:|---:| +| link-down, 창 전 | 198,874 | 8.5 → 84.1 MiB | 399 B | | +| link-down | 202,933 | 8.5 → 57.6 MiB | 254 B | 219 B | +| wire-down, 창 전 | 198,269 | 3.0 → 104.0 MiB | 534 B | | +| wire-down | 202,831 | 3.0 → 54.5 MiB | 266 B | 218 B | + +- 창은 링크에서 6–8초 사이, HTTP에서 4–6초 사이에 찼다(보낸 수 4만–5만). 그 뒤로 는 것은 outbox 한 건(221 B, 위의 outbox + 경우)과 같다. +- 전송이 쥐는 것은 이제 보내는 곳마다 상수다. 예산과 함께면 노드의 상태 쪽 메모리는 예산의 약 1.7배와 창들의 합으로 묶인다. + +### 체크포인트가 쓰는 동안 + +- 체크포인트는 목록 하나의 스냅숏을 쥐고 이미지를 쓴다(소유자 밖의 스레드). 그동안 앱이 고친 잎과 바꾼 텍스트는 스냅숏이 + 쥐고 있어 풀리지 않는다. 그래서 더 드는 것은 "쓰는 동안 고친 것"이고, 많아야 목록 한 벌이다. +- unheld 경우(무작위 항목의 글을 고침)를 항목 수와 저장 장치를 바꿔 쟀다 + (`research/experiments/021-memory/2026-09-30-checkpoint-linux-aarch64.jsonl`). 최고 상주는 0.5초마다 읽은 VmRSS와 끝의 + VmHWM 가운데 큰 것이다(VmHWM은 42.5 MiB라 했지만 172초에 읽은 VmRSS가 42.6 MiB였다). + +| 저장소 | 항목 | 고친 수 | 체크포인트 | 상주(시작 → 끝) | 최고 | 상태 바이트 | +|---|---:|---:|---:|---|---|---:| +| tmpfs | 50,000 | 205,531(30초) | 4 | 11.0 → 11.5 MiB | 11.5 MiB | 5,600,000 | +| SD 카드 | 50,000 | 36,522(30초) | 1 | 11.3 → 11.6 MiB | 11.6 MiB | 5,600,000 | +| SD 카드 | 200,000 | 331,881(240초) | 2 | 35.1 → 42.5 MiB | 42.6 MiB | 22,400,000 | + +- tmpfs와 작은 목록(5.6 MB, 카드의 빠른 쓰기 구간 안)에서는 체크포인트가 보이지 않는다. 금방 끝나 고친 것이 적다. +- SD 카드의 22.4 MB 목록에서는 두 번 모두 보인다. 6–10초에 +3.0 MiB, 164–172초에 +4.4 MiB다. 초당 1,400건을 고치는 동안 + 8초쯤 이어졌다. 고친 잎(짧은 수와 포인터)과 바뀐 글(각 100 B)이 쥐어졌고, 목록 한 벌(상주 약 37 MB)에는 한참 못 미친다. +- 쓴 뒤에 그 메모리는 풀리지만 프로세스는 돌려주지 않고 다음에 쓴다(할당기가 쥔다). 그래서 상주가 새 높이에 머문다. +- 최고 42.6 MiB는 상태 22.4 MB의 약 2배(1.99)다. 013의 상한(상태 한 벌과 스냅숏 한 벌)과 중단 기준(3배) 안이다. diff --git a/research/experiments/021-memory/2026-09-30-checkpoint-linux-aarch64.jsonl b/research/experiments/021-memory/2026-09-30-checkpoint-linux-aarch64.jsonl new file mode 100644 index 0000000..1fbe32f --- /dev/null +++ b/research/experiments/021-memory/2026-09-30-checkpoint-linux-aarch64.jsonl @@ -0,0 +1,3 @@ +{"label": "tmpfs", "case": "unheld", "seconds": 30, "posted": 205531, "refused": 0, "rss_began_kib": 11312, "rss_end_kib": 11728, "rss_peak_kib": 11728, "items": 50000, "rss_per_post_bytes": 2.1, "store_bytes": 22647708, "samples": [[0.0, 11312, 0], [2.0, 11696, 13881], [4.01, 11696, 27968], [6.01, 11696, 41364], [8.01, 11696, 54455], [10.02, 11728, 67781], [12.02, 11728, 81825], [14.03, 11728, 95982], [16.03, 11728, 110099], [18.03, 11728, 123891], [20.04, 11728, 137984], [22.04, 11728, 151784], [24.04, 11728, 164949], [26.05, 11728, 178675], [28.05, 11728, 192438], [30.05, 11728, 205527], [30.05, 11728, 205527]], "stats": {"active": 0, "retained_payload_bytes": 0, "accepted": 8, "requests": 255531, "bursts": 152162, "burst_events": 255531, "ticks": 0, "expired": 0, "threads": 2, "owner": true, "snapshots": 152163, "views_from_snapshots": 0, "delivered": 0, "delivery_attempts": 0, "answers": 0, "commit_groups": 0, "grouped_commits": 0, "checkpoints": 4, "link_packets": 0, "link_records": 0, "link_resent": 0, "link_lost": 0, "link_acknowledged": 0, "link_stored": 0, "link_refused": 0, "listener_packets": 0, "listener_received": 0, "listener_deliveries": 0, "listener_woken": 0, "listener_remembered": 0, "listener_swept": 0, "listener_active": 0, "listener_keyed": 0, "listener_secrets": 0, "listener_epochs": 0, "listener_refused": 0, "state_bytes": 5600000, "memory_refused": 0, "windows_full": 0}} +{"label": "sd-card", "case": "unheld", "seconds": 30, "posted": 36522, "refused": 0, "rss_began_kib": 11568, "rss_end_kib": 11920, "rss_peak_kib": 11920, "items": 50000, "rss_per_post_bytes": 9.9, "store_bytes": 22647708, "samples": [[0.0, 11568, 0], [2.0, 11872, 2094], [4.0, 11920, 4139], [6.01, 11920, 6475], [8.01, 11920, 8779], [10.01, 11920, 11122], [12.01, 11920, 13701], [14.01, 11920, 16234], [16.01, 11920, 18733], [18.01, 11920, 21216], [20.01, 11920, 23727], [22.02, 11920, 26210], [24.02, 11920, 28596], [26.02, 11920, 30956], [28.02, 11920, 33798], [30.02, 11920, 36518], [30.02, 11920, 36518]], "stats": {"active": 0, "retained_payload_bytes": 0, "accepted": 8, "requests": 86522, "bursts": 24027, "burst_events": 86522, "ticks": 0, "expired": 0, "threads": 2, "owner": true, "snapshots": 24028, "views_from_snapshots": 0, "delivered": 0, "delivery_attempts": 0, "answers": 0, "commit_groups": 0, "grouped_commits": 0, "checkpoints": 1, "link_packets": 0, "link_records": 0, "link_resent": 0, "link_lost": 0, "link_acknowledged": 0, "link_stored": 0, "link_refused": 0, "listener_packets": 0, "listener_received": 0, "listener_deliveries": 0, "listener_woken": 0, "listener_remembered": 0, "listener_swept": 0, "listener_active": 0, "listener_keyed": 0, "listener_secrets": 0, "listener_epochs": 0, "listener_refused": 0, "state_bytes": 5600000, "memory_refused": 0, "windows_full": 0}} +{"label": "sd-card", "case": "unheld", "seconds": 240, "posted": 331881, "refused": 0, "rss_began_kib": 35920, "rss_end_kib": 43488, "rss_peak_kib": 43488, "items": 200000, "rss_per_post_bytes": 23.4, "store_bytes": 56224924, "samples": [[0.0, 35920, 0], [2.0, 36016, 2823], [4.01, 36016, 5611], [6.01, 36416, 7810], [8.01, 38048, 9291], [10.01, 39136, 11267], [12.01, 39136, 14243], [14.01, 39136, 16904], [16.01, 39136, 19857], [18.01, 39136, 22534], [20.01, 39136, 25452], [22.01, 39136, 28212], [24.01, 39136, 30754], [26.02, 39136, 33010], [28.02, 39136, 35403], [30.02, 39136, 37617], [32.02, 39136, 39666], [34.02, 39136, 42030], [36.02, 39136, 44303], [38.02, 39136, 46536], [40.02, 39136, 48546], [42.02, 39136, 50862], [44.03, 39136, 53150], [46.03, 39136, 55317], [48.03, 39136, 57851], [50.03, 39136, 60239], [52.03, 39136, 62468], [54.03, 39136, 64813], [56.03, 39136, 66894], [58.03, 39136, 69147], [60.03, 39136, 71306], [62.03, 39136, 73476], [64.04, 39136, 75829], [66.04, 39136, 78844], [68.04, 39136, 81637], [70.04, 39136, 84140], [72.04, 39136, 86830], [74.04, 39136, 89258], [76.04, 39136, 92081], [78.04, 39136, 95081], [80.04, 39136, 98077], [82.04, 39136, 100937], [84.05, 39136, 103911], [86.05, 39136, 106974], [88.05, 39136, 109948], [90.05, 39136, 113173], [92.05, 39136, 116688], [94.05, 39136, 120226], [96.05, 39136, 123090], [98.05, 39136, 125795], [100.05, 39136, 129171], [102.06, 39136, 132362], [104.06, 39136, 135085], [106.06, 39136, 138303], [108.06, 39136, 141374], [110.06, 39136, 144455], [112.06, 39136, 147323], [114.06, 39136, 150009], [116.06, 39136, 152857], [118.06, 39136, 155628], [120.07, 39136, 158297], [122.07, 39136, 160911], [124.07, 39136, 164122], [126.07, 39136, 167114], [128.07, 39136, 169741], [130.07, 39136, 172414], [132.07, 39136, 175596], [134.07, 39136, 179054], [136.07, 39136, 182091], [138.07, 39136, 185356], [140.08, 39136, 188694], [142.08, 39136, 191798], [144.08, 39136, 195078], [146.08, 39136, 198423], [148.08, 39136, 201395], [150.08, 39136, 204285], [152.08, 39136, 207284], [154.08, 39136, 210240], [156.08, 39136, 213144], [158.08, 39136, 215643], [160.09, 39136, 218353], [162.09, 39136, 220651], [164.09, 39536, 222664], [166.09, 41056, 225034], [168.09, 41872, 226643], [170.09, 42832, 228707], [172.09, 43632, 230744], [174.09, 43488, 233315], [176.09, 43488, 235998], [178.09, 43488, 238643], [180.1, 43488, 241252], [182.1, 43488, 244278], [184.1, 43488, 247052], [186.1, 43488, 250316], [188.1, 43488, 253671], [190.1, 43488, 256698], [192.1, 43488, 259851], [194.11, 43488, 263181], [196.11, 43488, 266340], [198.11, 43488, 269500], [200.11, 43488, 272626], [202.11, 43488, 275599], [204.11, 43488, 278757], [206.11, 43488, 282039], [208.11, 43488, 285131], [210.11, 43488, 288008], [212.12, 43488, 290872], [214.12, 43488, 294022], [216.12, 43488, 297195], [218.12, 43488, 300336], [220.12, 43488, 303276], [222.12, 43488, 306555], [224.12, 43488, 309734], [226.12, 43488, 312564], [228.12, 43488, 315365], [230.13, 43488, 317789], [232.13, 43488, 320287], [234.13, 43488, 323353], [236.13, 43488, 326444], [238.13, 43488, 329302], [240.13, 43488, 331877], [240.13, 43488, 331877]], "stats": {"active": 0, "retained_payload_bytes": 0, "accepted": 8, "requests": 531881, "bursts": 144699, "burst_events": 531881, "ticks": 0, "expired": 0, "threads": 2, "owner": true, "snapshots": 144700, "views_from_snapshots": 0, "delivered": 0, "delivery_attempts": 0, "answers": 0, "commit_groups": 0, "grouped_commits": 0, "checkpoints": 2, "link_packets": 0, "link_records": 0, "link_resent": 0, "link_lost": 0, "link_acknowledged": 0, "link_stored": 0, "link_refused": 0, "listener_packets": 0, "listener_received": 0, "listener_deliveries": 0, "listener_woken": 0, "listener_remembered": 0, "listener_swept": 0, "listener_active": 0, "listener_keyed": 0, "listener_secrets": 0, "listener_epochs": 0, "listener_refused": 0, "state_bytes": 22400000, "memory_refused": 0, "windows_full": 0}} diff --git a/research/experiments/021-memory/2026-09-30-window-linux-aarch64.jsonl b/research/experiments/021-memory/2026-09-30-window-linux-aarch64.jsonl new file mode 100644 index 0000000..7370490 --- /dev/null +++ b/research/experiments/021-memory/2026-09-30-window-linux-aarch64.jsonl @@ -0,0 +1,2 @@ +{"label": "window", "case": "link-down", "seconds": 30, "posted": 202933, "refused": 0, "rss_began_kib": 8688, "rss_end_kib": 58976, "rss_per_post_bytes": 253.8, "store_bytes": 34678695, "samples": [[0.0, 8688, 0], [2.0, 14384, 13522], [4.01, 19600, 27002], [6.01, 24224, 40567], [8.01, 26928, 52717], [10.01, 29760, 66334], [12.02, 32656, 80087], [14.02, 35632, 93864], [16.02, 38640, 107662], [18.03, 41520, 121411], [20.03, 44464, 135167], [22.03, 47360, 148920], [24.03, 50192, 162021], [26.04, 53120, 175808], [28.04, 56128, 189345], [30.04, 58976, 202929], [30.04, 58976, 202929]], "stats": {"active": 0, "retained_payload_bytes": 0, "accepted": 4, "requests": 202933, "bursts": 122711, "burst_events": 202933, "ticks": 0, "expired": 0, "threads": 2, "owner": true, "snapshots": 122712, "views_from_snapshots": 0, "delivered": 0, "delivery_attempts": 0, "answers": 0, "commit_groups": 122712, "grouped_commits": 122712, "checkpoints": 0, "link_packets": 67, "link_records": 350, "link_resent": 32, "link_lost": 0, "link_acknowledged": 0, "link_stored": 0, "link_refused": 0, "listener_packets": 0, "listener_received": 0, "listener_deliveries": 0, "listener_woken": 0, "listener_remembered": 0, "listener_swept": 0, "listener_active": 0, "listener_keyed": 0, "listener_secrets": 0, "listener_epochs": 0, "listener_refused": 0, "state_bytes": 27598888, "memory_refused": 0, "windows_full": 100054}} +{"label": "window", "case": "wire-down", "seconds": 30, "posted": 202831, "refused": 0, "rss_began_kib": 3104, "rss_end_kib": 55856, "rss_per_post_bytes": 266.3, "store_bytes": 34678695, "samples": [[0.0, 3104, 0], [2.0, 10528, 13345], [4.01, 17488, 26797], [6.01, 21232, 40539], [8.01, 24192, 53780], [10.01, 26896, 67387], [12.02, 29728, 80588], [14.02, 32672, 94295], [16.02, 35648, 108084], [18.03, 38464, 121233], [20.03, 41376, 135025], [22.03, 44320, 148807], [24.03, 47088, 161562], [26.04, 50096, 175369], [28.04, 52928, 189121], [30.04, 55920, 202827], [30.04, 55920, 202827]], "stats": {"active": 0, "retained_payload_bytes": 0, "accepted": 4, "requests": 202831, "bursts": 121645, "burst_events": 202831, "ticks": 0, "expired": 0, "threads": 2, "owner": true, "snapshots": 121646, "views_from_snapshots": 0, "delivered": 0, "delivery_attempts": 9, "answers": 0, "commit_groups": 121646, "grouped_commits": 121646, "checkpoints": 0, "link_packets": 0, "link_records": 0, "link_resent": 0, "link_lost": 0, "link_acknowledged": 0, "link_stored": 0, "link_refused": 0, "listener_packets": 0, "listener_received": 0, "listener_deliveries": 0, "listener_woken": 0, "listener_remembered": 0, "listener_swept": 0, "listener_active": 0, "listener_keyed": 0, "listener_secrets": 0, "listener_epochs": 0, "listener_refused": 0, "state_bytes": 27585016, "memory_refused": 0, "windows_full": 103922}} diff --git a/research/experiments/021-memory/run.py b/research/experiments/021-memory/run.py index 49bb5a9..546bfbc 100644 --- a/research/experiments/021-memory/run.py +++ b/research/experiments/021-memory/run.py @@ -15,7 +15,12 @@ size at the end. unheld the same, no surface python3 research/experiments/021-memory/run.py [--seconds 30] [--cases outbox,budget,held] [--out FILE] - [--label NAME] + [--label NAME] [--items 20000] [--stores DIRECTORY] + +--items sets how many items held and unheld begin with; --stores puts the +stores in a directory of the device to measure (a flash card: checkpoints +then take long, and a snapshot is held that long). Each row gives the peak +resident memory too (VmHWM). PURE_SERVE=PATH runs another build's pure-serve (--label names it in the rows). """ @@ -87,10 +92,10 @@ def start(args): return process, json.loads(line)['port'] -def rss_kib(pid): +def rss_kib(pid, field='VmRSS:'): with open(f'/proc/{pid}/status') as status: for line in status: - if line.startswith('VmRSS:'): + if line.startswith(field): return int(line.split()[1]) return 0 @@ -121,8 +126,8 @@ def post_all(port, bodies, stop, counts, index, refused): connection.close() -def run_case(case, work, seconds, clients=4): - store = work / f'store-{case}' +def run_case(case, work, seconds, clients=4, stores=None): + store = (stores or work) / f'store-{case}-{os.getpid()}' args = [SERVE, '--store', store, '--threads', '2'] note = 'n' * NOTE if case in ('outbox', 'budget', 'link-down', 'wire-down'): @@ -183,31 +188,35 @@ def run_case(case, work, seconds, clients=4): stop.set() for t in threads: t.join() - rss_end = rss_kib(process.pid) + rss_end, rss_peak = rss_kib(process.pid), rss_kib(process.pid, 'VmHWM:') finally: process.send_signal(signal.SIGTERM) _, error = process.communicate(timeout=60) stats = [json.loads(line) for line in error.decode().splitlines() if line.startswith('{"active"')] posted = sum(counts) return {'case': case, 'seconds': seconds, 'posted': posted, 'refused': sum(refused), 'rss_began_kib': rss_began, - 'rss_end_kib': rss_end, + 'rss_end_kib': rss_end, 'rss_peak_kib': rss_peak, 'items': SEEDED, 'rss_per_post_bytes': round((rss_end - rss_began) * 1024 / posted, 1) if posted else None, 'store_bytes': store_bytes(store), 'samples': samples[::4] + [samples[-1]], 'stats': stats[-1] if stats else None} def main(): + global SEEDED parser = argparse.ArgumentParser() parser.add_argument('--seconds', type=int, default=30) parser.add_argument('--cases', default='outbox,budget,link-down,wire-down,unheld,held') parser.add_argument('--out', type=Path, default=None) parser.add_argument('--label', default=None, help='the build, named in each row') + parser.add_argument('--items', type=int, default=SEEDED) + parser.add_argument('--stores', type=Path, default=None) args = parser.parse_args() + SEEDED = args.items with tempfile.TemporaryDirectory(prefix='pure-memory-') as directory: work = Path(directory) build(work) for case in args.cases.split(','): - row = run_case(case, work, args.seconds) + row = run_case(case, work, args.seconds, stores=args.stores) if args.label: row = {'label': args.label, **row} line = json.dumps(row) |
