Poor performance when using FusionCache as a simple memory cache #507
-
Hi I'm seeing unexpected performance overhead when using FusionCache in a very simple setup — pure memory cache only, with all advanced features disabled. 🔧 ConfigurationHere’s how I register FusionCache: services.AddFusionCache().WithOptions(options =>
{
options.DefaultEntryOptions = new FusionCacheEntryOptions
{
Duration = TimeSpan.FromMinutes(5),
IsFailSafeEnabled = false,
SkipDistributedCacheRead = true,
SkipDistributedCacheWrite = true,
};
options.TagsDefaultEntryOptions = new FusionCacheEntryOptions
{
Duration = TimeSpan.FromMinutes(5),
IsFailSafeEnabled = false,
SkipDistributedCacheRead = true,
SkipDistributedCacheWrite = true,
};
}); So I'm using FusionCache as a memory-only cache, with no distributed cache, no fail-safe, and no fallback logic. 🧪 Test Codeasync Task<string> GetDataAsync()
{
return await fusionCache.GetOrSetAsync(
$"Tag",
async _ => await Task.FromResult("test"),
tags: ["Tags"]
);
} And I measure timings: var stopWatch = new Stopwatch();
stopWatch.Start();
await GetDataAsync();
stopWatch.Stop();
logger.LogInformation("Insert executed in {ElapsedMilliseconds} ms", stopWatch.ElapsedMilliseconds);
stopWatch.Restart();
await GetDataAsync();
stopWatch.Stop();
logger.LogInformation("Read executed in {ElapsedMilliseconds} ms", stopWatch.ElapsedMilliseconds); 📊 Result (FusionCache)
In contrast, using ❓ QuestionIs this performance expected, even when using FusionCache in a memory-only configuration like this? And if not, is there something I’m missing in terms of configuration or tuning to get better performance in this basic use case? Thanks for your time and for all the great work on FusionCache 🙏 |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 1 reply
-
Hi @sacrejohn , Short version: even with a ton more features, First, I'd like to make a couple of points that are important. TaggingI noticed you only posted the test code for Another thing to point out is that, because of the design of tagging in FusionCache (see here if interested), there is a (very) little extra work to be done for some first time reads. This is (imho) negligible, but of course it can depends. But if you don't use tagging, you can totally disable it via
FusionCache fully supports tagging, both with L1 only and with L1+L2, in every possible scenario. HybridCache otoh currently does not support multi-node invalidations, meaning tagging only works locally on a single node. Having everything work correctly in every scenario requires a ton of careful things, and that takes extra time (even though I've been able to make it a really really small amount). Proper benchmarkingTo do proper benchmarking, a simple code like the one outlined above is not enough, since performance measurement in general (and even more so in particular with really small timings, like with this case) is a pretty complex subject. That is why projects like BenchmarkDotNet exist. Also with caching (and not just that tbh), usage on the long run is different than just a measured "first method call": things naturally optimize along the way with usage, so it should be taken into account. And with caching as we know, the real benefit is with multiple reads, so it's even more important. Having said that, for the gist you'll see below I kept a simple console app, but with some changes to make it more realistic. Don't trust meAnyway I created a simple gist you can run on your own, you can find it here. Some things to notice:
Following the results on my machine: MemoryCache KEY: foo
- insert took 3 ms
- read 0 took 0 ms
- read 1 took 0 ms
- read 2 took 0 ms
- read 3 took 0 ms
- read 4 took 0 ms
- read 5 took 0 ms
- read 6 took 0 ms
- read 7 took 0 ms
- read 8 took 0 ms
- read 9 took 0 ms
KEY: bar
- insert took 0 ms
- read 0 took 0 ms
- read 1 took 0 ms
- read 2 took 0 ms
- read 3 took 0 ms
- read 4 took 0 ms
- read 5 took 0 ms
- read 6 took 0 ms
- read 7 took 0 ms
- read 8 took 0 ms
- read 9 took 0 ms HybridCache KEY: foo
- insert took 12 ms
- read 0 took 0 ms
- read 1 took 0 ms
- read 2 took 0 ms
- read 3 took 0 ms
- read 4 took 0 ms
- read 5 took 0 ms
- read 6 took 0 ms
- read 7 took 0 ms
- read 8 took 0 ms
- read 9 took 0 ms
KEY: bar
- insert took 0 ms
- read 0 took 0 ms
- read 1 took 0 ms
- read 2 took 0 ms
- read 3 took 0 ms
- read 4 took 0 ms
- read 5 took 0 ms
- read 6 took 0 ms
- read 7 took 0 ms
- read 8 took 0 ms
- read 9 took 0 ms FusionCache KEY: foo
- insert took 12 ms
- read 0 took 3 ms
- read 1 took 0 ms
- read 2 took 0 ms
- read 3 took 0 ms
- read 4 took 0 ms
- read 5 took 0 ms
- read 6 took 0 ms
- read 7 took 0 ms
- read 8 took 0 ms
- read 9 took 0 ms
KEY: bar
- insert took 0 ms
- read 0 took 0 ms
- read 1 took 0 ms
- read 2 took 0 ms
- read 3 took 0 ms
- read 4 took 0 ms
- read 5 took 0 ms
- read 6 took 0 ms
- read 7 took 0 ms
- read 8 took 0 ms
- read 9 took 0 ms As you can see they are in the same range with, most importantly, all Hope this helps, let me know! |
Beta Was this translation helpful? Give feedback.
-
Sorry for the late reply, and a huge thank you for your detailed answer and the supporting tests! You’re absolutely right — I was mistaken to judge based on a single call. Just because MemoryCache or Hybrid respond quickly on first access doesn't mean FusionCache should do the same — thank you for pointing that out! Without disabling tags entirely, I also noticed that only using them when truly needed improves performance. I’ve updated my code generator accordingly. On a basis of 10,000 calls, I noticed a very slight difference (around 5 ms between FusionCache and Hybrid), but it’s negligible compared to everything FusionCache brings to the table — especially its advanced fallback logic and fine-grained control options. So instead, I focused on optimizing my code to limit unnecessary calls, and I also avoided using async/await when it wasn't needed. Thanks again for your time and insights 🙏 |
Beta Was this translation helpful? Give feedback.
Hi @sacrejohn ,
the performance is different from the one outline here, let me explain (then let me know what you think please).
Short version: even with a ton more features,
FusionCache
is basically as fast asMemoryCache
andHybridCache
, all are at0ms
basically: but I don't want you to just trust me, I'll try to outline why I'm saying so.First, I'd like to make a couple of points that are important.
Tagging
I noticed you only posted the test code for
FusionCache
, where I see you are using tags:MemoryCache
does not support tagging, so it would be apples to oranges, so to speak.Another thing to point out is that, because of the design of tagging in FusionCache (see here if interested)…