Custom Profiles

A custom profile is a YAML file that describes a run of your own. A benchmark profile lists which tests to run, in which order and with which parameters. A stress limit profile chooses which workloads to push towards the breaking point, and how.

Profiles live in plugins/VoxelBench/custom_benchmarks/ and run with /bench custom run <name>.

This page is the full reference. Benchmarks gives a short overview. Custom profiles are not the same thing as benchmark-mode: custom in config.yml, which changes what /bench start runs (see Benchmarks).

Quick Start

/bench custom list
/bench custom info standard
/bench custom run standard
  1. /bench custom list shows the profiles that loaded, tagged [STD] (benchmark) or [STRESS] (stress limit).
  2. /bench custom info standard shows a profile's details and its steps.
  3. /bench custom run standard runs it. A player must type it in-game, and the server must be linked to a voxelbench.com account, even if the profile sends nothing.
  4. To write your own, copy standard.yml to my-profile.yml in the same folder and edit the copy: give it its own name:, then change the steps. Run /bench custom reload, then /bench custom run my-profile. No restart is needed.

Bundled Profiles

VoxelBench ships nine profiles:

FileKindContent
example.ymlbenchmarkQuick smoke test in 7 steps: disk, memory, network, chunkLoading, hopper, singleCoreMax, multiCore
standard.ymlbenchmarkThe 16 tests of /bench start with the same loads, as a starting point to copy. Its runs are not scored and are not comparable with /bench start
showcase.ymlbenchmarkAll 26 built-in tests, each parameter written out with a comment. It loads like any profile, but it is a reference to copy blocks from, not a benchmark to run
free-host.ymlbenchmarkThe 16 standard tests with much lighter loads, for free hosting tiers. It reminds you that many free hosts forbid benchmarks in their terms
low-memory.ymlbenchmarkThe 16 standard tests scaled down for servers with a 2-4 GB heap
hopper-heavy.ymlbenchmarkThe hopper test alone, at 100 lines per zone: ten times the standard load. Heavy: run it on a test server or off-peak
stress-redstone.ymlstresslimitRedstone only, from 400 up to a soft ceiling of 8,000, with a fine ramp and a precise search at the breaking point
stress-monster.ymlstresslimitSix stress types (every type except villagers) with their nominal bounds, an aggressive ramp and 15 minutes per type
stress-freehost.ymlstresslimitMobs, redstone and hoppers with low soft ceilings (4,000, 2,000 and 2,000), stricter break thresholds (TPS 19, MSPT 45 ms), 8-second tiers and 2 zones

Updates and Deleted Profiles

  • VoxelBench copies each bundled profile once: all of them when it creates the folder, and a profile added by a later version (such as hopper-heavy.yml) at the first start after the update.
  • The folder's .bundled-profiles file lists the profiles already copied, so a bundled profile you delete stays deleted. To get one back, remove its name from .bundled-profiles and run /bench custom reload.
  • Each time the folder is read (at startup and on reload), a copy that is exactly an earlier version of a bundled profile, line endings aside, is replaced by the current version, and the console says so. A copy you changed, even by one character, is never touched. To keep your changes and still receive updates of the original, work on a copy under another name.

Profile Files

  • A profile is one .yml or .yaml file in plugins/VoxelBench/custom_benchmarks/.
  • Its name is the file name without the extension, in lower case: My-Profile.yml is run with /bench custom run my-profile. Two files whose names only differ by case or extension share the same name, and only one of them is loaded.
  • One folder holds both kinds. The top-level kind: key chooses: benchmark (the default) or stresslimit. Without kind:, a file that has a stress: section and no tests: section is a stress limit profile; any other file is a benchmark profile. Any kind: value other than stresslimit (in any case) is read as benchmark.

Common Keys

These keys are optional and work the same way in both kinds:

KeyDefaultMeaning
kindSee abovebenchmark or stresslimit
nameThe file nameDisplay name, shown by list and info and on voxelbench.com
descriptionEmptyShown under the profile in list, and by info
authorEmptyShown by info and on voxelbench.com
version1A whole number, your own revision number
tagsNoneA list of words, for example [redstone, farms]
submitfalsetrue sends the report to voxelbench.com at the end of the run (see On voxelbench.com)

Other top-level keys are ignored.

Benchmark Profiles

kind: benchmark                # optional: benchmark is the default
name: "Redstone and Chunks"
description: "Redstone clocks, then chunk generation"
author: "YourName"
version: 1
tags: [redstone, chunks]
submit: false                  # true = send the report to voxelbench.com

options:
  auto-temp-world: true        # false = run in the main world (see Test World)

tests:
  - id: disk
    params:
      threads: 4
      fileSizeMb: 1024
  - id: redstone
    params:
      pistonCount: 3200
      durationSeconds: 60
  - id: chunkLoading
    params:
      chunksToLoad: 1000
      dispersedZones: 8        # more than 1 = the run's 8 zones
  - id: worldSave              # no parameters
  - id: singleCoreBenchmark
    params:
      durationSeconds: 30

options: has a single setting, auto-temp-world (see Test World). Other keys under options: do nothing, but they count in the profile hash.

Steps

  • tests: is the list of steps. Each entry is a map with an id: and, optionally, params:.
  • The steps run in the order written, one after the other. The bundled profiles put hardware tests first, gameplay tests next and CPU tests last, which gives the most reliable numbers.
  • id: is a test ID: one of the 26 built-in tests, or a test added by an extension. Case, - and _ do not matter: chunkLoading, chunk-loading and CHUNK_LOADING are the same test. /bench test list lists every test the server knows, extension tests included.
  • The short names that /bench test also accepts (lighting, collision, tileentity, villager) are not test IDs: in a profile, write lightingUpdate, entityCollision, tickingTileEntity and villagerTrading.

Parameters

In a profile, every parameter is written by name under params:, unlike /bench test, where many tests take plain values in a fixed order (see Commands). A parameter you leave out takes its default value. A number outside the allowed range is brought back to the nearest bound when the run starts, with a warning.

TestParameters: default (allowed range)
diskthreads 4 (1–32), queueDepth 8 (1–64), fileSizeMb 512 (16–8192), randomOps 200 (10–5000), passes 3 (1–20)
networkNone
memorytableSize 512 (16–4096), iterations 75000 (1000–1000000), passes 3 (1–20)
multiCorethreads 100 (1–1000), iterations 100000 (1000–10000000), kernel int
singleCoreBenchmarkdurationSeconds 10 (3–60), kernel int
singleCoreMaxoperations 50000000 (1000000–1000000000), passes 3 (1–20), kernel int
chunkLoadingchunksToLoad 200 (10–5000)*, dispersedZones 1 (1–10)
mobSpawnmobCount 300 (10–5000)*, dispersedZones 1 (1–10)
hopperparallelLines 50 (1–500), dispersedZones 1 (1–10)
explosiontntCount 100 (1–1000)*, dispersedZones 1 (1–10)
lightingUpdatetotalUpdates 400 (10–10000), dispersedZones 1 (1–10)
worldSaveNone
redstonepistonCount 1000 (10–5000), durationSeconds 15 (5–120)
blockPhysicsfallingBlockCount 12000 (100–50000), spawnFrequencyTicks 1 (1–20)
chunkTickingchunks 200 (10–1000), tickSpeed 3 (1–256), durationSeconds 60 (10–300)
entityCollisionitems 500 (10–5000), entities 200 (10–2000), durationSeconds 30 (10–120)
tickingTileEntitytotal 5000 (100–20000), type ALL, dispersedZones 1 (1–10), durationSeconds 15 (5–120)
mobAIvillagerCount 100 (50–3000), houseCount 20 (5–500), hostileCount 200 (10–2000), phase1Seconds 10 (5–60), phase2Seconds 15 (5–120)
mobPathfindingtotalMobs 200 (10–2000), durationSeconds 30 (10–120)
villagerTradingvillagers 100 (20–2000), houses 20 (5–200), durationSeconds 60 (30–300), zombies 50 (0–500)
boneMealGrowthsaplingCount 200 (10–2000), cropCount 500 (10–5000), durationSeconds 20 (5–120)
liquidPhysicswaterSourceCount 100 (1–1000), lavaSourceCount 50 (0–1000), durationSeconds 20 (5–120)
combatSimulationzombieCount 60 (0–500), skeletonCount 80 (0–500), pillagerCount 40 (0–500), durationSeconds 30 (10–120)
projectileStormprojectilesPerWave 100 (10–2000), durationSeconds 20 (5–120)
entityCrammingtotalEntities 500 (10–5000), durationSeconds 20 (5–120)
playerWorldLoadplayerCount 10 (1–100)

* Also capped by config.yml, without a warning: see Settings Read from config.yml.

  • kernel (CPU tests) is int, float, memory or branch. Any other value runs int, without a warning.
  • type (tickingTileEntity) is FURNACE, HOPPER, SPAWNER or ALL. Any other value runs ALL, with a warning.
  • Write numbers as plain integers (50000000). A value that is not a number gives the default, with a warning.
  • For chunkLoading, mobSpawn, hopper and explosion, dispersedZones only chooses between one zone and eight. Every profile run lays out 8 test zones far apart, as /bench start does. Such a test given dispersedZones: 1, or no dispersedZones at all, runs in a single zone; given any value from 2 to 10, it runs in the run's 8 zones. Write 8 to make that explicit. lightingUpdate and tickingTileEntity use the number of zones you write.
  • chunkLoading stays under a memory ceiling: 30 % of the maximum heap, counted at about 50 KB per chunk, shared by the zones the test actually walks (the run's 8 zones for any dispersedZones above 1, a single zone otherwise), and never cut below 200 chunks per zone. With 8 zones, each zone gets at most 0.75 chunk per MB of maximum heap: chunksToLoad: 1000 is cut on a heap under about 1,330 MB. Before VoxelBench 2.0.3, the ceiling was divided by the dispersedZones value written in the profile instead, so a profile with dispersedZones: 2 could load up to four times the ceiling on a small heap: its earlier runs are not comparable with new ones there.
  • Tests added by extensions declare their own parameters: see the extension's documentation.

The bundled showcase.yml repeats every parameter with a short comment, and standard.yml holds the values of /bench start.

Test World

A benchmark profile picks its world like this:

  1. A world pinned with /bench world set is always used, if it is loaded. If it is not loaded, you get a warning and the next rules apply.
  2. Otherwise, when options.auto-temp-world is true (its default is the value of benchmark.auto-temp-world in config.yml), the run creates a temporary flat world, voxelbench_temp_<timestamp>, and deletes it at the end. This only happens when benchmark.auto-temp-world is also true in config.yml: a profile can turn the temporary world off, not on. If the world cannot be created, the run falls back to the main world and the console says so.
  3. Otherwise the tests run in the server's main world (the first world it loads): they build and clear their test zones there.

playerWorldLoad only runs in the pinned world or in a voxelbench_* world. Anywhere else, the step is skipped with the reason benchmark_world_required and the rest of the run goes on. See also Configuration and Benchmarks.

Settings Read from config.yml

A profile does not hold every setting of its tests. A few are read from the server's config.yml at each run (see Configuration):

  • benchmark-tests.chunk-loading.limits, benchmark-tests.mob-spawn.limits and benchmark-tests.explosion.limits cap chunksToLoad, mobCount and tntCount, without a warning. With the shipped config.yml, that means at most 5,000 chunks, 1,000 mobs and 200 TNT, even if the table above allows more.
  • benchmark-tests.chunk-loading.adaptive (chunk generation throttle), benchmark-tests.hopper.hopper-chain-length, benchmark-tests.mob-spawn.duration-seconds and benchmark-tests.explosion.raise-host-tnt-quota also apply.
  • benchmark-tests.hopper.limits does not apply: it only bounds the /bench test hopper command. A profile runs the lines it asks for, up to 500 per zone.

Two servers running the same profile therefore run the same load only if these settings match.

Stress Limit Profiles

A stress limit profile runs the stress limit mode with your own recipe: which stress types to push, from which load, up to which ceiling, and when a tier counts as broken.

kind: stresslimit
name: "Farms Check"
description: "Hoppers and mobs, stricter break threshold"
author: "YourName"
version: 1
tags: [farms]
submit: false

stress:
  types:
    - id: hoppers
      base: 100          # starting load
      max: 4000          # soft ceiling
    - id: mobs           # no base or max: the type's nominal bounds
  thresholds:
    tpsBreak: 19.0
    msptBreak: 50.0
  timing:
    palierDurationSec: 12
  zones: 2

stress.types is the only required key. Each entry is a map with an id:, one of mobs, chunks, hoppers, tnt, redstone, entities or villagers (see Stress Types). The types run in the order written; a type listed twice runs once.

  • base is the starting load and max the soft ceiling. Leave one out and it keeps the type's nominal value. A base above max is lowered to max.
  • The soft ceiling still grows while the server has headroom, up to the type's hard cap of five times its nominal ceiling. A max above that cap is lowered to it: no profile can raise it.

Every other key is optional and keeps the value used by /bench stresslimit:

KeyDefaultEffect
stress.thresholds.tpsBreak18.0A tier breaks when the average TPS falls below this value
stress.thresholds.msptBreak55.0A tier breaks when the average MSPT rises above this value, in milliseconds
stress.ramp.stepMin1.15Load multiplier near the breaking point
stress.ramp.stepMax2.5Load multiplier when the server is idle
stress.ramp.curve2.0Above 1, big steps last longer while the server has headroom
stress.ramp.plateauBoosttrueAfter three nearly idle tiers in a row, each further nearly idle tier multiplies the step by 1.5, cumulatively; false turns this off. Counted in the profile hash and recorded in its report. Before VoxelBench 2.0.3 the setting had no effect
stress.refine.targetPrecision250The search at the breaking point stops when the gap is this many units
stress.refine.maxIterations8Maximum attempts of that search
stress.timing.palierDurationSec10Seconds per tier
stress.timing.maxDurationMinutes10Time limit per type
stress.timing.earlySkiptrueLets a tier end early when the server is clearly comfortable
stress.zones4Number of test zones

The thresholds decide the result; the ramp, the search and the timing decide how fast and how precisely it is found. VoxelBench does not check the range of these numbers, so keep them close to the bundled profiles' values. How the tiers, the ramp and the search work is explained in Stress Limit.

A stress limit profile has no options:. It uses the pinned world, otherwise a temporary world when benchmark.auto-temp-world is true, otherwise the main world, like /bench stresslimit. It starts from the warm-start memory and updates it, as a full stress limit run does. Unlike /bench stresslimit, it starts without asking for confirmation.

Commands

CommandDescription
/bench custom list (alias ls)List the profiles that loaded: kind, name, display name, number of steps or types, and whether it submits
/bench custom info <name> (alias show)Show a profile: display name, description, author, version, tags, submit, the first 12 characters of its hash, then its steps and parameters as written, or its stress types and break thresholds
/bench custom run <name> [force] [warmup] [<runs>]Run a profile
/bench custom reloadRead the folder again: new, changed and deleted files, and the bundled profiles (see above). Prints how many profiles loaded

Every /bench custom command needs voxelbench.custom or voxelbench.start (operators by default); reload also needs voxelbench.reload, and running a stress limit profile also needs voxelbench.stresslimit, like /bench stresslimit. /bench reload does not re-read the profiles. Tab completion offers the loaded profile names after info, and after run only the profiles you can run: stress limit profiles only with voxelbench.stresslimit. The list of available profiles printed after an unknown name follows the same rule; list and info show every profile.

/bench custom run:

  • must be typed by a player in-game, not from the console;
  • needs a linked server, even when the profile has submit: false;
  • is refused while another test or run is in progress, and /bench stop stops it;
  • shares the cooldown of /bench start (see Configuration). force ignores the cooldown if you have voxelbench.start.force;
  • starts at once, without confirmation;
  • runs a single iteration: warmup and a run count are accepted, but a message tells you they are not applied to profiles.

A run is saved in your local reports (/bench reports), under Standard Benchmark for a benchmark profile and under Stress Limit for a stress limit profile, unless config.yml turns that report type off. See Reports.

See also Commands.

From the GUI

/bench gui, then the Tests tab, then Custom Profiles, lists the loaded profiles you can run: a nether star marks a benchmark profile, TNT a stress limit profile (shown only with voxelbench.stresslimit). The screen needs voxelbench.custom or voxelbench.start. Clicking a profile runs /bench custom run <name>, with the same checks. The refresh button runs /bench custom reload, which needs voxelbench.reload.

Validation

When Loading

VoxelBench reads the folder at startup, on /bench custom reload and with the GUI's refresh button. A file with a problem is left out of /bench custom list, and the console says why:

ProblemResult
The file is not valid YAMLThe server logs the parsing error, and the profile is skipped
A benchmark profile has no tests: list, or an empty oneSkipped
A step has no id:The whole profile is rejected
A test ID is unknownThe whole profile is rejected; the message lists the valid IDs
A stress limit profile has no stress.types listSkipped
A stress type has no id:, or an unknown oneThe whole profile is rejected; the message lists the valid types

Parameters are not checked at this point: /bench custom info shows them exactly as written.

Tests from extensions are only known once their plugin has started, which is after VoxelBench. A profile that uses one is therefore rejected at server startup: run /bench custom reload once the server is up.

A benchmark profile whose hopper step asks for more lines than benchmark-tests.hopper.limits.max also gets a console warning at load (the bundled hopper-heavy.yml excepted): before VoxelBench 2.0.0 such profiles silently ran at most that many lines (10 by default), so their earlier results are not comparable with new ones.

When a Run Starts

All steps are prepared before the first test starts:

  • A number outside its allowed range is brought back within it, with a warning in the chat and the console, for example mobCount=9000 clamped to 5000 (allowed range: 10..5000). The run goes on.
  • A value that is not a number is replaced by the default, with a warning.
  • A parameter name the test does not know is ignored, without a warning: the test runs with its default value.
  • If a test is no longer available (an extension was removed after the profile loaded), the run does not start, and a message names the test.

Sharing a Profile

A profile is a single, self-contained file. To share one, send the file: the other operator drops it into plugins/VoxelBench/custom_benchmarks/ and runs /bench custom reload. On their server, the file name becomes the profile name.

Profile Hash

When VoxelBench loads a profile, it computes a SHA-256 hash of its recipe. /bench custom info shows its first 12 characters, and a report sent to voxelbench.com carries the whole hash, so two runs with the same hash ran the same recipe.

For a benchmark profile, the hash covers:

  • the kind, name, description, author, version, tags and submit;
  • every key under options:;
  • the steps, in order: each test, with its parameters exactly as written.

It does not depend on the file name (except when there is no name:, since the file name is then the display name), comments, blank lines, the order of the keys under params: or options:, the order of the tags, or the way the test ID is spelled (chunk-loading and chunkLoading give the same hash). A parameter left out and the same parameter written with its default value give two different hashes, as do an out-of-range value and the bound it is brought back to.

For a stress limit profile, the hash covers the same metadata, the stress types in order with their bounds, and every stress setting as it applies to the run: a setting left out and the same setting written with its default value give the same hash.

Any other change, even to the description or to submit, makes a different hash, and therefore a different profile on voxelbench.com. The hash describes the recipe, not the server: results still depend on the machine and on the config.yml settings listed in Settings Read from config.yml.

On voxelbench.com

With submit: false, the default, nothing is sent: the run only stays in your local reports, where a benchmark profile's report is marked as not submitted.

With submit: true, a benchmark profile sends its report when the run ends, and the chat prints the link. On voxelbench.com, the report:

  • belongs to the account your server is linked to, and is private at first. You can make it unlisted (anyone with the link can see it) or private again, but never public;
  • gets no VoxelScore and no rank, and appears on no leaderboard;
  • shows a Custom Profile card: display name, author, version, description, tags, the first 12 characters of the hash, and how many runs sent to voxelbench.com used the same hash.

The card leaves out a field that is too long: file name over 64 characters, display name or author over 255, description over 2,000, a tag over 32. Only the first 20 tags are kept. The run itself is still stored. For the reports of tests added by extensions, see Extending VoxelBench.

A stress limit profile with submit: true also sends its report when the run ends. voxelbench.com treats it like a benchmark profile's report: it belongs to the linked account, is private at first and can be made unlisted, never public, and it gets no score and no rank and appears on no leaderboard, since its break thresholds and ramp are the profile author's.

If your voxelbench.com plan includes auto-bench, an auto-bench job can run a benchmark profile of your server by its name. The name must then use only letters, digits, - and _, up to 64 characters. Such a run is always sent, whatever submit says. Stress limit profiles cannot be run this way.

Common Pitfalls

  • A misspelled parameter name is ignored silently: the test runs with its default value. Compare the names printed by /bench custom info with the parameter table.
  • A step written - disk instead of - id: disk is ignored. Each step must be a map with an id:.
  • kind: stress-limit is not kind: stresslimit: any other value makes a benchmark profile, which is then skipped because it has no tests:.
  • A profile that uses an extension test disappears after a restart: run /bench custom reload once the server is up.
  • dispersedZones: 2 or 4 does not mean 2 or 4 zones for chunkLoading, mobSpawn, hopper and explosion: any value above 1 means the run's 8 zones.
  • options.auto-temp-world: true cannot create a temporary world when benchmark.auto-temp-world is false in config.yml: the run uses the main world.
  • mobCount and tntCount stop at the config.yml limits (1,000 mobs and 200 TNT by default), without a warning.
  • A new profile file does nothing until you run /bench custom reload or restart the server.
  • A stress limit profile missing from the tab completion or the GUI is not broken: running one needs voxelbench.stresslimit, and it is only offered to players who have it. /bench custom list shows it.