#!/usr/bin/env python3 """Check and summarize all ten completed URL-fix measurements.""" import hashlib import json from pathlib import Path ROOT = Path(__file__).resolve().parents[2] OUT = ROOT / 'tests/results/portal/performance-suite' def sha(path): with path.open('rb') as stream: return hashlib.file_digest(stream, 'sha256').hexdigest() def main(): run = json.loads((OUT / 'report.json').read_text()) assert run['success'] and len(run['commands']) == 5 and all(row['exitCode'] == 0 for row in run['commands']) assert run['runnerSha256'] == sha(OUT / 'runner-at-start.py') binaries = {name: sha(ROOT / 'build' / name) for name in ('docview', 'docview-pdf-worker', 'docview-archive-worker')} assert all(run['binaries'][name] == value for name, value in binaries.items()) rows, operation_count = [], 0 for corpus, formats in [('smoke', ['pdf', 'html', 'zip', 'epub']), ('standard', ['pdf', 'pdf-headings', 'html', 'zip', 'epub']), ('fonts', ['pdf'])]: for kind in formats: relative = corpus + '/' + kind + '.json' value = json.loads((OUT / relative).read_text()) assert value['success'] and value['originalUnchanged'] and all(value['measurementChecks'].values()) assert value['executableSha256'] == binaries and value['runnerSha256'] == sha(ROOT / 'tests/benchmark.py') assert len(value['openFrameMs']) == 30 and value['coldProcessOpen']['requested'] == 30 and value['coldProcessOpen']['failed'] == 0 assert value['summary']['uniform_open_close_cycles'] == 29 and value['summary']['all_closes_empty'] display = value['display'] assert display['screenLogicalWidth'] == 1100 and display['screenLogicalHeight'] == 760 and display['windowDevicePixelRatio'] == 1 cold = list((OUT / corpus / 'cold' / kind).glob('[0-9][0-9][0-9].json')) assert len(cold) == 30 for path in cold: sample = json.loads(path.read_text()) assert sample['success'] and sample['documentSha256'] == value['documentSha256'] assert sample['display'] == display and sample['maximumMemoryPressureLevel'] == 0 operation_count += len(value['operations']) summary = value['summary'] exceeded = [name for name, observation in value['proposedBudgetObservations'].items() if observation is False or isinstance(observation, dict) and observation.get('observed_met') is False] rows.append({'corpus': corpus, 'format': kind, 'record': relative, 'recordSha256': sha(OUT / relative), 'coldOpenP95Ms': summary['fresh_process_fresh_app_cache_open']['p95_ms'], 'responseP95Ms': summary['response']['p95_ms'], 'finalQualityP95Ms': summary['final_quality']['p95_ms'], 'scrollP95Ms': summary['continuous_scroll_frames']['p95_ms'], 'processGroupPeakMiB': value['processGroupRssPeakBytes'] / 1024**2, 'budgetObservationsExceeded': exceeded, 'navigationCoverage': value['navigationCoverage']}) interaction = json.loads((OUT / 'interaction/record.json').read_text()) stress = json.loads((OUT / 'stress/report.json').read_text()) measured = {row['path']: row['sha256'] for row in interaction['binaries']} assert interaction['success'] and measured['test_app'] == sha(ROOT / 'build/test_app') assert all(measured[name] == digest for name, digest in binaries.items()) assert stress['success'] and stress['originalUnchanged'] and stress['sourceRemoved'] assert stress['executableSha256'] == binaries and stress['executablesUnchanged'] result = {'success': True, 'buildSha256': binaries, 'cases': rows, 'coldOpens': 300, 'sameProcessOpens': 300, 'summaryGeneratorSha256': sha(Path(__file__)), 'navigationOperations': operation_count, 'scope': run['scope'], 'interactionRecordSha256': sha(OUT / 'interaction/record.json'), 'stressRecordSha256': sha(OUT / 'stress/report.json'), 'proposedBudgetExceedances': sum(len(row['budgetObservationsExceeded']) for row in rows)} (OUT / 'summary.json').write_text(json.dumps(result, ensure_ascii=False, indent=2) + '\n') lines = ['# URL比較修正後の性能測定', '', '10条件で新規プロセス・空のXDGアプリキャッシュ30回、同一プロセス30回、適用される移動系列125操作、連続scroll240入力を実施した。原本不変、全closeの状態解放、メモリ圧迫なしを確認した。', '', 'ArchのXvfb 1100×760、DPR1、software描画。OSファイルキャッシュは保持。起動後のopenを起点とし、プロセス起動時間を含めない。Qt提示通知の測定で、Webの対象画素や物理scanoutの直接測定ではない。基準機・対象OSの性能受入とは区別する。', '', '| 資料 | 形式 | 冷open p95 ms | 応答 p95 ms | 最終品質 p95 ms | scroll p95 ms | 群RSS最大 MiB |', '|---|---|---:|---:|---:|---:|---:|'] for row in rows: lines.append(f"| {row['corpus']} | [{row['format']}]({row['record']}) | {row['coldOpenP95Ms']:.2f} | {row['responseP95Ms']:.2f} | {row['finalQualityP95Ms']:.2f} | {row['scrollP95Ms']:.2f} | {row['processGroupPeakMiB']:.2f} |") lines += ['', f"新規open 300回・同一プロセスopen 300回・移動系列{operation_count}操作。暫定予算の参考超過は{result['proposedBudgetExceedances']}件。元の予算値は変更していない。未測定の系列は各JSONのnavigationCoverageで区別する。", '', '[実行コマンド](report.json)、[機械集計](summary.json)、[操作負荷](interaction/record.json)、[約1GB・5,000ページPDF](stress/report.json)を保存した。重いPDFは3open・100操作の追加負荷であり、30回の性能受入標本ではない。', ''] (OUT / 'README.md').write_text('\n'.join(lines)) print(json.dumps({key: result[key] for key in ('success', 'coldOpens', 'sameProcessOpens', 'navigationOperations', 'proposedBudgetExceedances')})) if __name__ == '__main__': main()