Skip to content

Performance ​

Measured with BenchmarkDotNet on an Apple M1 Ultra and .NET 10, with the cache in memory only (no Redis). The benchmarks are in benchmarks/; run them with task bench.

The short version ​

  • A cache hit costs about 0.2 µs for each dependency the value recorded. FusionCache checks every tag of an entry on every hit, so this is the cost of tagging itself. Sluice adds about 15% on top.
  • Keep each value's dependencies to tens, not thousands. A value built from 1,000 rows read one by one, each recorded with its own Invoice.For(id), costs about 0.24 ms per hit: as much as a database query. Read such a list with one query or one read.Children, or record the source's All, so the value has one dependency.
  • With EF Core, a save costs about 4 µs more, and a cache miss about 4 µs more with a plain query, or 19 µs more through a read helper. Against a database across a network, where a query takes hundreds of microseconds, that's noise.

Cache hits ​

A hit returns the cached value. "FusionCache, same tags" is FusionCache on its own, given as many tags as Sluice records for the same dependencies.

DependenciesFusionCache, no tagsFusionCache, same tagsSluiceSluice allocates
10.18 µs0.54 µs0.67 µs1.0 KB
100.17 µs2.3 µs2.7 µs3.2 KB
1000.17 µs20 µs23 µs25 KB
1,0000.17 µs211 µs239 µs250 KB

Cache misses ​

A miss runs the compute and stores its value. Here the compute returns a constant, so the table shows only the cost of recording and storing; your fetch comes on top.

DependenciesFusionCache, no tagsFusionCache, same tagsSluice
10.58 µs1.5 µs1.1 µs
100.60 µs5.7 µs2.6 µs
1000.58 µs48 µs24 µs
1,0000.59 µs488 µs234 µs

Invalidation ​

Invalidating one key removes two tags: about 1.0 µs through Sluice, against 0.57 µs for FusionCache removing the same two itself. With Redis, each tag is also a Redis write and a backplane message; see When Redis is slow or down.

EF Core ​

On in-memory SQLite, the fastest database there is, so Sluice's share is as large as it gets. Sluice's misses include opening the compute's own DI scope and leasing its context from the pool:

Without SluiceWith Sluice
Save one changed row11.2 µs15.1 µs
Cache miss that reads one row13.2 µs (FusionCache and a plain query)17.0 µs (a plain query on read.Db)
31.9 µs (read.Entity)

The save pays for capturing what changed and invalidating it. A plain query pays for reading its tag and recording the entity types it names; working out what a query reads happens once, when EF compiles it. A miss through read.Entity costs about 15 µs more than the same miss through a plain query.

Not measured ​

  • Redis. A server that hasn't looked a tag up yet reads it from Redis, then keeps it in memory. So the first hit of a value on a fresh server can cost one Redis lookup per tag, and later hits cost what's above.
  • Contention. These run one call at a time.