Open makspll opened 2 years ago
Size. An enum
's size is the size of the largest variant (plus the discriminant). It is of vital interest to keep Dynamic
as small as possible
Box<dyn Trait>
is a fat pointer, meaning that it is two words long.
Box<Box<dyn Trait>>
goes through an extra level of redirection, but makes the data type only one word long.
Hmm I see now!
I do wonder if it's possible to get the best of both worlds by using some sort of custom allocator/arena, and storing an allocation ID instead. Going through a custom allocator could allow for some neat optimizations with type-erased data structures etc.
Yes, an arena would be a nice addition, especially when you can serialize different script runs (e.g. you're running a single-threaded even loop that pulls events one at a time). Then each script run can use the arena which gets cleared out at the end.
But still, Rhai originally work on the assumption that trait objects are rare -- most scripts would be working with integers, strings, booleans etc.
If that's not the case, what I can do is add a feature, e.g. heavy_variants
, which would remove the unnecessary level of Boxing at the expense of a larger Dynamic
type. How much it would speed up remains to be benchmarked however...
Although I suspect that only your function call arguments are trait objects; all internal script code are predominantly standard types... Remember, in Rhai, every operator is a function call, so you'd want Dynamic
to be small...
That's probably a fair assumption, this issue was just my first instinct so take it with a grain of salt!
I think Dynamic is a tool I'll personally use mostly for the ECS system, i.e. pulling a component from the game and then that component can be of any type. That and probably for binary operators where lhs and rhs can be many things (haven't looked how they work yet but I imagine that might be necessary).
heavy_variants could be useful but it's really hard to tell for me atm, the arena would probably be much more of a general solution,
my main worry were collection types since they seem to use Dynamic, meaning a lot of cache misses for any sort of collection type
my main worry were collection types since they seem to use Dynamic, meaning a lot of cache misses for any sort of collection type
Why? You mean a collection of Dynamic
values that hold trait objects?
For example the array built-in type: https://rhai.rs/book/language/arrays.html, which is mapped from Vec<Dynamic>
Well, any collection of trait objects will necessarily involve allocations and redirections.
You can register a special collection type that holds just a particular type, plus an API to access it.
For example, you can register Vec<R>
where R
is any non-trait-object type. Then register the necessary indexers and methods (such as len
). Then in Rhai it essentially serves as a TypedArray
similar to JavaScript. This way you avoid spreading your data all over the place.
Nevertheless, I would caution that allocations does not necessarily equate cache misses. Modern memory allocators try to put similarly-sized allocations close to each other. You may find that your array items actually reside together in memory.
I did some testing over this when deciding whether to pack AST nodes into an array, but it seems that all the nodes are allocated close to each other in memory anyway and there is no net gain.
That's interesting and good to know! Anyhow I will report back with any more issues!
Actually maybe I'll leave open with another title
I've been browsing the code, but cannot seem to find the use for the double allocation, What's the purpose of it?
This effectively means there are three indirection layers between the consumer and the underlying type: