HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Property caching

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
propertycachingstackoverflow

Problem

Trying to figure out how to efficiently cache property calculations with dependency tracking to invalidate the cache. Here is the syntax I have at the moment (one Cache instance supports multiple object properties):

class Multiplication
{
    public int Arg1 { get; set; }
    public int Arg2 { get; set; }
    public int Result => Cache
        .Lazy(() => Arg1 * Arg2)
        .From(() => Arg1, () => Arg2);

    Cache Cache { get; } = new Cache();
}


Here Result is recalculated on Arg* change. Implementation classes:

public class Cache
{
    public LazyProperty Lazy(
        Func factory, 
        [CallerMemberName] string memberName = "") =>
        new LazyProperty(
             value => (Cached)Store.GetOrAdd(memberName, value), 
             new Cached(factory));

    ConcurrentDictionary Store { get; } = 
        new ConcurrentDictionary();
}


This one helps with flow API:

public class LazyProperty
{
    public static implicit operator T(LazyProperty property) => property.Resolve();

    internal LazyProperty(Func, Cached> getOrAdd, Cached value)
    {
        GetOrAdd = getOrAdd;
        Value = value;
    }

    public LazyProperty From(params Func[] dependencies) =>
        new LazyProperty(GetOrAdd, Value.Depending(dependencies));

    Func, Cached> GetOrAdd { get; }
    Cached Value { get; }
    Cached Resolve() => GetOrAdd(Value);
}


Dependency tracking done by:

```
public class Cached
{
public static implicit operator T(Cached cache) => cache.Value;

public Cached(Func factory, params Func[] dependencies)
{
if (factory == null)
throw new ArgumentNullException("factory");

if(dependencies == null || dependencies.Any(d => d == null))
throw new ArgumentNullException("dependancies");

_factory = factory;
_dependencies = dependencies;
}

public Cached Depending(params Func[] dependencies) =>
new Cached(_factory, _dependencies.Concat(dependenci

Solution

You could potentially use incremental computation (think Excel cells that cache values).

Some details (in F#) here:
https://fsprojects.github.io/FSharp.Data.Adaptive/

The above is ported from the Incremental portion of the Aardvark project:
https://github.com/aardvark-platform/aardvark.base

The documentation is all F# since this model works well in a functional style, but it works similarly well in imperative (and is interoperable with C#)
https://rawgit.com/wiki/aardvark-platform/aardvark.docs/docs/base/adaptive-functional-programming.html

These do pretty much exactly what you want, with automatic dependency tracking and lazy value updating (on read).

There are some utility classes for interop with C# as well, and they work decently - but you still end up having to do some manual messy F# interop on occasion.

Unfortunately, the documentation is also a bit lacking, so reading code is often also necessary if you want to do more than the basics, but it's a quite powerful model.

Context

StackExchange Code Review Q#129081, answer score: 4

Revisions (0)

No revisions yet.