-
-
Notifications
You must be signed in to change notification settings - Fork 49
feat: Better HashMaps - hashmap iteration and read-access in map_get() #487
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Changed Files
|
Added old map_get back as map_get_clone() - some times we still need to get fully Reflected values (for example can't constuct or dowcast asset handle from PartialReflect) |
Adding map_get_clone() back pushed me to idea, that iters may also need fully reflected values, added this also. |
|
Branch | hashmap_iter |
Testbed | linux-gha |
Click to view all benchmark results
Benchmark | Latency | Benchmark Result nanoseconds (ns) (Result Δ%) | Upper Boundary nanoseconds (ns) (Limit %) |
---|---|---|---|
component/access Lua | 📈 view plot 🚷 view threshold | 3,801.60 ns(-6.18%)Baseline: 4,051.83 ns | 4,399.80 ns (86.40%) |
component/access Rhai | 📈 view plot 🚷 view threshold | 5,911.00 ns(-1.15%)Baseline: 5,979.86 ns | 6,149.65 ns (96.12%) |
component/get Lua | 📈 view plot 🚷 view threshold | 2,200.10 ns(-9.27%)Baseline: 2,424.80 ns | 2,708.53 ns (81.23%) |
component/get Rhai | 📈 view plot 🚷 view threshold | 4,296.50 ns(-4.01%)Baseline: 4,475.80 ns | 4,818.35 ns (89.17%) |
conversions/Mut::from | 📈 view plot 🚷 view threshold | 80.90 ns(-7.38%)Baseline: 87.34 ns | 95.15 ns (85.03%) |
conversions/Ref::from | 📈 view plot 🚷 view threshold | 78.74 ns(-8.20%)Baseline: 85.78 ns | 96.83 ns (81.32%) |
conversions/ScriptValue::List | 📈 view plot 🚷 view threshold | 434.58 ns(+26.91%)Baseline: 342.42 ns | 488.55 ns (88.95%) |
conversions/ScriptValue::Map | 📈 view plot 🚷 view threshold | 973.49 ns(-9.29%)Baseline: 1,073.15 ns | 1,219.42 ns (79.83%) |
conversions/ScriptValue::Reference::from_into | 📈 view plot 🚷 view threshold | 23.85 ns(-9.24%)Baseline: 26.27 ns | 30.78 ns (77.47%) |
conversions/Val::from_into | 📈 view plot 🚷 view threshold | 292.49 ns(-2.50%)Baseline: 299.98 ns | 338.17 ns (86.49%) |
function/call 4 args Lua | 📈 view plot 🚷 view threshold | 1,616.40 ns(-13.11%)Baseline: 1,860.34 ns | 2,123.76 ns (76.11%) |
function/call 4 args Rhai | 📈 view plot 🚷 view threshold | 1,379.90 ns(-8.52%)Baseline: 1,508.48 ns | 1,675.43 ns (82.36%) |
function/call Lua | 📈 view plot 🚷 view threshold | 222.44 ns(-9.17%)Baseline: 244.90 ns | 278.10 ns (79.99%) |
function/call Rhai | 📈 view plot 🚷 view threshold | 447.85 ns(-1.18%)Baseline: 453.20 ns | 531.22 ns (84.31%) |
loading/empty Lua | 📈 view plot 🚷 view threshold | 874,530.00 ns(+125.00%)Baseline: 388,685.30 ns | 1,509,563.44 ns (57.93%) |
loading/empty Rhai | 📈 view plot 🚷 view threshold | 1,074,800.00 ns(+65.24%)Baseline: 650,445.00 ns | 1,542,515.12 ns (69.68%) |
math/vec mat ops Lua | 📈 view plot 🚷 view threshold | 6,622.90 ns(-15.28%)Baseline: 7,817.06 ns | 9,391.37 ns (70.52%) |
math/vec mat ops Rhai | 📈 view plot 🚷 view threshold | 5,963.40 ns(-11.65%)Baseline: 6,749.42 ns | 7,755.86 ns (76.89%) |
query/10 entities Lua | 📈 view plot 🚷 view threshold | 19,241.00 ns(-7.53%)Baseline: 20,807.70 ns | 22,762.71 ns (84.53%) |
query/10 entities Rhai | 📈 view plot 🚷 view threshold | 19,200.00 ns(-8.13%)Baseline: 20,898.60 ns | 22,657.12 ns (84.74%) |
query/100 entities Lua | 📈 view plot 🚷 view threshold | 39,157.00 ns(-8.68%)Baseline: 42,877.60 ns | 47,041.49 ns (83.24%) |
query/100 entities Rhai | 📈 view plot 🚷 view threshold | 31,824.00 ns(-7.56%)Baseline: 34,426.70 ns | 37,244.07 ns (85.45%) |
query/1000 entities Lua | 📈 view plot 🚷 view threshold | 243,380.00 ns(-13.02%)Baseline: 279,819.00 ns | 324,154.43 ns (75.08%) |
query/1000 entities Rhai | 📈 view plot 🚷 view threshold | 157,190.00 ns(-7.56%)Baseline: 170,046.00 ns | 186,471.03 ns (84.30%) |
reflection/10 Lua | 📈 view plot 🚷 view threshold | 5,548.30 ns(-11.99%)Baseline: 6,304.30 ns | 7,067.03 ns (78.51%) |
reflection/10 Rhai | 📈 view plot 🚷 view threshold | 15,012.00 ns(-5.52%)Baseline: 15,889.30 ns | 16,761.45 ns (89.56%) |
reflection/100 Lua | 📈 view plot 🚷 view threshold | 46,288.00 ns(-12.38%)Baseline: 52,826.60 ns | 59,633.83 ns (77.62%) |
reflection/100 Rhai | 📈 view plot 🚷 view threshold | 699,340.00 ns(-10.33%)Baseline: 779,941.00 ns | 864,096.62 ns (80.93%) |
resource/access Lua | 📈 view plot 🚷 view threshold | 3,407.90 ns(-5.63%)Baseline: 3,611.27 ns | 3,924.94 ns (86.83%) |
resource/access Rhai | 📈 view plot 🚷 view threshold | 5,336.30 ns(-2.10%)Baseline: 5,450.59 ns | 5,657.45 ns (94.32%) |
resource/get Lua | 📈 view plot 🚷 view threshold | 1,838.80 ns(-10.47%)Baseline: 2,053.83 ns | 2,307.54 ns (79.69%) |
resource/get Rhai | 📈 view plot 🚷 view threshold | 3,781.50 ns(-4.96%)Baseline: 3,978.82 ns | 4,283.30 ns (88.28%) |
let mut allocator_guard = allocator.write(); | ||
|
||
let key_ref = ReflectReference::new_allocated_boxed_parial_reflect( | ||
key.to_dynamic(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not convinced about using to_dynamic
, all of of the current API surface avoids non Reflect
values for a reason, any downcasts (i.e. into_script_ref calls) will fail on dynamic structs etc, this will work for primitives but more complex opaque values which don't have a reflect_clone
(I think, i.e. opaque values without an explicit reflect(clone)
, or even anything with a reflect(ignore)
field) will cause issues.
So while this might work individually, but will introduce problems with some bindings.
For example Ref<T>
uses try_downcast_ref::<T>
, which will fail if the value came from to_dynamic
, as DynamicStruct
etc are not concrete Reflect
types. This is why need calls to FromReflect
for getting owned variants of values.
Imo the decision to get a dynamic vs non dynamic value should not be made by the script user, as it's a bit of a leaky abstraction. Ideally the scripter should not need to know anything about the internals here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, I agree — I just made a few APIs to discuss the idea. So, just to clarify, we want to keep only the variant that I currently named with the _clone postfix, right?
also tried to make access to map via default_get/default set instead of separate map_get/insert, it works, but maybe you know some limitations of this approach? but tests runs fine. |
Hey! This PR improves how we work with HashMaps in the scripting layer. Two main things here:
What changed?
HashMap iteration support (commit b632625)
Added proper iteration over maps! Now you can do
for key, value in pairs(map)
in Lua and use.iter()
in Rhai. This was trickier than it looks because Bevy's reflection path system doesn't play nice with maps - it rejectsListIndex
access on map types. So I added a dedicatedReflectMapRefIter
that bypasses the path system and usesMap::get_at()
directly.The iterator returns
(key, value)
tuples that get properly unpacked in both languages - Lua'spairs()
just works, and Rhai gets a wrapped iterator with anext()
method.Fixed
map_get()
to use read-only access (commit fc7d746)Changed
map_get()
from usingfrom_reflect()
toto_dynamic()
. This might seem like a small thing but it's actually pretty important:ReflectFromReflect
registered. No more mysterious failures when accessing certain map value types.with_reflect_mut
towith_reflect
. This is also groundwork for an upcoming PR that will fix Bevy's change detection by properly checkingReflectReference
access modes.from_reflect()
does.to_dynamic()
is simpler.Testing
Added test scripts for both Lua and Rhai that verify:
Existing map operations should all still work as before.