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

More MemoryCache perf improvements #45280

Merged

Conversation

adamsitnik
Copy link
Member

@adamsitnik adamsitnik commented Nov 27, 2020

don't acces expensive CacheEntryHelper.Current if options can't be propagated anyway

CacheEntryHelper.Current uses expensive AsyncLocal. Before accessing it, we can just check if the options can be propagated and don't use it at all if not.

obraz

+7.5% (+18k) RPS for TechEmpower benchmark!

load before after
CPU Usage (%) 19 21
Cores usage (%) 528 574
Working Set (MB) 48 48
Build Time (ms) 4,500 4,541
Start Time (ms) 0 0
Published Size (KB) 76,401 76,401
.NET Core SDK Version 3.1.404 3.1.404
First Request (ms) 76 78
Requests/sec 234,029 252,468
Requests 3,533,646 3,812,090
Mean latency (ms) 1.28 1.18
Max latency (ms) 61.30 47.33
Bad responses 0 0
Socket errors 0 0
Read throughput (MB/s) 736.49 794.52
Latency 50th (ms) 1.06 0.99
Latency 75th (ms) 1.17 1.09
Latency 90th (ms) 1.33 1.23
Latency 99th (ms) 10.08 9.10

inline the expiration check hot paths

obraz

load before after
CPU Usage (%) 21 21
Cores usage (%) 574 580
Working Set (MB) 48 48
Build Time (ms) 4,541 4,923
Start Time (ms) 0 0
Published Size (KB) 76,401 76,401
.NET Core SDK Version 3.1.404 3.1.404
First Request (ms) 78 79
Requests/sec 252,468 259,239
Requests 3,812,090 3,914,333
Mean latency (ms) 1.18 1.15
Max latency (ms) 47.33 48.63
Bad responses 0 0
Socket errors 0 0
Read throughput (MB/s) 794.52 815.83
Latency 50th (ms) 0.99 0.96
Latency 75th (ms) 1.09 1.07
Latency 90th (ms) 1.23 1.19
Latency 99th (ms) 9.10 9.03

…opagated anyway, +20k RPS for TechEmpower benchmark!
@adamsitnik adamsitnik added this to the 6.0.0 milestone Nov 27, 2020
@ghost
Copy link

ghost commented Nov 27, 2020

Tagging subscribers to this area: @eerhardt, @maryamariyan
See info in area-owners.md if you want to be subscribed.

Issue Details

CacheEntryHelper.Current uses expensive AsyncLocal. Before accessing it, we can just check if the options can be propagated and don't use it at all if not.

obraz

+7.5% (+18k) RPS for TechEmpower benchmark!

load before after
CPU Usage (%) 19 21
Cores usage (%) 528 574
Working Set (MB) 48 48
Build Time (ms) 4,500 4,541
Start Time (ms) 0 0
Published Size (KB) 76,401 76,401
.NET Core SDK Version 3.1.404 3.1.404
First Request (ms) 76 78
Requests/sec 234,029 252,468
Requests 3,533,646 3,812,090
Mean latency (ms) 1.28 1.18
Max latency (ms) 61.30 47.33
Bad responses 0 0
Socket errors 0 0
Read throughput (MB/s) 736.49 794.52
Latency 50th (ms) 1.06 0.99
Latency 75th (ms) 1.17 1.09
Latency 90th (ms) 1.33 1.23
Latency 99th (ms) 10.08 9.10
Author: adamsitnik
Assignees: -
Labels:

area-Extensions-Caching, tenet-performance

Milestone: 6.0.0

@adamsitnik adamsitnik changed the title don't acces expensive CacheEntryHelper.Current if options can't be propagated anyway More MemoryCache perf improvements Nov 27, 2020
@@ -220,7 +221,8 @@ public void Dispose()
}
}

internal bool CheckExpired(DateTimeOffset now)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal bool CheckExpired(in DateTimeOffset now)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How much does the ‘in’ help here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just wanted to be 100% sure that this struct is not being copied on Linux.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just wanted to be 100% sure that this struct is not being copied on Linux.

Did this one change make any measurable impact? We should not be sprinkling lots of ins everywhere unless they're actually meaningful.


if (_slidingExpiration.HasValue
&& (now - LastAccessed) >= _slidingExpiration)
return SlowPath(now);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the idea here that the above check can get inlined, and then the “SlowPath” code remains a normal method call? Is there a good guidance on when this pattern should be used?

Side nit - is “SlowPath” a good name for this method?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the idea here that the above check can get inlined, and then the “SlowPath” code remains a normal method call?

Yes, exactly.

Is there a good guidance on when this pattern should be used?

I would say that it should be used when the condtion typically returns false and rarely executes the code inside it (like throwing exceptions) and is ofc on a hot path (small helper methods executed frequently)

Copy link
Member

@eerhardt eerhardt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This LGTM. Nice work here @adamsitnik! Looks like a good perf win.

cc @Tratcher

@@ -215,12 +216,17 @@ public void Dispose()
if (_valueHasBeenSet)
{
_notifyCacheEntryCommit(this);
PropagateOptions(CacheEntryHelper.Current);

if (CanPropagateOptions())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Always leave a comment about why you're doing what appears to be a redundant check.

@adamsitnik adamsitnik merged commit a2f81e7 into dotnet:master Dec 1, 2020
@adamsitnik adamsitnik deleted the anotherMemoryCachePerfImprovement branch December 1, 2020 05:57
@@ -235,27 +241,44 @@ internal void SetExpired(EvictionReason reason)
DetachTokens();
}

private bool CheckForExpiredTime(DateTimeOffset now)
private bool CheckForExpiredTime(in DateTimeOffset now)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same question.

@ghost ghost locked as resolved and limited conversation to collaborators Dec 31, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants