Simplified Expressions

Dec 2, 2009 at 3:11 AM

Writing expressions that pass in all inputs for performance reasons is somewhat cumbersome.  For example, it's much nicer to write:

Execute ( () => A + B )

Rather than:

Execute ( this, t => t.A + t.B )

Obviously this is a very simple example, but it's unfortunate that consumers of the library have to be fully aware of this significant performance issue.

In our system, we have a "Getter" method that lives in a base class and sets up the Obtics expressions.  In this, we would have the opportunity to transform an expression like the first form into an expression like the second form.  All we'd need to do is replace all references to "this" with a lambda parameter and then add a lambda parameter to the expression.  Can anyone comment on the easiest way to walk the lambda expression tree to do this transformation?  Is there perhaps a way to use Obtics framework code as a visitor system for performing this transformation?

I haven't thought it through, but perhaps a system that replaces variables with parameters would be useful in a general case for Obtics.  Upon compilation, it could strip the fixed values and make them into parameters, thereby providing greater opportunity for caching.  Obviously there would be some cost for the expression modification, but I imagine it would be significantly less than the cost of compilation.

Thoughts are welcome. :)

Coordinator
Dec 2, 2009 at 4:25 PM

In general use ExpressionObserver.Execute in not so often executed code only. That is when Execute itself is not called very often.

The second variation you advised certanly is faster but when the use of Execute starts to degrade performance I'd advice to switch to the ExpressionObserver.Compile method. Use that method to 'pre-compile' your lambda expression and store the resulting lambda method in a static field. This will automatically force you to use the 't' parameter en give compilation errors wherever you forget to insert one.

Regs,

Thomas.

Dec 2, 2009 at 6:01 PM

I found your ExpressionVisitor class... exactly what I wanted.  I wrote the following class to enable the above scenario with our "Getter" method.

I just have one question... do the names of parameters matter for the compilation cache, or does it just look at the signature?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using Obtics;

namespace Pleasant.Watchers
{
    internal class ConstantExtractionRewriter : ExpressionVisitor
    {
        public static ConstantExtractionRewriter Instance
        {
            get
            {
                if (_Instance == null)
                    _Instance = new ConstantExtractionRewriter();
                return _Instance;
            }
        }
        private static ConstantExtractionRewriter _Instance;

        private object _ConstantValue;
        private ParameterExpression _ConstantParameter;

        /// <summary>
        /// Takes a lambda expression with zero arguments and extracts a single constant into a parameter.
        /// </summary>
        /// <param name="expression"></param>
        /// <param name="constantValue"></param>
        /// <returns></returns>
        public Expression<Func<TConstant, TResult>> Rewrite<TConstant, TResult>(Expression<Func<TResult>> expression, TConstant constantValue)
        {
            _ConstantValue = constantValue;
            _ConstantParameter = Expression.Parameter(constantValue.GetType(), "extractedConstantParameter");
            Expression<Func<TConstant, TResult>> resultExpression =
                Expression.Lambda<Func<TConstant, TResult>>(Visit(expression.Body), _ConstantParameter);
            // Don't keep static references around, preventing garbage collection.
            _ConstantValue = null;
            _ConstantParameter = null;
            return resultExpression;
        }

        protected override Expression VisitConstant(ConstantExpression c)
        {
            if (c.Value == _ConstantValue)
                return _ConstantParameter;

            return base.VisitConstant(c);
        }
    }
}

Dec 2, 2009 at 6:10 PM

Is it fairly fast to execute a compiled lambda expression?   In other words, would it be reasonable to run it thousands of times for all of the items in a list?

In our homegrown solution, we ended up chaining factories together.  Each factory had already precompiled any expressions and then just had to attach propertychanged and collectionchanged event handlers for each item that the factory chain processed.

Coordinator
Dec 2, 2009 at 7:55 PM

When talking about observable expressions then the execution of a pre-compiled (with ExpressionObserver.Compile) expression is way faster than running ExpressionObserver.Execute over the same expression. Execute will at least have to compare the given lambda expression with the contents of the compilation cache and with luck run the same cached compiled lambda expression as would have been generated by Compile. The only problem with pre-compiling is that it is more cumbersome to write the code than just using Execute.

If it is fast enough for your list will depend on your requirements (minutes/seconds/milliseconds), specs of your host, size of your data and the actual expression you try to execute. I'd say just experiment with it.