From ddcc1202daa7de84f13ff712fdb5ad12159facc1 Mon Sep 17 00:00:00 2001 From: eunhhu Date: Thu, 1 Oct 2026 06:25:21 +0100 Subject: Measure one host of many apps against a process each (111) 017's first stage met its four criteria on the Pi (RAM stores, five rounds, the order of the ways turned): app B's 99th percentile while app A is flooded by 64 connections is 168 us, half of two processes' 336 us; the two apps busy together write 146.1k a second against 146.2k, with the same 1.4 ms tail; an app costs a host 120 KiB of its own memory (a process: 1,311 KiB PSS) and 100 apps still run on three threads; a host of one app keeps its speed (TPC-C +1.2% at the median of six pairs, two of them interfered and kept; the bank's queries within 2.6%) and its instructions (+0.94% at most). On the way: apps hopped between workers each turn (fixed in db4ee9c), connections of one app spread over all connection threads made the host a third more expensive a request and its tail a kernel tick (fixed by home threads, d24881d), and the work budget's counting cost every table row 26 instructions (fixed in be297ea). The earlier runs are kept apart (db4ee9c's isolation, d24881d's pairs and counts, the interrupted first try). --- .../experiments/026-apps-on-one-host/memory.py | 76 ++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 research/experiments/026-apps-on-one-host/memory.py (limited to 'research/experiments/026-apps-on-one-host/memory.py') diff --git a/research/experiments/026-apps-on-one-host/memory.py b/research/experiments/026-apps-on-one-host/memory.py new file mode 100644 index 0000000..05c93c5 --- /dev/null +++ b/research/experiments/026-apps-on-one-host/memory.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python3 +"""What an app costs a host (docs/design/017): pure-serve with N apps of the +scoreboard (benchmarks/http/scores.aup), each written once and read once +(its store opened, its snapshot published, a reader of it used), in RAM; +then the host's own memory (smaps_rollup's Private_Dirty, RSS, PSS) and +threads. Beside it, one pure-serve of one app (--module) the same way: the +process an app costs when each app is a process. + +memory.py SERVE [N ...] +""" +import http.client +import json +import select +import shutil +import subprocess +import sys +import tempfile +from pathlib import Path + +HERE = Path(__file__).resolve().parent +ROOT = HERE.parents[2] +AUP = ROOT.parent / 'aup' / 'build' / 'aup' + + +def used(pid): + fields = {} + for line in Path(f'/proc/{pid}/smaps_rollup').read_text().splitlines()[1:]: + name, value = line.split(':') + fields[name] = int(value.split()[0]) + threads = int(Path(f'/proc/{pid}/status').read_text().split('Threads:')[1].split()[0]) + return {'private_dirty_kib': fields['Private_Dirty'], 'rss_kib': fields['Rss'], 'pss_kib': fields['Pss'], + 'threads': threads} + + +def touched(port, prefixes): + connection = http.client.HTTPConnection('127.0.0.1', port, timeout=30) + for prefix in prefixes: + for method, path, body in (('POST', f'{prefix}/inputs/score?points=1', b'k'), ('GET', f'{prefix}/views/total', None)): + connection.request(method, path, body, {'Content-Length': str(len(body))} if body else {}) + answer = connection.getresponse() + answer.read() + assert answer.status == 200, (path, answer.status) + connection.close() + + +def measured(args, prefixes): + process = subprocess.Popen([*map(str, args), '--port', '0'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + select.select([process.stdout], [], [], 30) + info = json.loads(process.stdout.readline()) + touched(info['port'], prefixes) + result = used(process.pid) + process.terminate() + process.communicate(timeout=60) + return result + + +def main(): + serve = Path(sys.argv[1]).resolve() + counts = [int(n) for n in sys.argv[2:]] or [1, 10, 100] + work = Path(tempfile.mkdtemp(prefix='pure-apps-memory-', dir='/dev/shm')) + try: + module = work / 'scores.pmod' + subprocess.run([str(AUP), 'build', str(ROOT / 'benchmarks' / 'http' / 'scores.aup'), '-o', str(module)], check=True) + one = measured([serve, '--module', module, '--store', work / 'one', '--threads', 1], ['']) + print(json.dumps({'way': 'process', 'apps': 1, **one}), flush=True) + for n in counts: + manifest = work / f'apps-{n}.conf' + manifest.write_text(''.join(f'app{i} {module} {work / f"store-{n}-{i}"}\n' for i in range(n))) + host = measured([serve, '--apps', manifest, '--threads', 1, '--workers', 2], [f'/app{i}' for i in range(n)]) + print(json.dumps({'way': 'host', 'apps': n, **host}), flush=True) + finally: + shutil.rmtree(work) + + +if __name__ == '__main__': + main() -- cgit v1.2.3