mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-10-18 11:33:23 +03:00
- expose whether native backend initialization has completed - distinguish completed initialization from backend availability - wait up to one second for asynchronous initialization in benchmark mode - exclude AT-SPI registration activity from notification timing - report backend readiness latency in benchmark results - verify disabled backends complete initialization immediately
152 lines
4.8 KiB
Python
152 lines
4.8 KiB
Python
#!/usr/bin/env python3
|
|
|
|
import argparse
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
import statistics
|
|
import subprocess
|
|
import sys
|
|
import time
|
|
|
|
|
|
def check(condition, message):
|
|
if not condition:
|
|
raise RuntimeError(message)
|
|
|
|
|
|
def run_sample(args, disabled):
|
|
environment = os.environ.copy()
|
|
if sys.platform == "darwin":
|
|
executable_directory = str(Path(args.executable).resolve().parent)
|
|
current_library_path = environment.get("DYLD_LIBRARY_PATH")
|
|
environment["DYLD_LIBRARY_PATH"] = (
|
|
executable_directory
|
|
if not current_library_path
|
|
else executable_directory + os.pathsep + current_library_path
|
|
)
|
|
if disabled:
|
|
environment["EEPP_DISABLE_ACCESSIBILITY"] = "1"
|
|
else:
|
|
environment.pop("EEPP_DISABLE_ACCESSIBILITY", None)
|
|
start = time.perf_counter()
|
|
completed = subprocess.run(
|
|
[
|
|
args.executable,
|
|
"--benchmark-inactive",
|
|
"--benchmark-iterations",
|
|
str(args.iterations),
|
|
],
|
|
check=True,
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=args.timeout,
|
|
env=environment,
|
|
)
|
|
wall_ms = (time.perf_counter() - start) * 1000
|
|
result = json.loads(completed.stdout)
|
|
result["wall_ms"] = wall_ms
|
|
result["initialization_ms"] = result.pop("initialization_us") / 1000
|
|
result["readiness_ms"] = result.pop("readiness_us") / 1000
|
|
result["notifications_ms"] = result.pop("notifications_us") / 1000
|
|
return result
|
|
|
|
|
|
def median_sample(samples):
|
|
return {
|
|
key: statistics.median(sample[key] for sample in samples)
|
|
for key in samples[0]
|
|
}
|
|
|
|
|
|
def collect_samples(args, first_index, count, enabled_samples, disabled_samples):
|
|
for index in range(first_index, first_index + count):
|
|
# Reverse each adjacent pair so neither mode consistently benefits from running second.
|
|
modes = (True, False) if index % 2 == 0 else (False, True)
|
|
for disabled in modes:
|
|
result = run_sample(args, disabled)
|
|
(disabled_samples if disabled else enabled_samples).append(result)
|
|
|
|
|
|
def summarize(enabled_samples, disabled_samples):
|
|
enabled = median_sample(enabled_samples)
|
|
disabled = median_sample(disabled_samples)
|
|
check(disabled["notifications_ms"] > 0, "disabled notification measurement was zero")
|
|
check(disabled["wall_ms"] > 0, "disabled wall-time measurement was zero")
|
|
return {
|
|
"disabled": disabled,
|
|
"enabled": enabled,
|
|
"raw_disabled": disabled_samples,
|
|
"raw_enabled": enabled_samples,
|
|
"notification_ratio": enabled["notifications_ms"] / disabled["notifications_ms"],
|
|
"wall_ratio": enabled["wall_ms"] / disabled["wall_ms"],
|
|
}
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Benchmark inactive accessibility overhead")
|
|
parser.add_argument("--executable", default="bin/eepp-ui-accessibility")
|
|
parser.add_argument("--iterations", type=int, default=100000)
|
|
parser.add_argument("--samples", type=int, default=5)
|
|
parser.add_argument("--confirmation-samples", type=int, default=10)
|
|
parser.add_argument("--timeout", type=float, default=10)
|
|
parser.add_argument("--max-initialization-ms", type=float, default=250)
|
|
parser.add_argument("--max-notification-ratio", type=float, default=1.01)
|
|
parser.add_argument("--max-wall-ratio", type=float, default=1.25)
|
|
parser.add_argument("--json", action="store_true")
|
|
args = parser.parse_args()
|
|
check(args.iterations > 0 and args.samples > 0, "iterations and samples must be positive")
|
|
check(args.confirmation_samples >= 0, "confirmation samples must not be negative")
|
|
|
|
enabled_samples = []
|
|
disabled_samples = []
|
|
collect_samples(args, 0, args.samples, enabled_samples, disabled_samples)
|
|
output = summarize(enabled_samples, disabled_samples)
|
|
if (
|
|
output["notification_ratio"] > args.max_notification_ratio
|
|
or output["wall_ratio"] > args.max_wall_ratio
|
|
) and args.confirmation_samples:
|
|
collect_samples(
|
|
args,
|
|
args.samples,
|
|
args.confirmation_samples,
|
|
enabled_samples,
|
|
disabled_samples,
|
|
)
|
|
output = summarize(enabled_samples, disabled_samples)
|
|
|
|
enabled = output["enabled"]
|
|
failed = (
|
|
enabled["initialization_ms"] > args.max_initialization_ms
|
|
or output["notification_ratio"] > args.max_notification_ratio
|
|
or output["wall_ratio"] > args.max_wall_ratio
|
|
)
|
|
if failed:
|
|
print(json.dumps(output, sort_keys=True), file=sys.stderr)
|
|
check(
|
|
enabled["initialization_ms"] <= args.max_initialization_ms,
|
|
"inactive native backend initialization exceeded threshold",
|
|
)
|
|
check(
|
|
output["notification_ratio"] <= args.max_notification_ratio,
|
|
"inactive accessibility notification overhead exceeded threshold",
|
|
)
|
|
check(
|
|
output["wall_ratio"] <= args.max_wall_ratio,
|
|
"inactive accessibility wall-time overhead exceeded threshold",
|
|
)
|
|
if args.json:
|
|
print(json.dumps(output, sort_keys=True))
|
|
else:
|
|
print(
|
|
"Inactive accessibility benchmark passed: "
|
|
f"initialization {enabled['initialization_ms']:.3f}ms, "
|
|
f"notifications {enabled['notifications_ms']:.3f}ms "
|
|
f"({output['notification_ratio']:.3f}x disabled), "
|
|
f"wall {output['wall_ratio']:.3f}x disabled"
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|