Skip to content

I heard you all liked breaking changes...

Choose a tag to compare

@grofit grofit released this 24 Oct 20:19
· 7 commits to main since this release
d61a1c6

I JEST!!! kind of...

So this release is quite a big one, and really the only thing I wanted to add was MultiplexingSystem and it ended up highlighting some allocation issues which while a problem in benchmarking scenarios, is not really an issue for most real world situations, but as there was now clear metrics on some slow parts it seemed like a good time to spike some solutions.

Enter ILazyComputed

To quote my good friend Edgar Vautrine

You don't want the very best. You want cheap. And I got cheap

This is something that was toyed with before but due to the old Computed approach being a bit more complex it wasn't really viable, however with the previous few versions re-writing most of the Computed eco system, it now felt like a good time to give it another try, and it works pretty well.

Until now all computed classes would re-evaluate their data on a dependent change, i.e if your data source was a ComputedEntityGroup and it changed, it would automatically refresh its internal computed data, however in reality you don't really need to do this for all computeds, its only when something asks for the Value that you need to refresh the data, as the Lazy prefix suggests.

As part of this the Lazy computeds offer you a new OnHasChange<Unit> which rather than giving you the payload can notify you if the computed data should be refreshed, which internally drives an IsDirty flag.

Historically if you had 100 entities join a group and you requested the Value you would have had 100 refreshes of the data occur internally so the Value was always up to date even if you didn't need it, however now it will only result in a single refresh of data which occurs at point of getting the Value, internally it knows 100 changes have happened but it wont bother refreshing the computed data until you need it.

This also means that the OnChanged will only get triggered when the internal refresh occurs, so using the above example you would have had 100 OnChanged triggers, but now you will only get 1 when the Value is read, if this is a problem for you then you can always use the OnHasChange and subscribe to it, and have that run ForceRefresh() which will forcibly refresh internal state and trigger OnChanged<T>.

This may not be for everyone and thats why we still have the non lazy variants but internally we have moved a lot of innards over to use this new lazy approach which reduces allocations, cpu time and surplus chatter of observables.

You said something about MultiplexingSystem before?

Yeah so this is a new system type, because we don't have enough of them already 😄.

Gist of it is that its a BatchedSystem but rather than it having its own logic, it delegates to IMultipledJob which contains the logic, allowing you to run several jobs with the same components in the same schedule, you can read more in the docs for it.

For example if you had 3 systems which ran every update and took the same components, even though they would all require same data it would be accessed separately meaning it would need to read out components for each system and pass them in to execute. This side steps that by only reading the batches once, and them passing them to each job back to back, so you only pay for the batch lookup once not per system.

Other Minor Changes

  • IPool now has Clear method, most of its implementations had one already anyway so made it more formal at interface level
  • IEntityCollection has renamed RemoveAll to Clear to keep in line with other collection terminology
  • IComputedComponentGroup exposes a ref ReadOnlyMemory<T...> Batches {get;} which allows you to pass the ref to the pool data, sidestepping some byval pass throughs
  • Lots of higher level Computed objects like IComputedComponentGroup, IComputedEntityGroup and others now use the Lazy computed interfaces/inheritance
  • Lots of computeds that exposed their own RefreshData method are streamlined and replaced with ForceRefresh

Also a big thankyou to Jetbrains for renewing our open source license for dotTrace and dotMemory which made investigating and diagnosing all this stuff a lot easier.