Frage

, fragte ich eine Frage zu Lua perfromance, und auf der Antworten gefragt:

  

Haben Sie allgemeine Tipps für die Aufbewahrung Lua Leistung hoch untersucht? das heißt das Erstellen von Tabellen kennt und eher eine Tabelle wiederverwenden als einen neuen erstellen, die Verwendung von ‚lokalem print = print‘ und solche globale Zugriffe zu vermeiden.

Dies ist eine etwas andere Frage von Lua Patterns, Tipps und Tricks da würde ich wie Antworten, die speziell Leistung und (wenn möglich) eine Erklärung, warum die Leistung auswirken wird beeinträchtigt.

Ein Tipp pro Antwort wäre ideal.

War es hilfreich?

Lösung

Als Antwort auf einige der anderen Antworten und Kommentare:

Es ist wahr, dass als Programmierer Sie in der Regel vorzeitige Optimierung vermeiden sollten. Aber . Das ist nicht so gilt für Skriptsprachen, wo der Compiler nicht viel optimieren -. Oder gar

Also, wenn Sie etwas in Lua zu schreiben, und das ist sehr oft ausgeführt wird, wird in einer zeitkritischen Umgebung ausgeführt werden oder für eine Weile laufen könnte, ist es eine gute Sache, zu wissen, was zu vermeiden (und vermeiden).

Dies ist eine Sammlung von dem, was ich im Laufe der Zeit herausgefunden hat. Ein Teil davon fand ich über das Netz, aber eine verdächtigen Natur zu sein, wenn die interwebs sind besorgt ich alle es selbst getestet. Außerdem habe ich die Lua Leistung Papier bei Lua.org lesen.

Einige Referenz:

Vermeiden Sie Globals

Dies ist eines der am häufigsten verwendeten Hinweise, aber die besagt, es einmal mehr kann nicht schaden.

Globals wird in einer Hash-Tabelle mit ihrem Namen gespeichert. Zugriff auf sie bedeutet, dass Sie einen Tabellenindex zugreifen müssen. Während Lua eine ziemlich gute Hash-Tabelle Implementierung hat, ist es noch viel langsamer als eine lokale Variable zugreifen. Wenn Sie Globals verwenden müssen, weisen Sie ihren Wert auf eine lokale Variable, das ist schneller bei der 2. Variable Zugriff.

do
  x = gFoo + gFoo;
end
do -- this actually performs better.
  local lFoo = gFoo;
  x = lFoo + lFoo;
end

(Nicht, dass einfache Tests zu unterschiedlichen Ergebnissen führen kann. ZB. local x; for i=1, 1000 do x=i; end hier die für Schleifenkopf tatsächlich mehr Zeit als die Schleifenkörper nimmt, so könnte Profilierungs Ergebnisse verzerrt.)

Vermeiden Sie String Schaffung

Lua-Hashes alle Saiten auf Erstellung, macht diesen Vergleich und mit ihnen sehr schnell in Tabellen und reduziert die Speichernutzung, da alle Strings intern nur einmal gespeichert werden. Aber es macht String Schaffung teurer.

Eine beliebte Option übermäßige String Erzeugung zu vermeiden Tabellen verwendet. Zum Beispiel, wenn Sie eine lange Zeichenfolge zu montieren haben, eine Tabelle erstellen, legen Sie die einzelnen Strings in dort und dann table.concat es zu verbinden verwenden einmal

-- do NOT do something like this
local ret = "";
for i=1, C do
  ret = ret..foo();
end

Wenn foo() nur den Charakter A zurückkehren würde, würde diese Schleife eine Reihe von Zeichenketten wie "" schaffen, "A", "AA", "AAA" usw. Jeder String gehasht werden würde, und bis die Anwendung beendet im Speicher befinden - siehe das Problem hier ?

-- this is a lot faster
local ret = {};
for i=1, C do
  ret[#ret+1] = foo();
end
ret = table.concat(ret);

Diese Methode nicht Strings schaffen überhaupt während der Schleife wird die Zeichenfolge in der Funktion foo erstellt und nur Verweise in die Tabelle kopiert. Danach schafft concat eine zweite Zeichenfolge "AAAAAA..." (je nachdem, wie groß C ist). Beachten Sie, dass könnte Verwendung i statt #ret+1 aber oft Sie haben nicht so eine nützliche Schleife und Sie werden keine Iteratorvariable haben Sie verwenden können.

Ein weiterer Trick, den ich irgendwo auf lua-users.org gefunden ist gsub zu verwenden, wenn Sie eine Zeichenfolge zu analysieren haben

some_string:gsub(".", function(m)
  return "A";
end);

Dieses auf den ersten seltsam aussieht, ist der Vorteil, dass gsub eine Zeichenfolge „auf einmal“ in C erzeugt, die nur gehasht wird, nachdem es wieder zu lua geben wird, wenn kehrt gsub. Dies vermeidet das Erstellen von Tabellen, aber möglicherweise mehr Funktion Overhead (nicht, wenn Sie anrufen foo() sowieso, aber wenn foo() ist eigentlich ein Ausdruck)

Vermeiden Funktion Kopf

Verwenden Sprachkonstrukte statt Funktionen soweit möglich

Funktion ipairs

Wenn Sie eine Tabelle iteriert die Funktion über Kopf aus ipairs nicht rechtfertigt es Gebrauch ist. Um eine Tabelle zu wiederholen, verwenden Sie stattdessen

for k=1, #tbl do local v = tbl[k];

Es tut genau das gleiche, ohne den Funktionsaufruf Overhead (Paare gibt tatsächlich eine andere Funktion, die dann für jedes Element in der Tabelle aufgerufen wird, während #tbl nur einmal ausgewertet wird). Es ist viel schneller,selbst wenn müssen Sie den Wert. Und wenn Sie dies nicht tun ...

Hinweis für Lua 5.2 : In 5.2 Sie im Metatabelle ein __ipairs Feld tatsächlich definieren können, die hat ipairs in einigen Fällen nützlich machen. Allerdings Lua 5.2 auch die __len Feldarbeit für Tabellen macht, so dass Sie möglicherweise noch den obigen Code lieber als dann die ipairs metamethod __len wird nur einmal aufgerufen, während für ipairs würden Sie eine zusätzliche Funktion Anruf erhalten pro Iteration.

Funktionen table.insert, table.remove

Einfache Verwendung von table.insert und table.remove kann stattdessen mithilfe des # Operators ersetzt werden. Im Grunde ist dies für eine einfache Push-und Pop-Operationen. Hier sind einige Beispiele:

table.insert(foo, bar);
-- does the same as
foo[#foo+1] = bar;

local x = table.remove(foo);
-- does the same as
local x = foo[#foo];
foo[#foo] = nil;

Für Verschiebungen (z. B. table.remove(foo, 1)), und wenn sie mit einem spärlichen Tisch endet nicht erwünscht ist, ist es natürlich noch besser auf die Tabellenfunktionen zu verwenden.

Verwenden Sie Tabellen für SQL-IN vergleicht gleichermaßen

Das könnte dir - oder auch nicht - haben Entscheidungen im Code wie folgt aus

if a == "C" or a == "D" or a == "E" or a == "F" then
   ...
end

Jetzt dies ein absolut gültiger Fall ist jedoch (aus meinen eigenen Tests) mit 4 Vergleichen beginnen und Tabellenerzeugung ohne, das ist eigentlich schneller:

local compares = { C = true, D = true, E = true, F = true };
if compares[a] then
   ...
end

Und da Hash-Tabellen haben konstante Look Zeit, der Performance-Gewinn steigt mit jedem weiteren Vergleich. Auf der anderen Seite, wenn „die meiste Zeit“ ein oder zwei Vergleiche übereinstimmen, können Sie mit dem Booleschen Weg oder eine Kombination besser dran.

Vermeiden Sie häufige Tabellenerstellung

Dies wird gründlich in Lua Performance-Tipps diskutiert. Grundsätzlich ist das Problem ist, dass Lua Tisch auf Anfrage reserviert und es auf diese Weise tut, wird tatsächlich mehr Zeit als es reinigt Inhalt ist und es wieder zu füllen.

Dies ist jedoch ein bisschen ein Problem, da Lua selbst kein Verfahren für nicht bieten alle Elemente aus einer Tabelle zu entfernen und pairs() ist nicht die Leistung Tier selbst. Ich habe keine Performance-Tests auf diesem Problem selbst noch nicht getan.

Wenn Sie können, eine C-Funktion definieren, die eine Tabelle löscht, ist dies eine gute Lösung für die Tabelle Wiederverwendung sein sollte.

Vermeiden Sie das gleiche immer und immer

Dies ist das größte Problem, denke ich. Während ein Compiler in einer nicht-interpretierte Sprache leicht viele Redundanzen optimieren weg kann, Lua nicht.

Memoize

Mit Tabellen können diese ganz einfach in Lua erfolgen. Für Single-Argument Funktionen können Sie sogar mit einem Tisch und __index metamethod ersetzen. Auch wenn diese transparancy zerstört, ist die Leistung besser auf zwischengespeicherten Werte aufgrund einer weniger Funktionsaufruf.

Hier ist eine Implementierung von memoization für ein einziges Argument eines Metatabelle verwenden. (Wichtig:. Diese Variante funktioniert nicht einen Nullwert Argument unterstützt, ist aber verdammt schnell für bestehende Werte)

function tmemoize(func)
    return setmetatable({}, {
        __index = function(self, k)
            local v = func(k);
            self[k] = v
            return v;
        end
    });
end
-- usage (does not support nil values!)
local mf = tmemoize(myfunc);
local v  = mf[x];

Sie könnte tatsächlich dieses Muster für mehrere Eingangswerte ändern

Teil Anwendung

Die Idee ist ähnlich wie memoization, die auf „Cache“ Ergebnisse ist. Aber hier statt Cachen der Ergebnisse der Funktion, würden Sie Zwischenwert zwischenzuspeichern durch ihre Berechnung in einer Konstruktor Funktion setzen, die die Berechnungsfunktion in seinem Block definiert. In Wirklichkeit würde ich nenne es einfach geschickter Einsatz von Verschlüssen.

-- Normal function
function foo(a, b, x)
    return cheaper_expression(expensive_expression(a,b), x);
end
-- foo(a,b,x1);
-- foo(a,b,x2);
-- ...

-- Partial application
function foo(a, b)
    local C = expensive_expression(a,b);
    return function(x)
        return cheaper_expression(C, x);
    end
end
-- local f = foo(a,b);
-- f(x1);
-- f(x2);
-- ...

Auf diese Weise ist es möglich, leicht flexible Funktionen zu erstellen, ohne zu viel Einfluss auf die Programmablauf einen Teil ihrer Arbeit zwischenzuspeichern.

Eine extreme Variante davon sei Currying , aber das ist eigentlich mehr eine Art und Weise funktionale Programmierung als alles zu imitieren sonst .

Hier ist ein umfangreiches ( „reale Welt“) Beispiel mit einigen Code Auslassungen es sonstleicht hier die ganze Seite einnehmen würde (nämlich get_color_values tut wirklich viel Wert Prüfung und erkennt Werte akzeptieren gemischt)

function LinearColorBlender(col_from, col_to)
    local cfr, cfg, cfb, cfa = get_color_values(col_from);
    local ctr, ctg, ctb, cta = get_color_values(col_to);
    local cdr, cdg, cdb, cda = ctr-cfr, ctg-cfg, ctb-cfb, cta-cfa;
    if not cfr or not ctr then
        error("One of given arguments is not a color.");
    end

    return function(pos)
        if type(pos) ~= "number" then
            error("arg1 (pos) must be in range 0..1");
        end
        if pos < 0 then pos = 0; end;
        if pos > 1 then pos = 1; end;
        return cfr + cdr*pos, cfg + cdg*pos, cfb + cdb*pos, cfa + cda*pos;
    end
end
-- Call 
local blender = LinearColorBlender({1,1,1,1},{0,0,0,1});
object:SetColor(blender(0.1));
object:SetColor(blender(0.3));
object:SetColor(blender(0.7));

Sie sehen, dass, wenn der Mixer erstellt wurde, wird nur die Funktion hat einen einzigen Wert Sanity-Check statt bis zu acht Jahren. Ich extrahierte auch die Differenzberechnung, obwohl es wahrscheinlich nicht viel verbessern, ich hoffe, es zeigt, was dieses Muster zu erreichen versucht.

Andere Tipps

Wenn Ihr lua Programm wirklich zu langsam ist, die Lua Profiler und teure Sachen aufzuräumen oder zu C. wandern Aber wenn Sie nicht da und warten sitzen, Ihre Zeit verschwendet wird.

Das erste Gesetz der Optimierung: Do not

.

Ich würde gerne ein Problem sehen, wo Sie die Wahl zwischen ipairs und Paare haben und kann die Wirkung der Differenz messen.

Das ein einfaches Stück niedrig hängende Früchte zu erinnern, in jedem Modul lokale Variablen zu verwenden. Es ist allgemein nicht wert Sachen wie

tun
local strfind = string.find

, wenn Sie eine Messung finden Sie sonst zu sagen.

  • machen die meisten verwendeten Funktionen Einheimischen
  • Eine gute Nutzung von Tabellen HashSets
  • Tieferlegung Tabellenerstellung durch Umnutzung
  • Verwenden luajit!

Halten Sie Tabellen kurz, desto größer die Tabelle je länger die Suchzeit. Und in der gleichen Zeile über Iterieren numerisch indizierte Tabellen (= Arrays) ist schneller als Schlüssel basierte Tabellen (also ipairs ist schneller als Paare)

Es muss auch darauf sein, dass Array mit Feldern von Tabellen ist viel schneller als Tabellen mit jeder Art von Schlüssel. Es passiert (fast) alle Lua-Implementierungen (einschließlich LuaJ) speichert ein als „Array-Teil“ innerhalb von Tabellen, die durch die Tabellen Array-Felder zugegriffen wird, und nicht das Feld Schlüssel nicht speichern, noch Nachschlag für sie;).

Sie können sogar imitieren auch statische Aspekte anderer Sprachen wie struct, C ++ / Java class, etc .. Einheimische und Arrays sind genug.

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