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

Add cache for fromString #92

Merged
merged 7 commits into from
Jun 28, 2022
Merged

Conversation

mcpower
Copy link
Contributor

@mcpower mcpower commented Mar 26, 2022

In Prestige Tree Rewritten, Decimal.fromString takes 16% of scripting time, mostly called from the constructor (which is called from D). Strings constants are passed into Decimal methods, which get converted into new Decimals using D.

These string constants are being converted into strings every time those methods are called. To fix this, Prestige Tree Rewritten could instead pre-convert all string constants used to be Decimals, then use those Decimals instead. However, that would require a LOT of new code and would be unwieldy to use.

Instead, we can implement caching for fromString from break_eternity.js's end. This reduces the aforementioned 16% of scripting time down to 0.6% without changing a line of game code.

This PR introduces a simple LRU cache that I whipped up. It has the ability to change size on-demand by setting Decimal.fromStringCache.maxSize, but we currently don't expose this to users.

The default cache size is mostly arbitrary (1023) and could be changed to another (power of 2) - 1.

In Prestige Tree Rewritten, Decimal.fromString takes 16% of scripting
time, mostly called from the constructor (which is called from D).
Strings constants are passed into Decimal methods, which get converted
into new Decimals using D.

These string constants are being converted into strings every time those
methods are called. To fix this, Prestige Tree Rewritten could instead
pre-convert all string constants used to be Decimals, then use those
Decimals instead. However, that would require a LOT of new code and
would be unwieldy to use.

Instead, we can implement caching for fromString from
break_eternity.js's end. This reduces the aforementioned 16% of
scripting time down to 0.6% without changing a line of game code.
This commit also in-lines the type checks from the constructor into D,
reducing the need for un-necessary type-checks in the constructor.
See the newly added comment in lru-cache.ts for more information.
Prettier explicitly parenthesised `1 << 10 - 1` to `1 << (10 - 1)`, as
bitwise operator precedence is pretty confusing. Thanks Prettier!
@Patashu
Copy link
Owner

Patashu commented Jun 27, 2022

Makes a lot of sense to cache anything that we do a lot that's expensive, especially given the 'naive' way of writing incremental games leads to doing a lot of things like this.

I heard from Razenpok that another thing we'd really like to cache is creating and destroying Decimals, since Javascript has no concept of structs (objects that don't exist on the heap and thus don't need to be heap allocated and garbage collected) so any time we make an object that we could have reused it's a performance hit. I have no idea how hard it would be and what it would involve - would have to look into what incremental games do in practice and optimize for their use case.

(Unless you already did it, it's been a hot minute)

I have just one question before merging. In dc5e216 what's with the change to pow? Is it a mistake or does it have a good reason?

@mcpower
Copy link
Contributor Author

mcpower commented Jun 27, 2022

Oops, that was a mistake to work around Jacorb90/Prestige-Tree#19 when testing. Good catch!

Feel free to fix this when merging, or I can fix it in a few hours if you'd prefer that instead.

@Patashu
Copy link
Owner

Patashu commented Jun 27, 2022

There's a merge conflict too, so give it a look in a few hours and get it set up. Will merge after that. Thanks!

@mcpower
Copy link
Contributor Author

mcpower commented Jun 27, 2022

so any time we make an object that we could have reused it's a performance hit.

I think this is closely related to #86. I've tried to work around this internally with 38bcc55 (D/fromValue_noAlloc returns the same instance of the object if it is cached) in this specific optimisation, but we currently don't nicely expose an interface to minimise allocations/GCs with mutable objects.

@Patashu
Copy link
Owner

Patashu commented Jun 27, 2022

Makes sense, really no surprise that it's hard. What we ACTUALLY want is javascript structs but that might never happen.

dc5e216 introduced an unintended
functional change to Decimal.pow intended to be used for testing only.
@mcpower
Copy link
Contributor Author

mcpower commented Jun 28, 2022

Merge conflict resolved (as a merge commit) and minor mistake fixed.

@Patashu Patashu merged commit 2d464f6 into Patashu:master Jun 28, 2022
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

Successfully merging this pull request may close these issues.

2 participants