Frage

Ich beabsichtige, einen Webdienst zu erstellen, der eine große Anzahl von manuell spezifizierten Berechnungen so schnell wie möglich durchführt und die Verwendung von DLR untersucht hat.

Tut mir leid, wenn dies lang ist, aber Sie können gerne überfliegen und das allgemeine Kern bekommen.

Ich habe die Ironpython -Bibliothek verwendet, da die Berechnungen sehr einfach sind. Mein Arbeitslaptop liefert eine Leistung von etwa 400.000 Berechnungen pro Sekunde.

ScriptEngine py = Python.CreateEngine();
ScriptScope pys = py.CreateScope();

ScriptSource src = py.CreateScriptSourceFromString(@"
def result():
    res = [None]*1000000
    for i in range(0, 1000000):
        res[i] = b.GetValue() + 1
    return res
result()
");

CompiledCode compiled = src.Compile();
pys.SetVariable("b", new DynamicValue());

long start = DateTime.Now.Ticks;
var res = compiled.Execute(pys);
long end = DateTime.Now.Ticks;

Console.WriteLine("...Finished. Sample data:");

for (int i = 0; i < 10; i++)
{
    Console.WriteLine(res[i]);
}

Console.WriteLine("Took " + (end - start) / 10000 + "ms to run 1000000 times.");

Wobei DynamicValue eine Klasse ist, die zufällige Zahlen aus einem vorgefertigten Array zurückgibt (ausgesät und zur Laufzeit gebaut).

Wenn ich eine DLR -Klasse erstelle, um dasselbe zu tun, bekomme ich eine viel höhere Leistung (~ 10.000.000 Berechnungen pro Sekunde). Die Klasse ist wie folgt:

class DynamicCalc : IDynamicMetaObjectProvider
{
    DynamicMetaObject IDynamicMetaObjectProvider.GetMetaObject(Expression parameter)
    {
        return new DynamicCalcMetaObject(parameter, this);
    }

    private class DynamicCalcMetaObject : DynamicMetaObject
    {
        internal DynamicCalcMetaObject(Expression parameter, DynamicCalc value) : base(parameter, BindingRestrictions.Empty, value) { }

        public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args)
        {
            Expression Add = Expression.Convert(Expression.Add(args[0].Expression, args[1].Expression), typeof(System.Object));
            DynamicMetaObject methodInfo = new DynamicMetaObject(Expression.Block(Add), BindingRestrictions.GetTypeRestriction(Expression, LimitType));
            return methodInfo;
        }
    }
}

und wird auf die gleiche Weise genannt/getestet, indem sie Folgendes machen:

dynamic obj = new DynamicCalc();
long t1 = DateTime.Now.Ticks;
for (int i = 0; i < 10000000; i++)
{
    results[i] = obj.Add(ar1[i], ar2[i]);
}
long t2 = DateTime.Now.Ticks;

Wobei AR1 und AR2 vorgefertigt sind, renne gesätte Arrays von Zufallszahlen.

Die Geschwindigkeit ist auf diese Weise großartig, aber es ist nicht einfach, die Berechnung anzugeben. Ich würde im Grunde genommen versuchen, meinen eigenen Lexer & Parser zu erstellen, während Ironpython alles, was ich dort brauche, bereits benötigt.

Ich hätte gedacht, ich könnte von Ironpython viel bessere Leistung erzielen, da es auf dem DLR implementiert ist, und ich könnte mit besserem als was ich bekomme.

Nutzt mein Beispiel den Ironpython -Motor am besten? Ist es möglich, eine deutlich bessere Leistung daraus zu machen?

(Bearbeiten) Wie erstes Beispiel, jedoch mit der Schleife in C#, Einstellen von Variablen und Aufrufen der Python -Funktion:

ScriptSource src = py.CreateScriptSourceFromString(@"b + 1");

CompiledCode compiled = src.Compile();

double[] res = new double[1000000];

for(int i=0; i<1000000; i++)
{
    pys.SetVariable("b", args1[i]);
    res[i] = compiled.Execute(pys);
}

Wo Pys ein Skriptscope von PY und Args1 ist ein vorgefertigtes Array von zufälligen Doppel. Dieses Beispiel wird langsamer ausgeführt, als die Schleife im Python -Code auszuführen und in den gesamten Arrays zu übergeben.

War es hilfreich?

Lösung

Delnans Kommentar führt Sie zu einigen Problemen hier. Aber ich werde nur spezifisch darüber erfahren, was die Unterschiede hier sind. In der C# -Version haben Sie eine erhebliche Menge der dynamischen Anrufe in der Python -Version ausgegeben. Für den Anfang wird Ihre Schleife in INT getippt und es klingt so, als ob AR1 und AR2 stark eingegeben werden. In der C# -Version sind die einzigen dynamischen Vorgänge, die Sie haben, der Aufruf von OBJ.ADD (der 1 Operation in C# ist) und möglicherweise die Zuordnung zu den Ergebnissen, wenn sie nicht auf Objekt tippt, was unwahrscheinlich erscheint. Beachten Sie auch, dass der gesamte Code frei ist.

In der Python -Version haben Sie zuerst die Zuweisung der Liste - dies scheint auch während Ihres Timers zu sein, wo es wie in C# nicht so aussieht. Dann haben Sie den dynamischen Aufruf zum Bereich, zum Glück geschieht dies nur einmal. Dies erzeugt jedoch erneut eine gigantische Liste im Gedächtnis - Delnans Vorschlag von XRange ist hier eine Verbesserung. Dann haben Sie den Schleifenschalter i, der für jede Iteration durch die Schleife zu einem Objekt einbackt wird. Dann haben Sie den Anruf bei B.GetValue (), der tatsächlich 2 dynamische Invocatiosn ist - zuerst ein Get -Mitglied, um die "getValue" -Methode und dann ein Aufruf auf dieses gebundene Methodenobjekt zu erhalten. Dies erstellt erneut ein neues Objekt für jede Iteration der Schleife. Dann haben Sie das Ergebnis von B.GetValue (), die möglicherweise ein weiterer Wert sein kann, der bei jeder Iteration gepoert wird. Dann fügen Sie diesem Ergebnis 1 hinzu und haben bei jeder Iteration einen weiteren Boxbetrieb. Schließlich speichern Sie dies in Ihre Liste, die ein weiterer dynamischer Operation ist. Ich denke, dieser endgültige Vorgang muss sperren, um sicherzustellen, dass die Liste konsistent bleibt (erneut verbessert Delnans Vorschlag, ein Listenverständnis zu verwenden).

Während der Schleife haben wir also zusammengefasst:

                            C#       IronPython
Dynamic Operations           1           4
Allocations                  1           4
Locks Acquired               0           1

Im Grunde genommen ist Pythons dynamisches Verhalten zu einem Preis gegen C#. Wenn Sie das Beste aus beiden Welten wollen, können Sie versuchen, das auszugleichen, was Sie in C# gegen das tun, was Sie in Python tun. Sie können beispielsweise die Schleife in C# schreiben und einen Delegierten aufrufen, bei dem es sich um eine Python -Funktion handelt (Sie können Scope.getVariable> ausführen, um eine Funktion als Delegierter aus dem Geltungsbereich herauszuholen). Sie können auch in Betracht ziehen, ein .NET -Array für die Ergebnisse zuzuweisen, wenn Sie wirklich die letzte Leistung erhalten müssen, da es das Arbeitssatz und das GC -Kopieren verringern kann, indem Sie nicht eine Reihe von Box -Werten behalten.

Um den Delegierten durchzuführen, können Sie den Benutzer schreiben lassen:

def computeValue(value):
    return value + 1

Dann würden Sie im C# -Code tun:

CompiledCode compiled = src.Compile();
compiled.Execute(pys);
var computer = pys.GetVariable<Func<object,object>>("computeValue");

Jetzt können Sie:

for (int i = 0; i < 10000000; i++)
{
    results[i] = computer(i);
}

Andere Tipps

Wenn Sie sich über die Berechnungsgeschwindigkeit Sorgen machen, ist es besser, sich anzusehen niedrig Berechnungsspezifikation? Python und C# sind hochrangige Sprachen, und die Laufzeit implementieren kann viel Zeit für verdeckte Arbeiten verbringen.

Sehen Sie sich diese LLVM -Wrapper -Bibliothek an: http://www.llvmpy.org

  • Installieren Sie es mit: pip install llvmpy ply
  • oder unter Debian Linux: apt install python-llvmpy python-ply

Sie müssen noch einen winzigen Compiler schreiben (Sie können verwenden Ply -Bibliothek) und mit LLVM -JIT -Aufrufen binden (siehe LLVM -Ausführungs -Engine), aber dieser Ansatz kann effektiver sein (generierter Code viel näher am realen CPU -Code), und Multiplattform Vergleich zum .NET -Gefängnis.

LLVM hat bereit zu bedienen Compiler optimieren Infrastruktur, einschließlich vieler Optimierer -Bühnenmodule sowie große Nutzer- und Entwickler -Community.

Schau auch hier: http://gmarkall.github.io/tutorials/llvm-cauldron-2016

PS: Wenn Sie daran interessiert sind, kann ich Ihnen bei einem Compiler helfen und parallel zum Handbuch meines Projekts beitragen. Aber es wird nicht Sprungstart sein, dieses Thema ist auch neu für mich.

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