Wie skalieren die Kosten für EVM-Speicher?

Wie skalieren die Kosten für EVM-Speicher?

Ich frage mich, wie die Kosten für EVM-Speicher (nicht Speicher) steigen?

Tatsächlich sind die Speicherkosten nach dem Lesen anderer QAs keine lineare Funktion, sondern sollten schnell steigen, wenn mehr davon zugewiesen wird?

Nachdem ich den Floyd-Warshall-Algorithmus in Solidity geschrieben habe, der eine (nxn) Matrix von uints zuweist, sehe ich, dass der Gasverbrauch extrem schnell ansteigt. Dies ist sogar für n = 24 der Fall, wo der Gasverbrauch etwa 19085414 beträgt.

pragma solidity ^0.4.11;

contract APSPBenchmark is usingOraclize {

    event OraclizeEvent0(string desc);        
    event OraclizeEvent1(string desc, int[] apsp);

    int constant INF = -1;

    function APSPBenchmark() public payable {}    

    /*
    * Local all-pairs shortest path
    */
    function localAPSP(int[] w) public {

        int[] memory apsp  = allPairsShortestPath(w);

        OraclizeEvent0("local");
        //OraclizeEvent1("local", apsp); // Infinity encoded as -1
    }

    /*
    * All-pairs shortest path
    */
    function allPairsShortestPath(int[] w) private constant returns(int[]) {
        uint i; uint j; uint k;
        uint n = babylonian(w.length);
        int[] memory d = new int[](n * n);

        for (i = 0; i < n; ++i) {
            for (j = 0; j < n; ++j) {
                d[i * n +j] = (w[i * n + j] >= 100 ? INF : w[i * n + j]);
            }   
            d[i * n + i] = 0;
        }

        for (k = 0; k < n; ++k) {
            for (i = 0; i < n; ++i) {
                for (j = 0; j < n; ++j) {
                    if (d[i * n + k] == INF || d[k * n + j] == INF) continue;
                    else if (d[i * n + j] == INF) d[i * n + j] = d[i * n + k] + d[k * n + j];
                    else d[i * n + j] = min(d[i * n +j], d[i * n + k] + d[k * n + j]);
                }
            }
        }

        return d;
    }

    /*
    * Minimum of two values
    */
    function min(int a, int b) private constant returns(int)  {
        return a < b ? a : b;
    }    

    /*
    * Babylonian sqrt 
    */
    function babylonian(uint n) private constant returns(uint) {
        uint x = n;
        uint y = 1;
        while (x > y) {
            x = (x + y) / 2;
            y = n / x;
        }
        return x;
    }
}
Versuchen Sie, INFmit einer Konstante zu definieren: int constant INF = -1;Ein Problem ist, dass der obige Code gespeichert wird INF. Jedes Mal, wenn Sie es im Körper verwenden, kostet das Auslesen aus dem Speicher 200 Gas. Durch die Deklaration wird dies constantvermieden.
Behebt das den enormen Speicherverbrauch? Ich kann es jetzt nicht versuchen.
Laut meiner Antwort ist das Gedächtnis nicht Ihr Problem. Wenn Sie auf konstant wechseln INF, sparen Sie bis zu 600 Gas in Ihrem inneren Kreislauf (3 Ablesungen von INF). Ihre innere Schleife ist n ^ 3, also sparen Sie bei n = 24 bis zu 600 * 24 ^ 3 = 8,3 Millionen Gas. Das sollte helfen.
Vielen Dank! Wenn man jedoch 8 Millionen Gas von 19085414 (~ 19 Millionen) abzieht, sind es ~ 11 Millionen Gas. Ist das nicht viel zu viel?
Oder sind es die vielen Rechenoperationen und Speichersuchen, die zu einem so hohen Spritverbrauch führen?
Ich schlage vor, dass Sie zuerst das oben genannte versuchen und dann schauen, was übrig bleibt. Das ist nicht Ihr ganzer Vertrag, also weiß ich nicht, was sonst noch passiert. Es gibt einige Handoptimierungen, die Sie in der inneren Schleife vornehmen können, und Sie können den Solidity-Optimierer ausprobieren – er ist jetzt ziemlich gut – aber eins nach dem anderen. Wenn Sie keine Fortschritte machen, stellen Sie eine neue Frage im Sinne von „Wie optimiere ich diesen Code“ und posten Sie den gesamten Vertrag. Die Frage, wie Sie sie gestellt haben, ist beantwortet, damit Sie wissen, was zu tun ist ;-)
Habe es jetzt getestet. Mit Ihrer Optimierung betragen die angenommenen Kosten 10795214 (~10 Millionen). Zwar könnte Code immer optimiert werden, aber ich sehe hier keine „sehr schlechte“ Codierung. Ich habe den gesamten Vertrag beigefügt.
Und mit der Optimierung betragen die Kosten 10173951 (~ 10 Millionen) - danke, akzeptierte Ihre Antwort.

Antworten (2)

Die Gaskosten der Speichererweiterung sind im Yellow Paper Anhang H wie folgt definiert:

Speicherkosten-Formel

G_memory ist 3 und aist die Anzahl der zugewiesenen 256-Bit-Worte ( ints).

Für a=24^2 ergibt das 3 * 576 + 648 = 2.376. Es sieht also so aus, als ob die Speicherkosten nicht Ihr Hauptproblem sind.

Das obige beantwortet Ihre Frage. Ich bin jedoch auch gespannt, wohin das Benzin geht, also werde ich noch etwas weiter graben und in den Kommentaren posten, wenn ich einen Einblick habe.
Das von Ihnen eingefügte Bild wird @benjaminion nicht angezeigt
@Avatar - Bild scheint im Allgemeinen in Ordnung zu sein; kann ein lokales Problem sein. Wie auch immer, es ist nur Gleichung 222 aus dem Yellow Paper .
Beachten Sie, dass die Speicherkosten nicht die "Erweiterungskosten" sind. Siehe hier ethereum.stackexchange.com/a/62732/44907 für die Unterscheidung

Die Formel in dieser Antwort sind eigentlich nicht die Erweiterungskosten , sondern die Speicherkosten.

Die Erweiterungskosten sind die zusätzlichen Speicherkosten für die zuvor unberührten Speicherwörter. Siehe Solidity-Dokumentation und Zitat unten aus Anhang H im gelben Papier .

Beachten Sie auch, dass Cmem die Speicherkostenfunktion ist (die Erweiterungsfunktion ist die Differenz zwischen den Kosten davor und danach). Es handelt sich um ein Polynom, bei dem der Koeffizient höherer Ordnung geteilt und geglättet wird, und daher linear bis zu 724 B des verwendeten Speichers, danach kostet es wesentlich mehr.

(Nicht genug Repräsentant zum Kommentieren)