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

Optimizing Select Projections: Interlude, Refactoring

DZone's Guide to

Optimizing Select Projections: Interlude, Refactoring

Learn how to refactor your code in order to clean it up, enabling further performance optimization, by way of introducing a couple of delegates.

· Performance Zone
Free Resource

So we got an order of magnitude performance boost, without really doing much, to be honest. But the code is starting to look really ugly, and future optimizations are out until we fix it.

Since we don’t really care about the internal details, we can do two things in one move. First, we’ll clean up the code by introducing a delegate that will abstract the handling and the second is that this delegate will also allow us to handle caching of additional values simply by way of capturing the delegate state.

Here is what the code looks like:

private delegate object Project(Dictionary<string, object> d);

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

private static void Get(string projection, Dictionary<string, object> d)
{
    if (_cache.TryGetValue(projection, out Project v) == false)
    {
        v = ProjectViaJint(projection);

        _cache[projection] = v;
    }

    var result = v(d);
    // do something with the result
}

private static Project ProjectViaJint(string projection)
{
    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);
    var arg = engine.Object.Construct(new JsValue[0]);
    var args = new JsValue[] {arg};
    var func = engine.GetValue("project").TryCast<ICallable>();

    return src =>
    {
        foreach (var o1 in src)
        {
            var value1 = o1.Value is string s1 ? new JsValue(s1) : new JsValue(Convert.ToDouble(o1.Value));
            arg.Put(o1.Key, value1,
                true);
        }
        return func.Call(JsValue.Undefined, args);
    };
}

Note that we have it broken apart into distinct steps, and the ProjectViaJint method is self-contained and any caching it needs is done internally. This is much-needed refactoring if we want to be able to continue to optimize this code, and it runs in about the same speed as the previous version, so we didn’t hurt anything there.

With this in place, we can see how much better we can make things.

Before we go on, I want to emphasise that this code is actually making a lot of assumptions. The passed dictionary must always have the same set of fields, for example, otherwise, we may see a value from a previous iteration. No null handling or error handling is in place, we aren’t showing the unpacking of the values from the JS env to our code, and plenty of other stuff. That isn’t that important at this point because I’m showing the progression of optimizations, rather than production code.

Topics:
code optimization ,interlude ,refactoring ,performance

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 }}