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

Optional allocations #69

Open
Serdan opened this issue Dec 2, 2024 · 3 comments
Open

Optional allocations #69

Serdan opened this issue Dec 2, 2024 · 3 comments

Comments

@Serdan
Copy link

Serdan commented Dec 2, 2024

Because Optional is a reference type it causes heap allocations, which adds to garbage collection and all that nasty stuff we want to avoid in game dev. I just did a quick examination and it should be fairly straight-forward to make it a struct instead. This would be a breaking change for anyone who consumes the type directly, so I'm curious what you think @dcronqvist

One option is to add a ValueOptional to use internally and just leave the existing type as is.

I'd be happy to make a PR for whatever solution you decide on.

image

@dcronqvist
Copy link
Owner

You make an interesting point. As we are still in an initial development cycle (version < 1.0.0), I am not afraid to make breaking changes for the benefit of providing a more useful library.

I have made an initial implementation for this request in this branch: value-optional. Does that look like what you had in mind? I can see from running the benchmarks that we are performing much better in terms of heap allocation, but the benchmark maps are quite small. Would you be able to verify against a map you have, or share a map with me to perform further tests?

@Serdan
Copy link
Author

Serdan commented Dec 2, 2024

Something like that, yeah.

I can see you introduced an overload to TmjReaderBase.ReadTileset, but there's actually a trick you can use there. default is a valid default value for struct parameters, and it will skip the constructor and set all fields to the default for that type. In the case of Optional that means HasValue will be set to false, which is what we want.

My test map is also quite small.

Here's another trick for ergonomics:

public readonly struct OptionalEmpty;

public static class Optional
{
  public static readonly OptionalEmpty Empty = default;
}

public readonly struct Optional<T>
{
  // implementation
  public static implicit operator Optional<T>(OptionalEmpty _) => Empty;
}

Allows you to elide the generic param and just call Optional.Empty

@dcronqvist
Copy link
Owner

Those are both really nice tricks, thanks! I've force-pushed the branch again with some of those imrpovements. Let me know what you think. If you feel like it's what you're looking for, I'll clean up the branch and create a PR. I just released a version 3.0.0 today, but I could release another version quite soon with this change if you'd like it soon.

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

When branches are created from issues, their pull requests are automatically linked.

2 participants