aboutsummaryrefslogtreecommitdiffstats
path: root/research/experiments/030-app-lifecycle/memory.py
diff options
context:
space:
mode:
authoreunhhu <[email protected]>2026-10-19 09:58:47 +0100
committereunhhu <[email protected]>2026-10-19 09:58:47 +0100
commitb8e6a2e5b8fc64ec47adfbe583b8a27200cc5a2e (patch)
treed3a2a5041e42e4ca2fa4a7ec8e5cd10147c9030b /research/experiments/030-app-lifecycle/memory.py
parent31ef28609c6135058ffa0645fed9edd75c1086c7 (diff)
downloadpure-b8e6a2e5b8fc64ec47adfbe583b8a27200cc5a2e.tar.gz
pure-b8e6a2e5b8fc64ec47adfbe583b8a27200cc5a2e.zip
The modules an app ran are freed (research 115's next step): every app's module a Loaded with holds (each snapshot of it, each request read with it); one replaced is retired and freed on the apps thread once no app runs it, every connection thread has begun a pass since (woken for it), and nothing holds it; who keeps a module to ask later whether it is the same asks its serial (core: PureModule.serial, a reader's module_serial, pure_snapshot_module, pure_snapshot_release says it freed; serve: routes, groups, links, bindings, queries, the published snapshot), as an address may come back; a link binds under its receiver's lock, a link naming an input under binding_lock; 030's memory.py: 4,000 changes 0 bytes a change (was 19,284), 4,100 freed; stress.py: readers, view workers, push streams and a body across changes, 586 of 600 loads at a freed module's address, every answer right, ASan 1,000 changes clean; a push group that looked while its app resumed missed the new snapshot (published before the app took requests) and waited for its next save: woken again once it takes them (server_view_workers had failed once under load)
Diffstat (limited to 'research/experiments/030-app-lifecycle/memory.py')
-rw-r--r--research/experiments/030-app-lifecycle/memory.py21
1 files changed, 17 insertions, 4 deletions
diff --git a/research/experiments/030-app-lifecycle/memory.py b/research/experiments/030-app-lifecycle/memory.py
index 3c15d4a..49ed40c 100644
--- a/research/experiments/030-app-lifecycle/memory.py
+++ b/research/experiments/030-app-lifecycle/memory.py
@@ -6,9 +6,12 @@ Private_Dirty and Pss) is read after a warm-up of changes, then after
CHANGES changes more: in place to another module (its line naming in turn
items.pmod and items-next.pmod: each reading loads a module, and keeps the
one it replaced), and with its module as it was (its queue budget changed
-in turn: no module loaded). The difference a change.
+in turn: no module loaded). The difference a change. Each reading waits
+SETTLE seconds first: a module replaced is freed once no request or snapshot
+reads it and each connection thread has passed by (they wake each second);
+the host's own count of the modules it freed and still holds comes with it.
-memory.py PURE_SERVE CHANGES a JSON line per way
+memory.py PURE_SERVE CHANGES [SETTLE] a JSON line per way
"""
import json
import select
@@ -17,6 +20,7 @@ import signal
import subprocess
import sys
import tempfile
+import time
from pathlib import Path
from window import AUP, HERE, grow
@@ -32,6 +36,7 @@ def own(pid):
def main():
serve, changes = Path(sys.argv[1]).resolve(), int(sys.argv[2])
+ settle = float(sys.argv[3]) if len(sys.argv) > 3 else 2
work = Path(tempfile.mkdtemp(prefix='pure-030-', dir='/dev/shm'))
try:
modules = {}
@@ -62,15 +67,23 @@ def main():
for n in range(1, 101): # warm: the allocator's arenas settle
change(n)
+ time.sleep(settle)
dirty, pss = own(host.pid)
for n in range(101, 101 + changes):
change(n)
+ time.sleep(settle)
dirty_after, pss_after = own(host.pid)
host.terminate()
- host.communicate(timeout=60)
+ _, errors = host.communicate(timeout=60)
+ stats = {}
+ for line in errors.decode().splitlines():
+ if '"modules_freed"' in line:
+ stats = json.loads(line)
print(json.dumps({'way': way, 'changes': changes, 'private_dirty_kib': [dirty, dirty_after],
'pss_kib': [pss, pss_after],
- 'per_change_bytes': round((dirty_after - dirty) * 1024 / changes)}), flush=True)
+ 'per_change_bytes': round((dirty_after - dirty) * 1024 / changes),
+ 'modules_freed': stats.get('modules_freed'), 'modules_retired': stats.get('modules_retired')}),
+ flush=True)
finally:
shutil.rmtree(work)