aboutsummaryrefslogtreecommitdiffstats
path: root/research/experiments/030-app-lifecycle/memory.py
diff options
context:
space:
mode:
authoreunhhu <[email protected]>2026-10-04 11:07:55 +0100
committereunhhu <[email protected]>2026-10-04 11:07:55 +0100
commit52e51e4231f2e119c97826ae6d6e2f2af0da0c58 (patch)
tree0f17ced79a1881bd2b67fbaa9e7ffb823f54b984 /research/experiments/030-app-lifecycle/memory.py
parent420b749742badd26daf2474e9f157af17be2907f (diff)
downloadpure-52e51e4231f2e119c97826ae6d6e2f2af0da0c58.tar.gz
pure-52e51e4231f2e119c97826ae6d6e2f2af0da0c58.zip
Say what apps that change on a running host showed (017 stage 3, research 115)
Changing an app in place keeps a million items' app answering but for 1.12 ms (a host restart: 150 ms; stopping and starting it again from its store, as first built, 150.4 ms), 171.5 ms when its records change shape (332 ms). Another app's p99 stays 0.97 times as it was while one changes every 0.2 s; a host of one app is as it was (TPC-C +0.7%, instructions at most +0.02%); each module change leaves 19 KB. 030 holds the windows of both builds, the isolation, the pairs, the counts and the memory.
Diffstat (limited to 'research/experiments/030-app-lifecycle/memory.py')
-rw-r--r--research/experiments/030-app-lifecycle/memory.py79
1 files changed, 79 insertions, 0 deletions
diff --git a/research/experiments/030-app-lifecycle/memory.py b/research/experiments/030-app-lifecycle/memory.py
new file mode 100644
index 0000000..3c15d4a
--- /dev/null
+++ b/research/experiments/030-app-lifecycle/memory.py
@@ -0,0 +1,79 @@
+#!/usr/bin/env python3
+"""What changing an app leaves in a running host's memory (docs/design/017's
+stage 3, research 115). A host of apps holds an app of items.aup (a
+thousand items, its store in RAM); its own memory (smaps_rollup's
+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.
+
+memory.py PURE_SERVE CHANGES a JSON line per way
+"""
+import json
+import select
+import shutil
+import signal
+import subprocess
+import sys
+import tempfile
+from pathlib import Path
+
+from window import AUP, HERE, grow
+
+
+def own(pid):
+ fields = {}
+ for line in Path(f'/proc/{pid}/smaps_rollup').read_text().splitlines()[1:]:
+ name, value = line.split(':', 1)
+ fields[name] = int(value.split()[0])
+ return fields['Private_Dirty'], fields['Pss']
+
+
+def main():
+ serve, changes = Path(sys.argv[1]).resolve(), int(sys.argv[2])
+ work = Path(tempfile.mkdtemp(prefix='pure-030-', dir='/dev/shm'))
+ try:
+ modules = {}
+ for name in ('items', 'items-next'):
+ modules[name] = work / f'{name}.pmod'
+ subprocess.run([str(AUP), 'build', str(HERE / f'{name}.aup'), '-o', str(modules[name])], check=True)
+ for way in ('module', 'budget'):
+ conf = work / 'apps.conf'
+
+ def write(n):
+ module = 'items-next' if way == 'module' and n % 2 else 'items'
+ budget = f' queue={1000 + n % 2}' if way == 'budget' else ''
+ conf.write_text(f"x {modules[module]} {work / way}{budget}\n")
+
+ write(0)
+ host = subprocess.Popen([str(serve), '--apps', str(conf), '--port', '0'], stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ port = json.loads(host.stdout.readline())['port']
+ grow(port, 1000)
+
+ def change(n):
+ write(n)
+ host.send_signal(signal.SIGHUP)
+ ready, _, _ = select.select([host.stdout], [], [], 60)
+ assert ready
+ said = json.loads(host.stdout.readline())
+ assert said['apps'][0]['did'] == 'changed' and said['apps'][0]['running'], said
+
+ for n in range(1, 101): # warm: the allocator's arenas settle
+ change(n)
+ dirty, pss = own(host.pid)
+ for n in range(101, 101 + changes):
+ change(n)
+ dirty_after, pss_after = own(host.pid)
+ host.terminate()
+ host.communicate(timeout=60)
+ 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)
+ finally:
+ shutil.rmtree(work)
+
+
+if __name__ == '__main__':
+ main()