Skip to content

perf: inline small values, query results as plain lists - #652

Open
makspll wants to merge 6 commits into
agent/script-systems-queriesfrom
agent/script-systems-perf
Open

makspll wants to merge 6 commits into
agent/script-systems-queriesfrom
agent/script-systems-perf

Conversation

@makspll

@makspll makspll commented Oct 22, 2026 •

Copy link
Copy Markdown
Owner

Stacked on #651. Last of the script system PRs.

Inline values (generic, second commit)

  • ScriptValue::Value(InlineValue): V<T> returns any T of at most 16 bytes with no drop glue (e.g. Entity, Vec3, Quat) stored inline instead of in an allocated reference.
    • Decided at compile time (const fn is_inline::<T>() inside the monomorphized IntoScript for V<T>), so no registry lookups.
    • The value carries a per-type static vtable: type id, as_reflect, into_reference.
  • Scripts can't tell values from references. One generic wrapper per language (LuaValue / RuneValue) holds the inline value:
    • Non-mutating methods (dispatch::index_value), operators and printing use it directly.
    • Anything that needs a place (fields, assignments, M<T> methods, iteration) first promotes it in place to an allocated reference, i.e. what every V<T> result was before. Variables sharing the value see the change.
    • Field and element reads always return references into the place.
  • Conversions: R<T> holds a copy of an inline value, exact type only like for references (a structural from_reflect made a union pick Vec3 for a Quat). Its field is private now. V<T> converts like from a reference. M<T> only accepts values as a promoted receiver.

Plain list query results (first commit)

  • Query results are lists {entity, a, b} (result[1], result[2] in Lua, result[0], result[1] in Rune), from script systems and from world.query():build().
  • The entity() / components() script functions are removed.

Numbers (medians of 3 interleaved rounds against #651, ns)

bench before after
operator 846 632 (−25%)
method call returning value 995 841 (−15%)
method call 644 576 (−11%)
vec mat ops 2992 2664 (−11%)
field set 462 424 (−8%)
field get 414 450 (+9%)
nested method call 768 800 (+4%)
component / resource access 2452 / 2156 2559 / 2254 (+4%)
script system, query + iterate 1000 1.99 ms 1.29 ms (−35%)
script system, empty callback 1000 0.37 ms 1.23 ms (eager lists)

Things that behave differently

  • Rust code inspecting returned ScriptValues gets Value for small types (one test updated).
  • Functions taking a raw ScriptValue and matching only Reference silently miss values. Three are fixed: eq, EnumVariantRef::eq, trigger_event. Third-party code needs the same treatment (migration notes).
  • A function that mutates a value passed as an argument, not as the receiver, via M<T> errors, or with a raw ReflectReference mutates a copy. No bindings in the repo do this.

Tests

  • Lua + Rune with every binding feature: 325/325, including the new value_semantics tests (aliasing, copy on assignment, fields are places, equality between field references and values).
  • Unit tests, LAD / docgen tests, clippy (workspace, lua54 + rune), and builds for luajit / luau / lua51 all pass.

🤖 Generated with Claude Code

@semanticdiff-com

semanticdiff-com Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Review changes with  SemanticDiff

Changed Files
File Status
  crates/bindings/bevy_mod_scripting_bevy_ecs_bindings/src/lib.rs  74% smaller
  crates/bevy_mod_scripting_bindings/src/function/into.rs  60% smaller
  assets/tests/add_system/system_with_parameters.lua  55% smaller
  assets/tests/add_system/optional_components_and_disjoint_queries.lua  48% smaller
  crates/bevy_mod_scripting_core/src/derive_tests/mod.rs  43% smaller
  crates/bevy_mod_scripting_bindings/src/dispatch.rs  42% smaller
  assets/tests/query/query_can_access_components.lua  41% smaller
  crates/languages/bevy_mod_scripting_rune/src/value.rs  34% smaller
  benches/benchmarks.rs  29% smaller
  crates/languages/bevy_mod_scripting_lua/src/bindings/reference.rs  19% smaller
  assets/scripts/game_of_life.lua  16% smaller
  assets/tests/query/dynamic_component_query.lua  15% smaller
  assets/tests/query/query_returns_all_entities_matching.lua  15% smaller
  crates/bevy_mod_scripting_core/src/script_system.rs  15% smaller
  assets/tests/add_system/read_only_parameters_cannot_be_written.lua  14% smaller
  crates/bevy_mod_scripting_functions/src/core.rs  13% smaller
  crates/languages/bevy_mod_scripting_rune/src/types.rs  9% smaller
  crates/bevy_mod_scripting_bindings/src/query.rs  8% smaller
  crates/bevy_mod_scripting_bindings/src/function/from.rs  6% smaller
  assets/tests/add_system/system_cannot_access_component_outside_query_archetypes.lua  5% smaller
  crates/bevy_mod_scripting_bindings/src/script_value.rs  1% smaller
  assets/macro_benchmarks/script_systems/query_system.lua  0% smaller
  assets/macro_benchmarks/script_systems/query_system_iterate.lua  0% smaller
  assets/scripts/game_of_life.rn Unsupported file format
  assets/tests/add_system/system_with_parameters.rn Unsupported file format
  assets/tests/bindings/transform/value_semantics.lua  0% smaller
  assets/tests/bindings/transform/value_semantics.rn Unsupported file format
  assets/tests/query/dynamic_component_query.rn Unsupported file format
  assets/tests/query/query_can_access_components.rn Unsupported file format
  assets/tests/query/query_returns_all_entities_matching.rn Unsupported file format
  assets/tests/script_callbacks/lua/script_callbacks.lua  0% smaller
  assets/tests/script_callbacks/rune/script_callbacks.rn Unsupported file format
  crates/bevy_mod_scripting_bindings/src/function/from_ref.rs  0% smaller
  crates/bevy_mod_scripting_bindings/src/inline_value.rs  0% smaller
  crates/bevy_mod_scripting_bindings/src/lib.rs  0% smaller
  crates/bindings/bevy_mod_scripting_bevy_input_bindings/src/focus.rs  0% smaller
  crates/languages/bevy_mod_scripting_lua/src/bindings/script_value.rs  0% smaller
  crates/testing_crates/script_integration_test_harness/src/lib.rs  0% smaller
  docs/src/Development/AddingLanguages/necessary-features.md Unsupported file format
  docs/src/ReleaseNotes/0.23-to-0.24.md Unsupported file format
  docs/src/ScriptSystems/introduction.md Unsupported file format

@makspll
makspll added this pull request to stack #649 October 22, 2026 17:45
@github-actions

github-actions Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

🐰 Bencher Report

ProjectBevy Scripting
Branchagent/script-systems-perf
Testbedlinux-gha

⚠️ WARNING: Truncated view!

The full continuous benchmarking report exceeds the maximum length allowed on this platform.

🚨 3 Alerts

🐰 View full continuous benchmarking report in Bencher

@makspll makspll changed the title perf: entities as plain values, query results as plain lists perf: inline small values, query results as plain lists Oct 22, 2026
@makspll
makspll force-pushed the agent/script-systems-perf branch from 7ba5a13 to d0c0e88 Compare October 23, 2026 12:07
- `ScriptValue::Entity(Entity)`: entities never allocate, `V<Entity>`
  converts to and from it, entity fields read as values. Lua (`LuaEntity`)
  and Rune (`RuneEntity`) resolve its methods through `static_index(Entity)`
- query results convert to lists of the entity followed by the components
  (`result[1]`, `result[2]` in Lua) instead of allocated references, the
  `entity()` / `components()` script functions are removed
- `script_systems` macro benchmarks: iterating 1000 query results goes from
  2.08ms to 1.27ms
- `ScriptValue::Value(InlineValue)` replaces `ScriptValue::Entity`: `V<T>`
  stores any `T` of at most 16 bytes without drop glue inline, decided at
  compile time, with a per-type vtable (type id, reflect, promotion)
- languages hold it in one generic wrapper (`LuaValue` / `RuneValue`) and
  promote it in place to an allocated reference before anything which needs a
  place (fields, assignments, mutating methods, iteration), so scripts can't
  tell values from references; field reads stay references
- `R<T>` holds a copy of an inline value (exact type only, like references),
  `dispatch::binary`/`unary` take inline operands, `dispatch::index_value`
  finds non-mutating methods
- functions matching `ScriptValue::Reference` on a `ScriptValue` argument
  accept values (`eq`, `EnumVariantRef::eq`, `trigger_event`)
@makspll
makspll force-pushed the agent/script-systems-perf branch from d0c0e88 to 3bf0796 Compare October 23, 2026 12:10
…-perf

# Conflicts:
#	crates/bevy_mod_scripting_core/src/script_system.rs
…-perf

# Conflicts:
#	docs/src/ReleaseNotes/0.23-to-0.24.md

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant