Skip to content
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

Arena/Allocator for Dynamic types #617

Open
makspll opened this issue Aug 14, 2022 · 11 comments
Open

Arena/Allocator for Dynamic types #617

makspll opened this issue Aug 14, 2022 · 11 comments

Comments

@makspll
Copy link

makspll commented Aug 14, 2022

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:

  • &dyn
  • Box
  • Box
@schungx
Copy link
Collaborator

schungx commented Aug 14, 2022

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.

@makspll
Copy link
Author

makspll commented Aug 14, 2022

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.

@schungx
Copy link
Collaborator

schungx commented Aug 14, 2022

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.

@schungx
Copy link
Collaborator

schungx commented Aug 14, 2022

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...

@makspll
Copy link
Author

makspll commented Aug 14, 2022

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

@schungx
Copy link
Collaborator

schungx commented Aug 14, 2022

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?

@makspll
Copy link
Author

makspll commented Aug 14, 2022

For example the array built-in type: https://rhai.rs/book/language/arrays.html, which is mapped from Vec<Dynamic>

@schungx
Copy link
Collaborator

schungx commented Aug 14, 2022

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.

@schungx
Copy link
Collaborator

schungx commented Aug 14, 2022

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.

@makspll
Copy link
Author

makspll commented Aug 14, 2022

That's interesting and good to know! Anyhow I will report back with any more issues!

@makspll makspll closed this as completed Aug 14, 2022
@makspll
Copy link
Author

makspll commented Aug 14, 2022

Actually maybe I'll leave open with another title

@makspll makspll reopened this Aug 14, 2022
@makspll makspll changed the title Purpose of Double Box in Union::Variant Arena/Allocator for Dynamic types Aug 14, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants