Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Optimizing Select Projections, Part III

DZone's Guide to

Optimizing Select Projections, Part III

Sometimes simple tweaks can make a huge difference. In this post, we explore caching a part of our code can result in much better performance.

Free Resource

RavenDB vs MongoDB: Which is Better? This White Paper compares the two leading NoSQL Document Databases on 9 features to find out which is the best solution for your next project.  

After optimizing the projection performance by so much by just caching the engine, I got to thinking, we are also creating a new JS object to pass the arguments every single time. What would happen if we cache that?

Here is what I ended up with. Again, all of this is pretty brute force (mainly because I’m writing these posts while the baby is asleep, and I’m trying to get as many of them out before she wakes up):

private static readonly Dictionary<string, (Engine, ObjectInstance)> _cache = new Dictionary<string, (Engine, ObjectInstance)>();

private static void Get(string projection, Dictionary<string, object> d)
{
    if (_cache.TryGetValue(projection, out (Engine Engine, ObjectInstance Arg) v) == false)
    {
        v.Engine = new Engine();
        var start = projection.IndexOf("select", StringComparison.OrdinalIgnoreCase) + "select".Length;

        var source = "function project(d) { return " + projection.Substring(start) + "; }";
        v.Engine.Execute(source);
        v.Arg = v.Engine.Object.Construct(new JsValue[0]);

        _cache[projection] = v;
    }


    foreach (var o in d)
    {
        v.Arg.Put(o.Key, o.Value is string s ? 
            new JsValue(s) : 
            new JsValue(Convert.ToDouble(o.Value)),
            true);
    }

    var result = v.Engine.Invoke("project", new object[] {v.Arg});

    // do something with the result
}

I wouldn’t have expected this to be a dramatic boost, but we got:

And this runs in 0.57 seconds on the 10K, 5K, 1K run.

  • 10K in 413 ms.
  • 5K in 110 ms.
  • 1K in 45 ms.

That is also a third of the cost that we just saved.

This is interesting, so it is worth another check, there are other two potentially expensive operations here, the invocation of the method and the sending of arguments.

Trawling of the Jint code shows that we can remove some abstract by using ICallable directly, and we can cache the array of arguments, this all leads to:

private static readonly Dictionary<string, (ICallable, ObjectInstance, JsValue[])> _cache = new Dictionary<string, (ICallable, ObjectInstance, JsValue[])>();

private static void Get(string projection, Dictionary<string, object> d)
{
    if (_cache.TryGetValue(projection, out (ICallable Func, ObjectInstance Arg, JsValue[] Args) v) == false)
    {
        var engine = new Engine();
        var start = projection.IndexOf("select", StringComparison.OrdinalIgnoreCase) + "select".Length;

        var source = "function project(d) { return " + projection.Substring(start) + "; }";
        engine.Execute(source);
        v.Arg = engine.Object.Construct(new JsValue[0]);
        v.Func = engine.GetValue("project").TryCast<ICallable>();
        v.Args = new JsValue[] {v.Arg};
        _cache[projection] = v;
    }


    foreach (var o in d)
    {
        v.Arg.Put(o.Key, o.Value is string s ? 
            new JsValue(s) : 
            new JsValue(Convert.ToDouble(o.Value)),
            true);
    }


    var result = v.Func.Call(Undefined.Instance, v.Args);

    // do something with the result
}

And this runs in 0.37 seconds on the 10K, 5K, 1K run.

  • 10K in 279 ms.
  • 5K in 59 ms.
  • 1K in 32 ms.

And that is a wow. Because right now, without really doing much at all, we are already an order of magnitude higher than our first attempt, and we can do more.

Get comfortable using NoSQL in a free, self-directed learning course provided by RavenDB. Learn to create fully-functional real-world programs on NoSQL Databases. Register today.

Topics:
performance ,optimization ,web application performance ,database performance

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}