When the programmer has nothing to do or optimize the code using Linq.Expression

It so happened that I have not been able to actively code for five years. So every chance to get into the code and mess with your comrades there is perceived with joy, as an opportunity to shake the old days and make sure that there are still "berries in the buttocks" (aka an awl in the ass). Yes, and I'll make a reservation right away that the article is more of a tutorial, not from the series "look wow how cool I can", but from the series "oh, what a good option to show with a simple example the use of a technology that some colleagues find difficult. It is my deep belief that such solutions should be included in the arsenal of any C # developer.





Just now I reviewed the code, which includes a multi-step approximation process, in which the constant obtaining of the coefficient is actively involved (The coefficient is selected for a range of values. goes from the IGalla CSF table according to the current speed of the body (the range of speeds corresponds to a specific CSF, about 300 elements in the table). 





The process is multi-step, the search operation is performed often, and the kids-developers were great, went even further than binary search, implemented the calculation of an integer key for a range of values ​​and obtaining a coefficient for this key through Dictionary



And since the Agile team, and I, ugly, also SixSigma and I really like to see everything in numbers - then the guys convincingly showed the effectiveness of the chosen solution. For 1 calculation from 1000 points of approximation, the costs are:





Linear search in the table - 0.6ms

Linear search by chain if-return - 0.1ms

Search by division in half - 0.08ms

Search by dictionary - 0.018ms





It hit my desk at the moment when the project crawled to the stage “and now we are doing the same calculations on the microcontroller”. It turned out that the transfer of the dictionary was somehow not very good (yes, they could have thought in advance, but they faced microcontrollers for the first time, so we will forgive them for this omission). 





Thanks to the team, they have already counted the numbers for “thinking”, and I noticed that the gain of “clean code” versus searching in the data structure is 6 times (first lines). And saving the dictionary against halving - a little more than 4 times. 





“ ”, - -, data-driven , 20 . - .NET “ ”. if , Linq ? , . , , “” . 





- . . - - . - if, , - if, . 





if, , . - range (), , (value), (v) .   , , Linq - , if if.





If (v >= from && v < to) return value;
      
      



public Expression CreateSimpleIf(double from, double to, 
                      double value, 
                      Expression v, LabelTarget returnTarget)
{
    var returnStmt = Expression.Return(
      returnTarget, 
      Expression.Constant(value));
    var ifCondition = Expression.AndAlso(
        Expression.GreaterThanOrEqual(v, Expression.Constant(from)), 
        Expression.LessThan(v, Expression.Constant(to)));
    return Expression.IfThen(ifCondition, returnStmt);
}
      
      







. 1 2 , , -,   , . 





, . 









if (v >= mid.from && v < mid.to) 
   return mid.value 
if (v < mid.from)    
    return search in (0...mid-1) 
else    
    return search in (mid + 1...length - 1);
      
      



Span



/ / “ ”. 





public Expression CreateSpanExpression(Span<Coefficient> span, 
          Expression v, 
          LabelTarget returnTarget)
{
    if (span.Length == 1)
        return CreateSimpleIf(span[0].RangeFrom, 
                   span[0].RangeTo, 
                   span[0].Value, 
                   v, returnTarget);
    else if (span.Length == 2)
    {
        Expression[] ifs = new Expression[2];
        ifs[0] = CreateSimpleIf(span[0].RangeFrom, 
                   span[0].RangeTo, 
                   span[0].Value, 
                   v, returnTarget);
        ifs[1] = CreateSimpleIf(span[1].RangeFrom, 
                   span[1].RangeTo, 
                   span[1].Value, 
                   v, returnTarget);
        return Expression.Block(ifs);
    }
    else
    {
        int mid = span.Length / 2;
        Expression[] blocks = new Expression[2];
        blocks[0] = CreateSimpleIf(span[mid].RangeFrom, 
                       span[mid].RangeTo, 
                       span[mid].Value, 
                       v, returnTarget);

        var leftSide = 
          CreateSpanExpression(span.Slice(0, mid), 
                               v, returnTarget);
        var rightSide = 
          CreateSpanExpression(span.Slice(mid + 1, 
                               span.Length - mid - 1), 
                               v, returnTarget);

        Expression condition = 
          Expression.LessThan(v, 
                              Expression.Constant(span[mid].RangeFrom));
        blocks[1] = Expression.IfThenElse(condition, leftSide, rightSide);
        return Expression.Block(blocks);
     }
} 
      
      



- . , , lambda- . 





public Func<double, double> CreateBTReeExpression()	
{
    var value = Expression.Parameter(typeof(double), "value");
    var returnTarget = Expression.Label(typeof(double));

    var ifs = CreateSpanExpression(mCoefficients.ToArray(), 
                                   value, returnTarget);
    var body = Expression.Block(typeof(double), 
                 new Expression[] 
                 { 
                   ifs, 
                   Expression.Label(returnTarget, 
                     Expression.Constant(0.0))
                 });

    var expression = Expression.Lambda(typeof(Func<double, double>), 
                       body, 
                       new ParameterExpression[] { value });
    var functionDelegate = expression.Compile();
    return (Func<double, double>) functionDelegate;        
}
      
      







, , ? 0.012ms, 1.5 Dictionary



. . 





, , , “” - , , , “” . , - , .  





, , , “ IDE” , . 





, , , , - , , , , ( , ). “, ”.  





, – , , . silver bullet .





P.S.: , , . - , , , , , . , , , . .








All Articles