Files
Iris/docs/33 - Performance Tuning.md
T
2026-08-08 00:29:48 -06:00

115 lines
8.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 33 - Performance Tuning
Iris throughput is dominated by generation threads, mantle residency, pregen in-flight limits, cache sizes, and optional SIMD kernels. All knobs below live in `settings.json` under the Iris data directory unless noted. Settings overview: `03 - Configuration.md`. Pregen operations: `07 - Pregeneration.md`. Determinism must stay intact after tuning — verify with GoldenHash (`32 - Determinism & Goldenhash.md`).
## Where settings live
| Platform | Data directory | Settings file |
|----------|----------------|---------------|
| Bukkit-family | `plugins/Iris/` | `settings.json` |
| Fabric / Forge / NeoForge | loader config `iris/` | `settings.json` |
Hotload behavior for settings follows `03 - Configuration.md`. After changing SIMD or thread-pool related keys, restart if values are read once at kernel install / pool creation.
## Performance section (`performance`)
| Key | Default | Role |
|-----|---------|------|
| `performance.simdKernels` | `true` | When true and `jdk.incubator.vector` is available, use vector kernels; otherwise scalar fallbacks |
| `performance.mantleKeepAlive` | `30` | Mantle plate keep-alive window (seconds-scale residency control used by mantle lifecycle) |
| `performance.mantleCleanupDelay` | `200` | Delay before mantle cleanup work |
| `performance.trimMantleInStudio` | `false` | When true, studio worlds trim mantle more aggressively |
| `performance.noiseCacheSize` | `1024` | Noise sample cache capacity |
| `performance.resourceLoaderCacheSize` | `1024` | Pack resource loader cache capacity |
| `performance.objectLoaderCacheSize` | `4096` | Object (`.iob`) loader cache capacity |
| `performance.engineSVC.useVirtualThreads` | `true` | Engine service uses virtual threads when true |
| `performance.engineSVC.forceMulticoreWrite` | `false` | Force multicore write path |
| `performance.engineSVC.priority` | `Thread.NORM_PRIORITY` | Clamped to valid Java thread priorities |
| `performance.engineSVC.parallelism` | `-1` | `>0` caps at `2 * CPU`; `≤0` uses `ceil(sqrt(CPU))` |
Larger loader caches trade heap for fewer pack disk/JSON reloads during generation. Raise `objectLoaderCacheSize` when pregen is object-heavy and the pack is large; lower caches if heap pressure shows retained pack data.
## Pregen section (`pregen`)
| Key | Default | Role |
|-----|---------|------|
| `pregen.runtimeSchedulerMode` | `AUTO` | Bukkit pregen scheduler mode: `AUTO`, `PAPER_LIKE`, `FOLIA` (Folia runtime always resolves to Folia scheduling) |
| `pregen.paperLikeBackendMode` | `AUTO` | Paper-like backend: `AUTO`, `TICKET`, `SERVICE` |
| `pregen.chunkLoadTimeoutSeconds` | `15` | Clamped 5120 |
| `pregen.timeoutWarnIntervalMs` | `500` | Minimum 250 ms between timeout warnings |
| `pregen.saveIntervalMs` | `30000` | Clamped 5_000900_000 |
| `pregen.maxResidentTectonicPlates` | `96` | Soft cap (effective floor 16) on resident mantle tectonic plates |
| `pregen.mantleBackpressureWaitMs` | `25` | Clamped 51000; wait when mantle backpressure engages |
| `pregen.mantleBackpressureTimeoutMs` | `60000` | Clamped 5_000600_000 |
| `pregen.moddedPregenInFlight` | `0` | `0` = auto `clamp(16, cpu*2, 48)`; positive values clamp to 1512 |
Effective resident plates also scale with world height and process heap: higher worlds and smaller heaps reduce the effective plate count (minimum 16). If pregen stalls with mantle pressure, lower concurrency first, then reduce `maxResidentTectonicPlates`, or raise heap so the byte budget allows more plates.
Related world flag: `world.globalPregenCache` (default `false`) — global pregen cache behavior; see `03 - Configuration.md` / `07 - Pregeneration.md`.
## Concurrency helpers (`concurrency`)
`IrisSettingsConcurrency` exposes derived counts (not all are free-form JSON knobs with independent storage in every build path):
- World-gen style parallelism floors at `max(2, availableProcessors)`.
- IO parallelism floors at `max(2, availableProcessors / 2)`.
Prefer pregen in-flight limits and `engineSVC.parallelism` for production tuning rather than inventing extra thread pools outside settings.
## SIMD
**Note:** Broader SIMD coverage (including full noise-kernel wiring through production worldgen) is actively being worked on. Array kernels used on some hot paths already honor `performance.simdKernels` when the incubator Vector API is available; treat noise SIMD as incomplete until that work lands.
Runtime selection (`SimdSupport`) today:
1. If `performance.simdKernels` is false → scalar kernels.
2. Else if module `jdk.incubator.vector` is present and vector kernel classes load → vector kernels for **array ops** used in some generation hot paths (for example `roundToInt` via `ChunkedDoubleDataCache`, carving paths via `MantleCarvingComponent`).
3. Else → scalar kernels; startup log tells the operator to add `--add-modules jdk.incubator.vector`.
2D fractal noise vector kernels exist (`VectorNoiseKernels2D`) and are gated to CPUs where `double` vector width is profitable (≥ 4 lanes). That path is selected by `SimdSupport.noiseKernels2D()` but is **not** the primary wired worldgen path yet.
JVM flag (required for vector API incubator):
```
--add-modules jdk.incubator.vector
```
Server start scripts and Gradle run configs for Iris already pass this where Iris launches the JVM. Standalone microbench: `tools/simd-bench/` (`./run.sh` or `run.bat`). That tool force-measures kernels even when Iris would gate noise SIMD off (for example Apple Silicon 2-lane NEON). Microbench speedups do not guarantee end-to-end pregen gains.
To A/B SIMD on a full server: set `performance.simdKernels` false, restart, measure pregen chunks/s, re-enable, restart, remeasure. Confirm GoldenHash unchanged (`32 - Determinism & Goldenhash.md`).
## Operator pregen modes that affect load
| Mode | Platform | Effect |
|------|----------|--------|
| Default pregen | All | Concurrent generation within platform scheduler limits |
| `serial=true` | Bukkit Paper-compatible only | Strict one-in-flight chunk pregen; rejected on non-Paper serial support |
| `sync` / in-flight flags | Modded | Synchronous or capped async pregen; see `07 - Pregeneration.md` |
| `moddedPregenInFlight` | Modded | Caps concurrent pregen chunk work |
For profiling and determinism isolation, prefer serial/sync one-in-flight runs. For production throughput, use default concurrency and raise heap before raising mantle plate caps.
## Practical tuning order
1. **Heap and GC** — give the process enough heap for pack caches + mantle plates (release smoke used 8 GiB heap on large pregens; size to hardware).
2. **Confirm SIMD module** — check startup log for `SIMD: vector kernels enabled` vs scalar message.
3. **Pregen concurrency** — use default; only lower in-flight / use serial when CPU saturated or region scheduling warns.
4. **Mantle residency** — if backpressure timeouts appear, reduce `maxResidentTectonicPlates` or pregen speed; increase heap if plates thrash.
5. **Caches** — raise object/resource caches when the same objects reload repeatedly; lower if heap retains too much after pregen.
6. **engineSVC.parallelism** — set an explicit positive value only after measuring; `-1` already scales with CPU via `ceil(sqrt(n))`.
7. **Never “tune” by changing pack content** for performance without a GoldenHash re-baseline — pack edits change terrain.
## Measurement checklist
Record for each experiment: pack identity, seed, radius, serial/sync flags, JVM version/flags, heap, CPU, `settings.json` performance/pregen excerpts, chunks/second, duration, failed chunks, peak heap, and GoldenHash combined value. Reject optimizations that change hashes unless the behavior change is intentional and documented. Larger release-style baselines (5k10k chunks, JProfiler) are tracked in `87 - Maintainer - Release Readiness.md`.
## Offline tools
| Tool | Command | Use |
|------|---------|-----|
| Generation probe | `./gradlew :probe:genProbe -PprobePack=…` | Headless engine generate; not a throughput benchmark |
| Classload probe | `./gradlew :probe:run` | Purity/classload gate |
| SIMD microbench | `tools/simd-bench/./run.sh` | Kernel-only scalar vs vector timing |
Smoke procedures that combine pregen and GoldenHash: `31 - Operator Runbooks & Smoke Tests.md`.