1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
|
#!/usr/bin/env python3
"""Two apps, A and B (benchmarks/http/scores.aup, the scoreboard), as two
pure-serve processes or as one host of two apps (docs/design/017), in RAM
(/dev/shm), on cores 0-2; the load (benchmarks/http/load) on core 3. The
two ways have four threads for the apps: two processes of one connection
thread and an owner each, or one host of two connection threads and two
workers. A third, host1, is a host of one connection thread and two
workers (three threads).
Each round, both ways in turns (the order flipped every other round):
alone: B's writes, one connection, nothing else;
flood: A's writes from 64 connections, and B's from one, at once;
both: A's and B's writes from 32 connections each.
Prints a JSON line per run: B's p50 and p99, each app's writes a second.
isolation.py SERVE ROUNDS [SECONDS]
"""
import json
import select
import shutil
import subprocess
import sys
import tempfile
import time
from pathlib import Path
HERE = Path(__file__).resolve().parent
ROOT = HERE.parents[2]
LOAD = ROOT / 'benchmarks' / 'http' / 'load'
AUP = ROOT.parent / 'aup' / 'build' / 'aup'
def started(args):
process = subprocess.Popen(['taskset', '-c', '0-2', *map(str, args), '--port', '0'], stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
ready, _, _ = select.select([process.stdout], [], [], 10)
assert ready, process.stderr.read()
return process, json.loads(process.stdout.readline())
def stopped(process):
process.terminate()
_, error = process.communicate(timeout=60)
assert process.returncode == 0, error[-500:]
def load(port, connections, seconds, prefix):
return subprocess.Popen(['taskset', '-c', '3', str(LOAD), str(port), str(connections), str(seconds), 'score',
str(connections), 'keep-alive', '16', prefix], stdout=subprocess.PIPE, text=True)
def result(process):
out, _ = process.communicate()
return json.loads(out)
def servers(way, work, serve, module):
"""Where A and B are: ([processes], {app: (port, prefix)})."""
if way == 'processes':
a, info_a = started([serve, '--module', module, '--store', work / 'a', '--threads', 1])
b, info_b = started([serve, '--module', module, '--store', work / 'b', '--threads', 1])
return [a, b], {'A': (info_a['port'], ''), 'B': (info_b['port'], '')}
(work / 'apps.conf').write_text(f'a {module} {work / "a"}\nb {module} {work / "b"}\n')
host, info = started([serve, '--apps', work / 'apps.conf', '--threads', 1 if way == 'host1' else 2, '--workers', 2])
return [host], {'A': (info['port'], '/a'), 'B': (info['port'], '/b')}
def run(way, kind, seconds, serve, module, round_):
work = Path(tempfile.mkdtemp(prefix='pure-apps-', dir='/dev/shm'))
try:
processes, where = servers(way, work, serve, module)
try:
if kind == 'alone':
b = result(load(where['B'][0], 1, seconds, where['B'][1]))
a = None
elif kind == 'flood':
flood = load(where['A'][0], 64, seconds + 2, where['A'][1])
time.sleep(1)
b = result(load(where['B'][0], 1, seconds, where['B'][1]))
a = result(flood)
else:
both_a = load(where['A'][0], 32, seconds, where['A'][1])
both_b = load(where['B'][0], 32, seconds, where['B'][1])
a, b = result(both_a), result(both_b)
finally:
for process in processes:
stopped(process)
finally:
shutil.rmtree(work)
row = {'round': round_, 'way': way, 'kind': kind, 'b_p50_us': b['p50_us'], 'b_p99_us': b['p99_us'],
'b_per_second': b['requests_per_second'], 'b_5xx': b['5xx']}
if a:
row.update({'a_per_second': a['requests_per_second'], 'a_p99_us': a['p99_us'], 'a_5xx': a['5xx']})
return row
def main():
serve, rounds = Path(sys.argv[1]).resolve(), int(sys.argv[2])
seconds = float(sys.argv[3]) if len(sys.argv) > 3 else 15
work = Path(tempfile.mkdtemp(prefix='pure-apps-module-'))
try:
module = work / 'scores.pmod'
subprocess.run([str(AUP), 'build', str(ROOT / 'benchmarks' / 'http' / 'scores.aup'), '-o', str(module)], check=True)
for round_ in range(1, rounds + 1):
ways = ['processes', 'host', 'host1'] if round_ % 2 else ['host1', 'host', 'processes']
for kind in ('alone', 'flood', 'both'):
for way in ways:
print(json.dumps(run(way, kind, seconds, serve, module, round_)), flush=True)
finally:
shutil.rmtree(work)
if __name__ == '__main__':
main()
|