diff options
Diffstat (limited to 'research/experiments/030-app-lifecycle/memory.py')
| -rw-r--r-- | research/experiments/030-app-lifecycle/memory.py | 79 |
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() |
