This project has moved. For the latest updates, please go here.

Performance

Expression Evaluator performs runtime compilation of expressions which while fast, still takes a significant amount of time comparatively and this can cause a noticeable delay if the compilation is performed repeatedly.

In a lot of scenarios you would have an expression that is persisted as a string that does not change frequently. Instead, the values in the expression usually change over time.

Function Caching

We can cache this delegate against the string that created it using your run-of-the-mill Dictionary<TKey, TValue>.

var functionCache = new Dictionary<string, Func<object, object>> ();

public Func<object, object> GetCachedFunction(string expression)
{
   Func<object, object> result;
   if(!functionCache.TryGetValue(expression, out result)
   {
      var exp = new CompiledExpression(expression);
      result = exp.ScopeCompile();
      functionCache.Add(expression, result);
   }
   return result;
}

Our example becomes:

var scope = new Scope() { ... };
var expression = "helper.getPagedItems(data.Items, currentPage)";
var func = FunctionCache.GetCachedFunction(expression);
var iterator = func(scope);  // execute the compiled function against the scope
...
scope = new Scope() { ... }; // Change the scope
iterator = func(scope);  // works as expected, without recompile

This is a real-world example of a templating engine in which I have used ExpressionEvaluator. The template is in the form of XML that resembles HTML/CSS and uses AngularJS-like binding with equivalents of ng-if, ng-repeat.

Dynamics and Performance Degradation in IIS

I am using Expression Evaluator in application, using the caching mechanism. The code that executes the cached functions is called from a WCF web service hosted in IIS7.

I noticed a significant increase in the amount of time taken to execute the cached compiled function when running in IIS as compared to when testing the application in Visual Studio through IIS Express.

For example, a set (around 700+) of very simple expressions accessing a dynamic properties on an ExpandoObject would take 500-800 ms to execute altogether in IIS Express, whereas when running in IIS7 the exact same code would take 1000-1400 ms. (Time was measured using System.Diagnostics.Stopwatch calling Start() immediately before calling the Func<dynamic, object> and Stop() immediately after, and accumulating the results)

As the expression was already compiled, this was not an issue with Expression Evaluator, but with .NET itself. Searching for possible solutions led me to answers on StackOverflow that mentioned something to do with security checks.

The solution for me was to move the code into a WCF service hosted in a Windows Service.

The actual cause of the decrease in performance was never really determined. I felt it would take too much time trying to figure out what settings could be done in IIS to make the compiled code run optimally.

(Update)

After running performance tests on the application with PerfView, I found that a lot of time was spent in the DLR compiling my expressions. This led me to the conclusion that since I was using a dynamic as a scope context object, almost my expressions were being compiled as dynamic. This meant that .NET had no hints as to how to cache the dynamic code and was compiling each time the delegate was executed.

A lot of existing code/binding relied on dynamics in order to work. To fix this with minimal changes to existing bindings, I implemented a custom "dynamic" approach that uses a Dictionary-based scope context, with a getter/setter that was called by Expression Evaluator when a property on an object is not found.

This was a feature that had been requested before but I did not want to add to the code base in order to keep the code "clean". This feature is now part of Expression Evaluator.

Last edited Mar 2, 2016 at 12:02 AM by RupertAvery, version 7

Comments

No comments yet.