mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-08-27 12:41:43 +00:00
d
This commit is contained in:
+102
-93
@@ -1,136 +1,145 @@
|
||||
# 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`).
|
||||
Iris throughput is bounded by four things: how many chunks the platform will let Iris generate at once, how much mantle stays resident in heap, how often pack resources are reloaded from disk, and whether the JVM has the incubator Vector API. This page is organized by the symptom you are looking at, not by settings file order. Every knob lives in `settings.json` under the Iris data directory (`03 - Configuration.md`); pregen operations are in `07 - Pregeneration.md`. Any change here must leave GoldenHash unchanged (`32 - Determinism & Goldenhash.md`).
|
||||
|
||||
## Tutorial: measure one tuning change
|
||||
## Before you turn any knob
|
||||
|
||||
Choose the first knob from observed evidence, not from hardware size alone:
|
||||
Most bad tuning comes from changing three things, seeing a better number once, and keeping all three. Do this instead:
|
||||
|
||||
| Observed limit | First controlled change |
|
||||
|---|---|
|
||||
| Mantle backpressure or timeout warnings | Lower pregen in-flight concurrency; then test a lower `maxResidentTectonicPlates` if heap pressure remains |
|
||||
| High retained heap or long GC pauses | Lower loader caches and resident plate cap; do not raise concurrency |
|
||||
| Repeated object/resource loading with spare heap | Raise only the cache that is missing (`objectLoaderCacheSize` or `resourceLoaderCacheSize`) |
|
||||
| CPU-bound generation with stable heap | Confirm the Vector API module and A/B `simdKernels`; benchmark `engineSVC.parallelism` only afterward |
|
||||
| Region scheduler warnings or chunk-load timeouts | Lower pregen concurrency or use serial/sync isolation before increasing timeout values |
|
||||
| Studio memory growth during repeated edits | A/B `trimMantleInStudio` in Studio only |
|
||||
1. Freeze the inputs: Iris artifact, pack bytes, seed, center, radius, JVM flags, and server population.
|
||||
2. Run one warmup, then three measured runs. Record chunks/second, wall time, peak heap, GC behavior, and failed chunk count.
|
||||
3. Change exactly one setting. Restart if the setting is read once at startup — thread pools, caches, and SIMD kernel selection all are.
|
||||
4. Repeat the warmup and three runs over the same area. A comparison across different terrain is not a comparison.
|
||||
5. Keep the change only if the median improves with no determinism mismatch, no new failures, no unacceptable heap growth, and no worse tick latency.
|
||||
6. Restore the old value before testing the next knob.
|
||||
|
||||
1. Freeze the Iris artifact, pack bytes, seed, center, radius, Java flags, and server population.
|
||||
2. Run one warmup, then record at least three baseline runs with chunk throughput, wall time, peak heap, GC behavior, failed chunks, and GoldenHash.
|
||||
3. Change one setting from the tables below and restart if that setting constructs a pool, cache, or SIMD kernel.
|
||||
4. Repeat the same warmup and three measured runs. Reject comparisons that used different generated areas or active plugins.
|
||||
5. Keep the change only when the median improves without a determinism mismatch, new failure, unacceptable memory growth, or worse tick latency.
|
||||
6. Restore the previous value before testing the next knob.
|
||||
Reach for JProfiler when the numbers move without an obvious cause: stalls, allocation pressure, or scheduler behavior. A faster pregen status line on its own doesn't tell you why.
|
||||
|
||||
Use JProfiler for stalls, allocation pressure, scheduler behavior, or unexplained regressions. A faster pregen status line by itself is not enough evidence for a production tuning recommendation.
|
||||
## Symptom: pregen is slow
|
||||
|
||||
## Where settings live
|
||||
Work through these in order. The first two are free; the rest trade something.
|
||||
|
||||
| Platform | Data directory | Settings file |
|
||||
|----------|----------------|---------------|
|
||||
| Bukkit-family | `plugins/Iris/` | `settings.json` |
|
||||
| Fabric / Forge / NeoForge | loader config `iris/` | `settings.json` |
|
||||
1. **Check whether the platform is the limit, not Iris.** On Fabric, Forge, and NeoForge without a parallel chunk system, pregen runs through the vanilla main-thread chunk pipeline and throughput is capped there regardless of settings. Iris logs this at pregen start and names the fix: install C2ME on Fabric, or run Paper if you want Bukkit-level throughput. No Iris setting recovers that gap.
|
||||
2. **Confirm SIMD is on.** On Bukkit, the startup log prints one of `SIMD: vector kernels enabled (…)`, `SIMD: scalar kernels active; add --add-modules jdk.incubator.vector …`, or `SIMD: vector kernels disabled (performance.simdKernels=false)`. If you see the scalar message, add the JVM flag and restart. See the SIMD section for what it actually accelerates and how small that surface is. Mod loaders never print this line, so check the JVM flag directly there.
|
||||
3. **Leave concurrency alone unless it is warning at you.** Bukkit pregen concurrency is derived, not configured: Iris sizes it from the detected chunk-system worker pool (or CPU count) times 8, clamped to 16–128 on Paper-like servers and 64–192 on Folia. Raising it is not an option, and the adaptive limiter already lowers it when mantle backpressure engages. The only concurrency lever on Bukkit is `serial=true`, which drops to one chunk in flight — use it for profiling and determinism isolation, never for throughput.
|
||||
4. **On mod loaders, size `pregen.moddedPregenInFlight` to the chunk system.** Default `0` resolves to `clamp(16, cpu*2, 48)`, and whatever value comes out is floored at 8. Raise it only if the loader has a parallel chunk system and the CPU is not saturated; lower it if you see chunk-load timeouts. Positive values are capped at 512.
|
||||
5. **Raise the object cache if the same objects keep reloading.** `performance.objectLoaderCacheSize` (default 4096) bounds the loader caches for `.iob` objects, matter objects, and images. Object-heavy packs on large pregens hit this. The tradeoff is retained heap, so only do this if heap has room — see the memory section.
|
||||
6. **Give the process more heap before touching mantle caps.** Resident mantle plates are budgeted against process memory, so a bigger heap raises the effective plate count without any settings change.
|
||||
|
||||
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.noiseCacheSize` is not worth tuning for pregen: starting a pregen raises it to at least 4096 in memory, hotloads the engine, and sets `iris.cache.fast` as a system property. Neither is lowered again for the life of the process. The Bukkit plugin already sets `iris.cache.fast` during startup; on mod loaders it only comes on with the first pregen, so pass `-Diris.cache.fast=true` on the JVM command line there if you want it covering ordinary generation too.
|
||||
|
||||
## Performance section (`performance`)
|
||||
## Symptom: TPS dips or chunk-load timeouts while generating
|
||||
|
||||
| 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))` |
|
||||
Generation competing with the server tick shows up as timeout warnings, region scheduler complaints, or players reporting lag near the pregen frontier.
|
||||
|
||||
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.
|
||||
| Do this | Effect | Cost |
|
||||
|---|---|---|
|
||||
| Run pregen with `serial=true` (Bukkit, Paper-compatible) or `sync` (modded) | One chunk in flight at a time; the tick thread stops competing with a wide generation front | Much slower pregen; this is an isolation tool, not a production mode |
|
||||
| Lower `pregen.moddedPregenInFlight` (modded only) | Fewer concurrent chunk generations, so the chunk system keeps headroom for player chunks | Proportionally slower pregen |
|
||||
| Raise `pregen.chunkLoadTimeoutSeconds` (default 15, clamped 5–120) | Iris waits longer before declaring a chunk load stuck and warning | Hides a real stall instead of fixing it; try it last. Modded pregen ignores anything below 120 seconds |
|
||||
| Raise `pregen.timeoutWarnIntervalMs` (default 500, minimum 250) | Spaces out repeated timeout warnings in console | Log noise only; changes nothing about the stall |
|
||||
| Raise `pregen.saveIntervalMs` (default 30000, clamped 5000–900000) | Less frequent pregen state flushing, so less periodic IO | More work replayed if the job is interrupted |
|
||||
|
||||
## Pregen section (`pregen`)
|
||||
`pregen.runtimeSchedulerMode` (`AUTO`, `PAPER_LIKE`, `FOLIA`) and `pregen.paperLikeBackendMode` (`AUTO`, `TICKET`, `SERVICE`) exist for platform mismatches, not throughput. A Folia runtime always resolves to Folia scheduling regardless of the setting, and `AUTO` on Paper-like servers resolves to the ticket backend. Change these only when diagnosing a scheduler-specific defect.
|
||||
|
||||
| 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 5–120 |
|
||||
| `pregen.timeoutWarnIntervalMs` | `500` | Minimum 250 ms between timeout warnings |
|
||||
| `pregen.saveIntervalMs` | `30000` | Clamped 5_000–900_000 |
|
||||
| `pregen.maxResidentTectonicPlates` | `96` | Soft cap (effective floor 16) on resident mantle tectonic plates |
|
||||
| `pregen.mantleBackpressureWaitMs` | `25` | Clamped 5–1000; wait when mantle backpressure engages |
|
||||
| `pregen.mantleBackpressureTimeoutMs` | `60000` | Clamped 5_000–600_000 |
|
||||
| `pregen.moddedPregenInFlight` | `0` | `0` = auto `clamp(16, cpu*2, 48)`; positive values clamp to 1–512 |
|
||||
## Symptom: heap pressure, long GC pauses, or OOM risk
|
||||
|
||||
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.
|
||||
Mantle is the largest thing Iris keeps in heap. Iris already reacts to heap pressure on its own: as used heap climbs from 82% to 92%, the idle window before a mantle plate is trimmed shrinks linearly to zero, and above 96% Iris requests a reclaim (at most once every 30 seconds). If you are seeing pressure, that machinery is already running — you are deciding how much less mantle to hold.
|
||||
|
||||
Related world flag: `world.globalPregenCache` (default `false`) — global pregen cache behavior; see `03 - Configuration.md` / `07 - Pregeneration.md`.
|
||||
1. **Raise heap first if the machine has it.** The resident-plate budget is computed from process memory: roughly 60% of the heap, against a per-plate cost of about 48 MB at a 384-block world height, scaled by your actual dimension height. More heap means more plates without changing a setting.
|
||||
2. **Lower `pregen.maxResidentTectonicPlates`** (default 96). This is a soft cap on how many mantle tectonic plates stay resident. The effective number is the smaller of that cap, a height-scaled version of it, and the heap budget above — with a hard floor of 16. Taller worlds get fewer plates automatically. Lowering it cuts retained heap at the cost of more mantle reload work.
|
||||
3. **Lower `performance.mantleKeepAlive`** (default 30). This is how many seconds an idle mantle plate survives before maintenance trims it. Lower means memory comes back sooner; it also means recently-touched regions get re-read more often.
|
||||
4. **Lower the loader caches** if a heap dump shows retained pack data rather than mantle: `performance.objectLoaderCacheSize` (default 4096) and `performance.resourceLoaderCacheSize` (default 1024).
|
||||
5. **Slow the pregen down.** Backpressure knobs decide how long a generation thread waits when the mantle plate budget is full: `pregen.mantleBackpressureWaitMs` (default 25, clamped 5–1000) is the wait between retries, and `pregen.mantleBackpressureTimeoutMs` (default 60000, clamped 5000–600000) is how long it waits before giving up on that chunk. Raising the timeout buys a slow job time to finish instead of failing chunks; it does not reduce memory use.
|
||||
|
||||
## Concurrency helpers (`concurrency`)
|
||||
`performance.engineSVC.forceMulticoreWrite` (default false) makes mantle plate unloading use the parallel path all the time instead of only under heap pressure. It returns memory faster during sustained generation and costs CPU that would otherwise go to generating.
|
||||
|
||||
`IrisSettingsConcurrency` exposes derived counts (not all are free-form JSON knobs with independent storage in every build path):
|
||||
## Symptom: Studio memory keeps growing during editing
|
||||
|
||||
- World-gen style parallelism floors at `max(2, availableProcessors)`.
|
||||
- IO parallelism floors at `max(2, availableProcessors / 2)`.
|
||||
Studio worlds deliberately skip mantle trimming and per-chunk mantle cleanup, so a long authoring session accumulates mantle that a normal world would have released. Set `performance.trimMantleInStudio` to `true` to make studio worlds maintain mantle like any other world. The cost is that hotloaded pack edits will regenerate more from scratch because less is cached. A/B this in Studio only; it has no effect on production worlds.
|
||||
|
||||
Prefer pregen in-flight limits and `engineSVC.parallelism` for production tuning rather than inventing extra thread pools outside settings.
|
||||
## Symptom: the same pack resources reload constantly
|
||||
|
||||
`performance.resourceLoaderCacheSize` (default 1024) bounds the cache of parsed JSON pack resources; `performance.objectLoaderCacheSize` (default 4096) bounds `.iob`, matter, and image loaders. If profiling shows repeated parse or disk work for resources you know are in use, raise the one that is actually missing, one at a time. Both trade heap for fewer reloads, and neither changes generation output.
|
||||
|
||||
## Reference: `performance` section
|
||||
|
||||
| Key | Default | What it does |
|
||||
|-----|---------|--------------|
|
||||
| `performance.simdKernels` | `true` | Allows vector kernels when `jdk.incubator.vector` is on the module path; `false` forces scalar. Read once at class initialization, so a restart is required |
|
||||
| `performance.mantleKeepAlive` | `30` | Seconds an idle mantle plate survives before maintenance trims it. Shrinks toward zero as used heap climbs from 82% to 92% |
|
||||
| `performance.mantleCleanupDelay` | `200` | Ticks a loaded chunk waits before its mantle cleanup runs (200 = 10 s). Raising it keeps mantle data resident longer after chunk loads; see "03 - Configuration.md" |
|
||||
| `performance.trimMantleInStudio` | `false` | Whether studio worlds get mantle trimming and per-chunk cleanup at all; false means they keep everything resident |
|
||||
| `performance.noiseCacheSize` | `1024` | Noise sample cache capacity per engine. Starting a pregen raises it to at least 4096 for the rest of the process |
|
||||
| `performance.resourceLoaderCacheSize` | `1024` | Parsed pack resource entries held before eviction |
|
||||
| `performance.objectLoaderCacheSize` | `4096` | `.iob`, matter, and image loader entries held before eviction |
|
||||
| `performance.engineSVC.useVirtualThreads` | `true` | Maintenance workers run on virtual threads; `false` uses platform threads |
|
||||
| `performance.engineSVC.forceMulticoreWrite` | `false` | Always unload mantle plates on the parallel path instead of only under heap pressure |
|
||||
| `performance.engineSVC.priority` | `5` (`Thread.NORM_PRIORITY`) | Priority of maintenance platform threads, clamped to the legal Java range. Ignored entirely when virtual threads are on |
|
||||
| `performance.engineSVC.parallelism` | `-1` | Size of the engine maintenance worker pool. A positive value is capped at `2 × CPU`; zero or negative means `ceil(sqrt(CPU))` |
|
||||
|
||||
`engineSVC` sizes the maintenance service — mantle trimming, plate unloading, periodic saves — not chunk generation. Raising `parallelism` will not generate chunks faster; it makes mantle housekeeping finish sooner and take more CPU while it does. Generation parallelism is derived separately (see below).
|
||||
|
||||
## Reference: `pregen` section
|
||||
|
||||
| Key | Default | What it does |
|
||||
|-----|---------|--------------|
|
||||
| `pregen.runtimeSchedulerMode` | `AUTO` | Which scheduler the Bukkit pregen driver uses: `AUTO`, `PAPER_LIKE`, `FOLIA`. A Folia runtime always resolves to Folia |
|
||||
| `pregen.paperLikeBackendMode` | `AUTO` | How Paper-like pregen acquires chunks: `AUTO`, `TICKET`, `SERVICE`. `AUTO` resolves to `TICKET` |
|
||||
| `pregen.chunkLoadTimeoutSeconds` | `15` | How long a pregen worker waits for a chunk before warning. Clamped 5–120; modded pregen raises anything below 120 to 120 |
|
||||
| `pregen.timeoutWarnIntervalMs` | `500` | Minimum gap between repeated timeout warnings. Minimum 250 |
|
||||
| `pregen.saveIntervalMs` | `30000` | Gap between pregen progress flushes. Clamped 5000–900000 |
|
||||
| `pregen.maxResidentTectonicPlates` | `96` | Ceiling on resident mantle plates before the height and heap budgets narrow it further. Never drops below 16 |
|
||||
| `pregen.mantleBackpressureWaitMs` | `25` | Pause between retries when the plate budget is full. Clamped 5–1000 |
|
||||
| `pregen.mantleBackpressureTimeoutMs` | `60000` | How long a chunk waits on backpressure before failing. Clamped 5000–600000 |
|
||||
| `pregen.moddedPregenInFlight` | `0` | Concurrent pregen chunks on mod loaders. `0` resolves to `clamp(16, cpu*2, 48)`; positive values cap at 512; the result is floored at 8 |
|
||||
|
||||
Related: `world.globalPregenCache` (default `false`) — see `03 - Configuration.md` and `07 - Pregeneration.md`.
|
||||
|
||||
## Reference: derived concurrency
|
||||
|
||||
The `concurrency` section in `settings.json` has no writable keys. The values are computed from CPU count at runtime and cannot be overridden from the file:
|
||||
|
||||
- Generation burst pool: `max(2, availableProcessors)`
|
||||
- IO burst pool: `max(2, availableProcessors / 2)`
|
||||
- Bukkit pregen in-flight cap: worker threads × 8, clamped 16–128 on Paper-like servers and 64–192 on Folia, then lowered adaptively under mantle backpressure down to `max(4, min(16, cap / 4))`
|
||||
|
||||
If you need less generation concurrency, use `serial=true` (Bukkit) or `sync` (modded) rather than looking for a knob that does not exist.
|
||||
|
||||
## 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.
|
||||
What actually uses vector kernels today is narrow: an array rounding path in the chunked double data cache, and array operations in mantle carving. The 2D fractal noise vector kernels (`VectorNoiseKernels2D`) exist and are correct, but nothing in the production worldgen path calls `SimdSupport.noiseKernels2D()` yet. Treat noise SIMD as unfinished and do not size hardware around it.
|
||||
|
||||
Runtime selection (`SimdSupport`) today:
|
||||
Selection happens once, at class initialization:
|
||||
|
||||
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`.
|
||||
1. `performance.simdKernels` false → scalar kernels.
|
||||
2. Otherwise, if the `jdk.incubator.vector` module is present and the vector kernel class loads → vector kernels.
|
||||
3. Otherwise → scalar kernels, with a startup log line telling you to add the flag.
|
||||
|
||||
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.
|
||||
The 2D noise kernels add one more gate: they are only selected when the preferred `double` and `long` vector species have matching lane counts and at least 4 double lanes. Apple Silicon NEON, at 2 lanes, does not qualify.
|
||||
|
||||
JVM flag (required for vector API incubator):
|
||||
The JVM flag is required for any vector path:
|
||||
|
||||
```
|
||||
--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.
|
||||
The Iris Gradle build passes it for core compilation and tests and for every `probe` task, and `tools/simd-bench/` passes it in its own scripts. Nothing adds it to a production server's start script — a server operator must add it there.
|
||||
|
||||
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.
|
||||
To A/B on a real server: set `performance.simdKernels` false, restart, measure pregen chunks/second, set it true, restart, measure again, and confirm GoldenHash is unchanged (`32 - Determinism & Goldenhash.md`). `tools/simd-bench/` (`./run.sh` or `run.bat`) measures kernels in isolation and deliberately ignores the profitability gate, so its speedups do not predict end-to-end pregen gains.
|
||||
|
||||
## 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 (5k–10k chunks, JProfiler) are tracked in `87 - Maintainer - Release Readiness.md`.
|
||||
Record for every experiment: pack identity, seed, radius, serial/sync flags, JVM version and flags, heap size, CPU, the `performance` and `pregen` excerpts you changed, chunks/second, duration, failed chunks, peak heap, and the GoldenHash combined value. Reject any optimization that changes the hash unless the behavior change was intended and is documented. Release-scale baselines (5k–10k chunks with JProfiler) are tracked in `87 - Maintainer - Release Readiness.md`.
|
||||
|
||||
Never tune by editing pack content. Pack edits change terrain, which changes the hash, which means you are no longer comparing the same thing.
|
||||
|
||||
## 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 |
|
||||
| Generation probe | `./gradlew :probe:genProbe -PprobePack=…` | Headless engine generation; a correctness signal, not a throughput benchmark |
|
||||
| Classload probe | `./gradlew :probe:run` | Core-purity gate; fails if `org.bukkit` leaks into engine classes |
|
||||
| SIMD microbench | `tools/simd-bench/run.sh` | Kernel-only scalar versus vector timing |
|
||||
|
||||
Smoke procedures that combine pregen and GoldenHash: `31 - Operator Runbooks & Smoke Tests.md`.
|
||||
Runbooks that combine pregen and GoldenHash: `31 - Operator Runbooks.md`.
|
||||
|
||||
Reference in New Issue
Block a user