lundi 16 novembre 2015

Compiling many chunks of code into a single method

I have a legacy method which processes various quantities in real time. There is lots of data, and this method is basically a large if/switch mess which decides how to calculate the target value based on certain rules, and does this for each sample received from each device (and there are many of them). Its signature is something like:

double Process(ITimestampedData data, IProcessingRule rule);

where ISample contains multiple different quantities' values for a single timestamp, while IProcessingRule defines which value to use and how to process it to get the result (which can then be compared to a threshold).

I would like to get rid of all ifs and switches and refactor this into a factory which would create a single processing method for each rule, and then run these methods for input data. Since these rules have various parameters, I would also like to see if there is a way to fully resolve all these branches at compile-time (well, run-time, but I am referring to the point where I invoke the factory method once to "compile" my processing delegate).

So, I have something like this, but much more complex (more mutually-dependent conditions and various rules):

 // this runs on each call
 double result;
 switch (rule.Quantity)
 {
     case Voltage: 
     {
          Vector v;
          if (rule.Type == SinglePhase)
          {
             v = data.Vectors[Voltage].Phases[rule.Phase];
             if (rule.Phase == Neutral)
             {
                 v = v * 2; // making this up just to make a point
             }
          }
          else if (rule.Type == Symmetry)
          {
              v = CalculateSymmetry(data.Vectors);
          }

          if (rule.TargetProperty == Magnitude)
          {
              result = v.Magnitude();
              if (rule.Normalize) 
              {
                   result /= rule.NominalValue;
              }
          }
     }

     // ... this doesn't end so soon

Into something like this:

// this is a factory method which will return a single delegate
// for each rule - and do it only once, at startup
Func<ITimestampedData, double> GetProcessor(IProcessingRule) 
{
    Func<ITimestampedData, Vectors> quantityGetter;
    Func<Vectors, Vector> vectorGetter;
    Func<Vector, double> valueGetter;

    quantityGetter = data => data.Vectors[rule.Quantity];

    if (rule.Type == SinglePhase)
    {
        if (rule.Phase == Neutral)
            vectorGetter = vectors => 2 * vectors.Phases[rule.Phase];
        else
            vectorGetter = vectors => vectors.Phases[rule.Phase];
    }
    else if (rule.Type == Symmetry)
    {
        vectorGetter = vectors => CalculateSymmetry(vectors);
    }

    if (rule.TargetProperty == Magnitude)
    {
        if (rule.Normalize) 
            valueGetter = v => v.Magnitude() / rule.NominalValue;
        else 
            valueGetter = v => v.Magnitude();
    }
     ...

    // now we just chain all delegates into a single "if-less" call
    return data => valueGetter(vectorGetter(quantityGetter(data)));
 }

But the problem is:

  1. I still have lots of repetition inside my method,
  2. I have switched ifs for multiple delegate invocations and performance doesn't get any better,
  3. although this "chain" is fixed and known at the end of the factory method, I still don't have a single compiled method which would process my input.

So, finally, my question is:

Is there a way to somehow "build" the final compiled method from these various chunks of code inside my factory?

I know I can use something like CSharpCodeProvider, create a huge string and then compile it, but I was hoping for something with better compile time support and type checking.





Aucun commentaire:

Enregistrer un commentaire