Frage

Ich habe eine Idee, wie ich die Leistung mit dynamischer Code-Generierung verbessern kann, aber ich bin nicht sicher, was der beste Weg ist, um dieses Problem zu nähern.

Angenommen, ich habe eine Klasse


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

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

Die DoCalc Methode ist auf der untersten Ebene, und es wird oft bei der Berechnung genannt. Ein weiterer wichtiger Aspekt ist, dass ValueN ist erst am Anfang festgelegt und nicht bei der Berechnung ändern. So viele der ifs in der DoCalc Verfahren sind nicht erforderlich, da viele ValueN 0 sind, so ich, dass dynamische Code-Generierung hatte gehofft, könnte die Leistung zu verbessern.

Zum Beispiel, wenn ich eine Methode


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

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

und kompilieren Sie es mit Optimierungen auf dem C # -Compiler geschaltet ist intelligent genug, um nur die notwendigen Dinge zu halten. So möchte ich ein solches Verfahren zur Laufzeit erstellen, basierend auf den Werten von ValueN und verwenden Sie die generierte Methode bei der Berechnung.

Ich denke, dass ich Ausdruck Bäume dafür verwenden könnte, aber Ausdruck Bäume funktionieren nur mit einfachen Lambda-Funktionen, so dass ich nicht Dinge wie, wenn, während usw. im Funktionskörper verwenden kann. Also in diesem Fall ich brauche diese Methode in geeigneter Weise zu ändern.

Eine andere Möglichkeit ist es, den notwendigen Code als String zu erstellen und dynamisch kompilieren. Aber es wäre für mich viel besser, wenn ich die bestehende Methode nehmen könnte und ändern Sie es entsprechend an.

Es gibt auch Reflection.Emit, aber ich will nicht zu halten mit ihm, da es sehr schwierig sein würde, zu erhalten.

BTW. Ich bin nicht auf C # beschränkt. Also ich bin offen für Vorschläge von Programmiersprachen, die für diese Art von Problem am besten geeignet sind. Mit Ausnahme von LISP für ein paar Gründe.

Eine wichtige Klarstellung. DoValue1RelatedStuff () ist kein Methodenaufruf in meinem Algorithmus. Es ist nur einige formelbasierte Berechnung und es ist ziemlich schnell. Ich habe es so geschrieben


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

Ich habe einige Performance-Tests durchführen und das kann ich mit zwei ifs sehen, wenn man die optimierte Methode deaktiviert ist etwa 2-mal schneller als mit dem redundanten wenn.

Hier ist der Code, den ich für den Test verwendet:


    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;
        }
    }
War es hilfreich?

Lösung

Ich würde in der Regel nicht einmal denken über eine solche Optimierung. Wie viel Arbeit macht DoValueXRelatedStuff()? Mehr als 10 bis 50 Prozessorzyklen? Ja? Das heißt, Sie gehen ganz ein komplexes System zu bauen, sparen weniger als 10% der Ausführungszeit (und dies scheint recht optimistisch für mich). Dies kann leicht auf weniger als 1% nach unten gehen.

Gibt es keinen Raum für weitere Optimierungen? Bessere Algorithmen? Ein brauchen Sie wirklich nur einen einzigen Prozessorzyklus nehmen einzelne Zweige zu beseitigen (wenn die Verzweigungsvorhersage korrekt ist)? Ja? Sollten Sie nicht denken über Ihren Code in Assembler oder etwas anderes mehr maschinenspezifische Schreiben anstelle von .NET?

Könnten Sie geben die Reihenfolge des N, die Komplexität einer typischen Methode, und das Verhältnis von Ausdrücken in der Regel auf true Auswertung?

Andere Tipps

Es würde mich überraschen, ein Szenario zu finden, wo der Aufwand für die Bewertung der , wenn Aussagen ist die Mühe wert, um dynamisch emit Code.

Moderne CPUs Unterstützung Verzweigungsvorhersage und Zweig predication , die Niederlassungen in kleinen Segmenten von Code gegen Null gehen den Aufwand macht.

Haben Sie Benchmarks für zwei handcodierte Versionen des Codes versucht, eine, die alle haben die if-Anweisungen vorhanden, aber liefert Nullwerte für die meisten, und eine, die alle die gleichen, wenn Zweige entfernt?

Wenn Sie wirklich in Code-Optimierung sind - bevor Sie etwas tun - der Profiler laufen! Es wird Ihnen zeigen, wo der Engpass ist und welche Bereiche sind es wert, zu optimieren.

Auch - wenn die Sprache Wahl nicht begrenzt ist (mit Ausnahme von LISP), dann Assembler in Bezug auf die Leistung schlagen nichts;)

Ich erinnere mich an einige Performance-Magie zu erreichen, indem sie einige innere Funktionen Umschreiben (wie die, die Sie haben) Assembler verwenden.

Bevor Sie etwas tun, Sie haben tatsächlich ein Problem

d. tut es lange genug laufen, Sie zu stören?

Wenn ja, herauszufinden, was tatsächlich die Zeit nehmen, nicht, was Sie erraten . Diese ist die schnelle , schmutzig und sehr effektive Methode, die ich zu sehen, wo die Zeit vergeht.

Nun, Sie sprechen über die Interpretation im Vergleich zu kompilieren. Interpretierten Code ist in der Regel 1-2 Größenordnungen langsamer als kompilierte Code. Der Grund dafür ist, dass Dolmetscher ständig heraus sind herauszufinden, was als nächstes zu tun, und dann zu vergessen , während Code kompiliert nur weiß .

Wenn Sie in dieser Situation sind, dann kann es sinnvoll sein, den Preis zu zahlen für die Übersetzung, um die Geschwindigkeit des kompilierten Codes zu erhalten.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top