-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
Developers observe more efficient expiration checks within MemoryCache #47239
Comments
Tagging subscribers to this area: @eerhardt, @maryamariyan Issue DetailsPreviously, we avoided using a
|
Why does that constraint no longer exist? ASP.NET Core does not want timers firing on idle web services. @davidfowl |
We do have timers firing on idle web services. All the time. |
We are moving this out to .NET 7 as we haven't been able to get to it yet during .NET 6. |
I was chatting with @adamsitnik about some possible optimizations for MemoryCache:
There are 5 nullable valuetype fields (_absoluteExpirationRelativeToNow, _slidingExpiration, _size, AbsoluteExpiration, and LastAccessed). Each of those fields adds a single bit to store the Nullable.HasValue state and that bit gets padded out to 8 bytes to preserve the type alignment requirements. Moving those bits to the _state field would save 5*8 = 40 bytes per entry. There are 2 DateTimeOffset fields (LastAccessed and AbsoluteExpiration). DateTimeOffset has an 8 byte ticks value and a 2 byte offset value. The 2 byte offset is padded up to 8 bytes to preserve alignment. If the DateTimeOffset will always be reported as a UTC time then the offset is zero and there is no need to store it. We could instead store an 8 byte DateTime and convert it to a DateTimeOffset when it is accessed if needed. If it is important for users to be able to round trip a non-zero offset in AbsoluteExpiration those two bytes could still be stored separately in a location that won't be padded. The lazy allocation pattern being used for _tokens could be extended to store other uncommon data, for example rename the CacheEntryTokens type to be UncommonCacheEntryFields. We could move the _previous field into the uncommon set saving another 8 bytes. If it is rare for users to set both the _slidingExpiration and AbsoluteExpiration a single TimeSpan field could be used for both with a bit from _state used to store which one is present. If both are used simultaneously the 2nd field can be stored in the uncommon data. 8-16 more bytes savable here with the tradeoff that anyone who does use those fields will have more memory usage. The _cache field could also be removable if we stored that object reference in the same field as the current _tokens reference and use a bit from state to discriminate which one is there. If the uncommon fields are ever needed then the _cache field has to be copied into the uncommon state at that point. The size field could be changed to a 2 byte value within the state structure and if the full 8 bytes are needed then use the uncommon fields. I haven't vetted carefully, but if everything here panned out it should save 88 bytes per entry. |
I wasn't aware of it, I only knew of this one because Adam pointed me here : ) Now I am taking a look... |
#59110 looks like it does the first 2 of the 5 size optimizations, nice : ) |
I think a Timer that can accept an DateTimeOffset / DateTime would be great for this (aka is based on those types) and not like the ones in: System.Timers.Timer and System.Threading.Timer. Likewise I do have need to patch https://github.com/dotnet/runtime/blob/main/src/libraries/System.ComponentModel.TypeConverter/src/System/Timers/Timer.cs to max at uint.MaxValue and not int.MaxValue because for example a TimeSpan or DateTimeOffset that differs by 1 month can be a breaking for me (as I do have need for such a thing where I want to run a task only once a month inside of a program of mine). |
Previously, we avoided using a
Timer
for checking cache expiration because such usage would prevent recycling of idle services; this constraint no longer exists. With aTimer
-based expiration check,MemoryCache
APIs will no longer incur the cost of checking expiration time to spawn a background task to remove expired items. This will yield a ~10% RPS performance gain. See #45842 for more performance benchmark details and conversation.The text was updated successfully, but these errors were encountered: