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
|
#!/usr/bin/env python3
"""Measure repeated durable edits and page-segment rebuilds on one real document."""
import argparse
import hashlib
import json
from pathlib import Path
import platform
import statistics
import subprocess
import tempfile
from measure import INITIAL, PROGRAM, invoke
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--flat', type=Path, required=True)
parser.add_argument('--paged', type=Path, required=True)
parser.add_argument('--scratch-parent', type=Path, required=True)
parser.add_argument('--edits', type=int, default=70)
parser.add_argument('--output', type=Path, required=True)
args = parser.parse_args()
if args.edits < 66 or args.output.exists():
parser.error('at least 66 edits are needed to observe the current rebuild boundary; output must be new')
binaries = {'flat': args.flat.resolve(), 'paged': args.paged.resolve()}
args.scratch_parent.mkdir(parents=True, exist_ok=True)
with tempfile.TemporaryDirectory(prefix='pure-storage-sequence-',
dir=args.scratch_parent.resolve()) as name:
root = Path(name)
filesystem = subprocess.run(['findmnt', '-T', str(root), '-no', 'FSTYPE,SOURCE'],
capture_output=True, text=True, check=True).stdout.strip()
(root / 'initial').write_bytes(INITIAL)
patches = {'Y': root / 'Y', 'Z': root / 'Z'}
for label, file in patches.items():
file.write_bytes(label.encode())
bases = {variant: [binary, '--program', PROGRAM, '--store', root / variant]
for variant, binary in binaries.items()}
for base in bases.values():
result, _ = invoke([*base, 'edit', 'document', 0, 0, 0, root / 'initial'])
assert json.loads(result.stdout)['revision'] == '1'
samples = {variant: [] for variant in binaries}
for edit in range(1, args.edits + 1):
patch = 'Z' if edit % 2 else 'Y'
order = ('flat', 'paged') if edit % 2 else ('paged', 'flat')
for variant in order:
base = bases[variant]
result, elapsed = invoke([*base, 'edit', 'document', edit,
len(INITIAL) - 1, 1, patches[patch]])
assert json.loads(result.stdout)['revision'] == str(edit + 1)
files = [file for file in (root / variant).iterdir() if file.is_file()]
samples[variant].append({'edit': edit, 'elapsed_ms': round(elapsed, 3),
'files': len(files),
'allocated_bytes': sum(file.stat().st_blocks * 512
for file in files)})
expected = INITIAL[:-1] + patches[patch].read_bytes()
for base in bases.values():
shown, _ = invoke([*base, 'show', 'document'])
assert shown.stdout == expected
assert json.loads(shown.stderr)['revision'] == str(args.edits + 1)
result = {'profile': 'repeated-durable-one-mib-document-last-byte-edit',
'platform': platform.platform(), 'filesystem': filesystem,
'edits_per_variant': args.edits,
'final_body_sha256': hashlib.sha256(expected).hexdigest(),
'scope': ('Same actual source-loaded CLI and editor.pure, two persistent '
'stores, alternating operation order. Timings include each '
'edit process, parser, store init and fsync; creation and '
'final show are not timed. Device state is uncontrolled.'),
'variants': {variant: {
'binary_sha256': hashlib.sha256(binary.read_bytes()).hexdigest(),
'median_edit_ms': round(statistics.median(row['elapsed_ms']
for row in samples[variant]), 3),
'samples': samples[variant]}
for variant, binary in binaries.items()}}
args.output.parent.mkdir(parents=True, exist_ok=True)
args.output.write_text(json.dumps(result, indent=2) + '\n')
print(json.dumps({variant: {'median_edit_ms': row['median_edit_ms'],
'max_edit_ms': max(sample['elapsed_ms']
for sample in row['samples']),
'max_allocated_bytes': max(sample['allocated_bytes']
for sample in row['samples'])}
for variant, row in result['variants'].items()}, indent=2))
if __name__ == '__main__':
main()
|