Solidity-Inline-Assembler-Code verstehen

Hier ist ein Code aus der Solidity-Dokumentation:

 function sumAsm(uint[] _data) public returns (uint o_sum) {
        for (uint i = 0; i < _data.length; ++i) {
            assembly {
                o_sum := add(o_sum, mload(add(add(_data, 0x20), mul(i, 0x20))))
            }
        }
    }

Kann jemand diesen Code erklären. _data ist ein Array von uint. Wie können wir 0x20 zu einem Array hinzufügen? Wenn der Array-Name die Adresse des ersten Elements darstellt, warum versetzen wir es? Wie gibt diese Zeile (add(add(_data, 0x20), mul(i, 0x20)))die Adresse des ersten und der nachfolgenden Elemente des Arrays an.

Antworten (3)

Innerhalb des Assembler-Codes _dataist die Speicheradresse der Anfang der Array-Daten.

Das erste Speicherwort (32 Bytes = 0x20 Bytes) ist jedoch für die Länge des Arrays reserviert, also müssen wir darüber hinweggehen. Somit _data[0]befindet sich an der Speicheradresse _data + 0x20. Im Code sieht das so aus add(_data, 0x20).

Die Array-Elemente folgen fortlaufend, beginnend bei 0, und belegen jeweils ein 32-Byte-Wort (0x20-Byte). Daher ist Element [N] N * 0x20 vom Anfang der Array-Daten versetzt, die wir oben gefunden haben. Im Code ist dieser Offset mul(i, 0x20).

Alles zusammengenommen _data[i]findet man bei (_data + 0x20) + (i * 0x20), was der Ausdruck ist (add(add(_data, 0x20), mul(i, 0x20))).

Der Nettoeffekt der Schleife besteht darin, alle Array-Elemente zu addieren o_sum. Dies wird wahrscheinlich in der Assemblierung durchgeführt, um den Overhead der Überprüfung von Array-Grenzen zu vermeiden, die Solidity immer einfügt.

Die Antwort von @benjaminion ist fantastisch, aber ich habe seinen Assembler-Code verständlicher gemacht:

assembly {
    let ithpos := mul(add(i, 0x01), 0x20)
    let ithnumber := mload(add(_data, ithpos))
    o_sum := add(o_sum, ithnumber)
}

Was ich getan habe:

  1. Anstatt 0x20 zum Speicherort von hinzuzufügen, erhöhe _dataich einfach ium add(i, 0x01). Dies wird in die Multiplikatorfunktion geleitet, muldie den richtigen Wortindex erhält (denken Sie daran, dass Assembly nur mit 32-Byte-Variablen funktioniert).
  2. Laden Sie die Nummer an der ithPosition aus dem Speicher
  3. Fügen Sie schließlich das zu hinzu ithnumber, o_sumindem Sie eine einfache Addition ausführen

Beachten Sie auch, wenn Sie begonnen haben, solidity ^0.5.0 zu verwenden, dass Ihr _dataParameter jetzt wie folgt definiert werden muss:

function sumAsm(uint[] memory _data) public returns (uint o_sum) {
    ...
}

Es gibt ein zusätzliches memorySchlüsselwort zwischen dem Typ und dem Namen.

für pauls antwort:

o_sum := add(o_sum, mload(add(_data, mul(add(i, 0x01), 0x20))))ist tatsächlich billiger zu verwenden, da es keinen Speicher für 2 Variablen zuweist und einen Wert für beide speichert. es muss auch kein mload für jede dieser Variablen verwendet werden

Hallo, danke dafür, aber es kann sich stattdessen um einen Kommentar zur zugehörigen Antwort handeln, da dies selbst nicht die Antwort auf die ursprüngliche Frage ist. Vielen Dank.
Ich kann noch keinen Kommentar hinzufügen, da ich keine 50 Wiederholungen habe, aber da es bei der Montage um Effizienz geht, dachte ich, dass es immer noch relevant ist. Ich denke, es hängt mit beiden Antworten zusammen, da die Solidity-Dokumente auch alles in einer Zeile enthalten. Ich glaube, ich habe Benzinkosten von 41 für Paul und 29 für den 1-Zylinder gezählt. =) kleine Dinge, können aber eine große Verbesserung bewirken, wenn es in einer Schleife ist