Files
Iris/tools/compare-native-generation.py
Brian Neumann-Fopiano 8d5cf43139 Enable native noise generation
- Enable exact Rust Billow sampling and report the selected backend.
- Add generation parity probes and native comparison tooling.
2026-10-04 09:59:58 -04:00

100 lines
5.6 KiB
Python

import argparse
import fcntl
import json
import os
from pathlib import Path
import statistics
import subprocess
import time
def fields(line):
return dict(part.split('=', 1) for part in line.split()[1:])
def main():
parser = argparse.ArgumentParser()
parser.add_argument('--pack', required=True)
parser.add_argument('--dimension', required=True)
parser.add_argument('--volmlib', required=True)
parser.add_argument('--seed', type=int, default=1337)
parser.add_argument('--center-x', type=int, default=2048)
parser.add_argument('--center-z', type=int, default=2048)
parser.add_argument('--warmup', type=int, default=64)
parser.add_argument('--measured', type=int, default=192)
parser.add_argument('--pairs', type=int, default=2)
parser.add_argument('--diagnostics', action='store_true')
parser.add_argument('--output', required=True)
args = parser.parse_args()
if args.pairs < 1:
parser.error('--pairs must be positive')
root = Path(__file__).resolve().parents[1]
output = Path(args.output).resolve()
if not any(output.is_relative_to(root / allowed) for allowed in ('.perf', 'build')):
parser.error('--output must be inside this checkout\'s .perf or build directory')
output.mkdir(parents=True, exist_ok=False)
environment = os.environ.copy()
environment.pop('GIT_CONFIG_COUNT', None)
for variable in ('JAVA_TOOL_OPTIONS', '_JAVA_OPTIONS', 'JDK_JAVA_OPTIONS'):
environment.pop(variable, None)
results = []
expected_hashes = None
with open('/tmp/iris-acceleration-integration-benchmark.lock', 'a') as lock:
print('Waiting for generation benchmark lock', flush=True)
fcntl.flock(lock, fcntl.LOCK_EX)
for pair in range(args.pairs):
for native in ([False, True] if pair % 2 == 0 else [True, False]):
backend = 'native' if native else 'java'
command = [str(root / 'gradlew'), ':probe:genProbe', '--no-daemon',
'-PincludeNativeBillow=true', f'-PlocalVolmLibDirectory={args.volmlib}',
f'-PprobePack={Path(args.pack).resolve()}', f'-PprobeDimension={args.dimension}',
f'-PprobeSeed={args.seed}', f'-PprobeCenterChunkX={args.center_x}',
f'-PprobeCenterChunkZ={args.center_z}', f'-PprobeWarmupChunks={args.warmup}',
f'-PprobeMeasuredChunks={args.measured}', '-PprobeMulticore=false',
'-PprobeStudio=false', f'-PprobeNativeBillow={str(native).lower()}',
'-PprobeNativeAccess=true',
f'-PprobeNativeDiagnostics={str(args.diagnostics).lower()}',
f'-PprobeRequireNativeSamples={str(native and args.diagnostics).lower()}',
f'-PprobeExpectedBackend={"native" if native else "java-disabled"}']
label = f'{pair + 1}-{backend}'
(output / f'{label}.command.json').write_text(json.dumps(command, indent=2) + '\n')
print(f'Running {label}', flush=True)
started = time.monotonic()
with (output / f'{label}.log').open('w') as log:
completed = subprocess.run(command, cwd=root, env=environment, stdout=log,
stderr=subprocess.STDOUT, check=False)
text = (output / f'{label}.log').read_text()
lines = text.splitlines()
hashes = [line for line in lines if line.startswith('GENPROBE_CHUNK_HASH ')]
result_lines = [line for line in lines if line.startswith('IRIS_GENPROBE_RESULT ')]
backend_lines = [line for line in lines if line.startswith('IRIS_GENPROBE_BACKEND ')]
if completed.returncode != 0 or len(result_lines) != 1 or len(backend_lines) != 1:
raise RuntimeError(f'{label} failed; see {output / (label + ".log")}')
result = fields(result_lines[0])
status = fields(backend_lines[0])
if result['status'] != 'PASS' or len(hashes) != args.warmup + args.measured:
raise RuntimeError(f'{label} did not generate every chunk successfully')
expected_backend = 'native' if native else 'java-disabled'
if status['status'] != expected_backend:
raise RuntimeError(f'{label} backend mismatch: {status}')
if expected_hashes is None:
expected_hashes = hashes
elif hashes != expected_hashes:
raise RuntimeError(f'{label} chunk block/biome hashes differ')
results.append({'pair': pair + 1, 'backend': backend, 'result': result,
'backend_status': status, 'wall_seconds': time.monotonic() - started})
(output / 'results.json').write_text(json.dumps(results, indent=2) + '\n')
rates = {backend: statistics.median(float(run['result']['measured_cps']) for run in results
if run['backend'] == backend) for backend in ('java', 'native')}
summary = {'median_chunks_per_second': rates,
'native_throughput_change_percent': (rates['native'] / rates['java'] - 1) * 100,
'all_chunk_hashes_equal': True, 'diagnostics_enabled': args.diagnostics,
'run_count': len(results), 'seed': args.seed,
'unique_chunks': args.warmup + args.measured}
(output / 'summary.json').write_text(json.dumps(summary, indent=2) + '\n')
print(json.dumps(summary, indent=2))
if __name__ == '__main__':
main()