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

Optimizing Select Projections, Part II

DZone's Guide to

Optimizing Select Projections, Part II

When it comes to web applications being able to communicate and get data from a database, speed is key. One dev shows how to increase this speed.

· Performance Zone
Free Resource

In the previous post, I showed how we can handle select projections and setup a perf test. The initial implementation we had run in about 4.4 seconds for our test. But it didn’t give any thought to performance.

Let us see if we can do better. The first thing to do is to avoid building the Jint engine and parsing the code all the time. The way we set things up, we wrap the actual object literal in a function, and there is no state, so we can reuse the previous engine instance without any issues. 

That means that we don’t need to pay the cost of creating a new engine, parsing the code, etc. Here is what this looks like:

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

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

        var source = "function project(d) { return " + projection.Substring(start) + "; }";
        engine.Execute(source);

        _cache[projection] = engine;
    }

    var dJs = engine.Object.Construct(new JsValue[0]);
    foreach (var o in d)
    {
        dJs.Put(o.Key, o.Value is string s ? 
            new JsValue(s) : 
            new JsValue(Convert.ToDouble(o.Value)),
            true);
    }

    var result = engine.Invoke("project", new object[] {dJs});

    // do something with the result
}

Note that the cache key here is the raw projection, not the function we send to the entire database, this allows us to avoid any string allocations in the common (cached) path.

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

  • 10K in 574 ms.
  • 5K in 137 ms.
  • 1K in 51 ms.

Just this small change boosted our performance by a major margin.

Note that because the engines are not thread safe, to use that in a real application we’ll need to ensure thread safety. The way I did that is to have a pool of these engines for each projection and just use that, so an engine is always accessible in a single threaded mode.

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

Published at DZone with permission of Oren Eini, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

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

{{ parent.tldr }}

{{ parent.urlSource.name }}