Pregunta

Tengo una idea de cómo puedo mejorar el rendimiento con la generación dinámica de código, pero no estoy seguro de cuál es la mejor manera de abordar este problema.

Supongamos que tengo una clase


class Calculator
{
  int Value1;
  int Value2;
  //.......... 
  int ValueN;

  void DoCalc()
  {
    if (Value1 > 0)
    {
      DoValue1RelatedStuff();    
    }
    if (Value2 > 0)
    {
      DoValue2RelatedStuff();    
    }
    //....
    //....
    //....
    if (ValueN > 0)
    {
      DoValueNRelatedStuff();    
    }
  }
}

El método DoCalc está en el nivel más bajo y se llama muchas veces durante el cálculo.Otro aspecto importante es que los valores N sólo se establecen al principio y no cambian durante el cálculo.Muchos de los if en el método DoCalc son innecesarios, ya que muchos de ValueN son 0.Así que esperaba que la generación dinámica de código pudiera ayudar a mejorar el rendimiento.

Por ejemplo, si creo un método


  void DoCalc_Specific()
  {
    const Value1 = 0;
    const Value2 = 0;
    const ValueN = 1;

    if (Value1 > 0)
    {
      DoValue1RelatedStuff();    
    }
    if (Value2 > 0)
    {
      DoValue2RelatedStuff();    
    }
    ....
    ....
    ....
    if (ValueN > 0)
    {
      DoValueNRelatedStuff();    
    }
  }

y compilarlo con optimizaciones activadas, el compilador de C# es lo suficientemente inteligente como para conservar solo lo necesario.Por lo tanto, me gustaría crear dicho método en tiempo de ejecución en función de los valores de ValueN y utilizar el método generado durante los cálculos.

Supongo que podría usar árboles de expresión para eso, pero los árboles de expresión solo funcionan con funciones lambda simples, por lo que no puedo usar cosas como if, while, etc.dentro del cuerpo de la función.Entonces, en este caso necesito cambiar este método de manera apropiada.

Otra posibilidad es crear el código necesario como una cadena y compilarlo dinámicamente.Pero sería mucho mejor para mí si pudiera tomar el método existente y modificarlo en consecuencia.

También está Reflection.Emit, pero no quiero seguir con él porque sería muy difícil de mantener.

POR CIERTO.No estoy restringido a C#.Por eso estoy abierto a sugerencias de lenguajes de programación que sean más adecuados para este tipo de problema.Excepto LISP por un par de razones.

Una aclaración importante.DoValue1RelatedStuff() no es una llamada a un método en mi algoritmo.Es sólo un cálculo basado en fórmulas y es bastante rápido.debería haberlo escrito así


if (Value1 > 0)
{
  // Do Value1 Related Stuff
}

He realizado algunas pruebas de rendimiento y puedo ver que con dos if cuando uno está deshabilitado, el método optimizado es aproximadamente 2 veces más rápido que con el if redundante.

Aquí está el código que utilicé para las pruebas:


    public class Program
    {
        static void Main(string[] args)
        {
            int x = 0, y = 2;

            var if_st = DateTime.Now.Ticks;
            for (var i = 0; i  < 10000000; i++)
            {
                WithIf(x, y);
            }
            var if_et = DateTime.Now.Ticks - if_st;
            Console.WriteLine(if_et.ToString());

            var noif_st = DateTime.Now.Ticks;
            for (var i = 0; i  < 10000000; i++)
            {
                Without(x, y);
            }
            var noif_et = DateTime.Now.Ticks - noif_st;
            Console.WriteLine(noif_et.ToString());

            Console.ReadLine();

        }

        static double WithIf(int x, int y)
        {
            var result = 0.0;
            for (var i = 0; i  < 100; i++)
            {
                if (x > 0)
                {
                    result += x * 0.01;
                }
                if (y > 0)
                {
                    result += y * 0.01;
                }
            }
            return result;
        }

        static double Without(int x, int y)
        {
            var result = 0.0;
            for (var i = 0; i < 100; i++)
            {
                result += y * 0.01;
            }
            return result;
        }
    }
¿Fue útil?

Solución

Me lo general ni siquiera pensar en tal optimización. ¿Cuánto trabajo realiza DoValueXRelatedStuff()? Más de 10 a 50 ciclos de procesador? ¿Si? Eso significa que se va a construir un sistema bastante complejo para salvar a menos de 10% el tiempo de ejecución (y esto parece bastante optimista para mí). Esto puede ir fácilmente a menos del 1%.

¿No hay espacio para otras optimizaciones? Mejores algoritmos? Una lo que realmente necesita para eliminar las ramas individuales tomando sólo un único ciclo del procesador (si la predicción es correcta rama)? ¿Si? ¿No deberías pensar en escribir el código en ensamblador o alguna otra cosa más específica de la máquina en lugar de utilizar .NET?

Podría dar la orden de N, la complejidad de un método típico, y la relación entre las expresiones que evalúan generalmente a cierto?

Otros consejos

Se me sorprendería encontrar un escenario en el que los gastos generales de la evaluación de la si afirmaciones es la pena el esfuerzo para emitir dinámicamente código.

predicción de saltos y predication rama , que hace que la sobrecarga para sucursales en pequeños segmentos de enfoque código cero.

¿Ha tratado de dos versiones de referencia de codificación manual del código, que tiene todas las sentencias if en su lugar, pero proporciona valores cero para la mayoría, y uno que elimina todos los mismos si las ramas?

Si usted está realmente en la optimización del código - antes de hacer nada - ejecutar el generador de perfiles! Se le mostrará donde el cuello de botella es y qué áreas son la optimización de la pena.

También - si la opción de idioma no se limita (a excepción de LISP) entonces nada le ganará a ensamblador en términos de rendimiento;)

Recuerdo lograr un rendimiento un poco de magia reescribiendo algunas funciones internas (como el que usted tiene) usando ensamblador.

Antes de hacer nada, ¿realmente tienen un problema

es decir. no correr el tiempo suficiente para molestar?

Si es así, averiguar lo que realmente está tomando tiempo, no lo adivinas . Esta es la rápida , método sucio, y altamente eficaz que use para ver donde pasa el tiempo.

Ahora, se habla de interpretación frente a la compilación. código interpretado es típicamente 1-2 órdenes de magnitud más lento que el código compilado. La razón es que los intérpretes están pensando continuamente qué hacer a continuación, y luego olvidarse , mientras que el código compilado solo sabe .

Si usted está en esta situación, entonces puede tener sentido para pagar el precio de la traducción con el fin de obtener la velocidad de código compilado.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top