Ich habe einen einfachen Vertrag, der das letzte Element des Arrays löscht:
pragma solidity^0.4.11;
contract GasRefundTest {
uint[] myArray = [1, 2];
function deleteLastElem() public returns(bytes32) {
myArray.length--;
}
}
Die Transaktionskosten für den Anruf deleteLastElem()
betragen 17182 Gas.
Wenn ich es ändere zu:
pragma solidity^0.4.11;
contract GasRefundTest {
uint[] myArray = [1, 2];
function deleteLastElem() public returns(bytes32) {
delete myArray[1];
myArray.length--;
}
}
die Transaktionskosten betragen 22480 Gas.
Ich dachte, das Löschen von Speicherplätzen sollte zu einer Gasrückerstattung führen, stattdessen sehe ich einen Gasanstieg.
Kann jemand erklären, was hier los ist.
Das Reduzieren der Größe eines Arrays mit dynamischer Größe löscht bereits die Elemente, die „entfernt“ wurden.
Die Version Ihres Codes, die zuerst a ausführt, führt delete myArray[1]
also nur einen zusätzlichen Schreibvorgang in den Speicher durch, der ohnehin gleich ausgeführt wird.
Lustige Dinge zum Ausprobieren:
// This doesn't take more gas depending on how big you make the array.
myArray.length = 3; // or 300 or 3000
myArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
...
// This takes more gas the more elements you're removing, because it has to zero
// out more positions in storage.
myArray.length = 9; // vs. 1
BEARBEITEN
Ich sollte darauf hinweisen, dass dieses letzte Beispiel etwas verwirrend ist. Die Gasrückerstattung tritt dort ein, ist aber auf die Hälfte des verbrauchten Benzins begrenzt.
myArray.length
und stattdessen Ihre eigene Variable behalten, die angibt, wie viele Elemente gültig sind. Dann könnten Sie jede gewünschte Logik implementieren, wann Dinge auf Null gesetzt werden sollen.Um es auf hohem Niveau zu erklären, Sie führen 2 Operationen statt 1 durch, daher ist mehr Gas erforderlich. Der gesamte von Ihnen geschriebene Code wird in Low-Level-Befehle der Ethereum Virtual Machine (EVM) kompiliert, die dann von ihr interpretiert werden. Für jeden dieser Befehle ist ein bestimmter Gaspreis definiert, siehe hier .
Im zweiten Fall verwenden Sie jetzt delete
. Aus den Dokumenten ,
a löschen weist a den Anfangswert für den Typ zu
Es ist wichtig zu beachten, dass sich das Löschen von a wirklich wie eine Zuweisung zu a verhält, dh es speichert ein neues Objekt in a.
Sie setzen also 0
zuerst dorthin, bevor Sie die Länge eines Arrays verringern. Aus reinem Interesse können Sie versuchen, myArray[1] = 0;
stattdessen zu verwenden und zu sehen, wie sich dies auf das verwendete Gas auswirkt.
foo = 6; foo = 0;
ist "2 Arbeitsgänge statt 1", verbraucht aber weniger Gas als nur foo = 6;
)myArray.length--
Es ist die erste Zeile meiner Antwort: „Das Reduzieren der Größe eines Arrays mit dynamischer Größe löscht delete myArray[myArray.length - 1];
bereits
die Elemente, die ‚entfernt‘ wurden delete
bringt dir nichts.foo = 6; foo = 0;
Beispiel davon ausgegangen, dass foo
dies nicht bereits der Fall war 0
.foo = 6
. Mein Punkt war nur, dass "2 Operationen teurer als 1" aufgrund von Gasrückerstattungen nicht korrekt sind. Vielleicht wäre ein deutlicheres Beispiel, foo = 6; bar = 0;
weniger Gas zu verbrauchen als nur foo = 6
(wiederum, wo bar
zuvor nicht Null war).gas_consumed = max(21000, gas_consumed - min(gas_consumed / 2, gas_refund))
.x = 5; x = 0;
in einer Schleife würde 20.000 (Kosten für das Schreiben einer Nicht-Null, wo zuvor eine Null war) + 5.000 (Kosten für das Schreiben einer Null) an Gas kosten und würde 15.000 zur Gasrückerstattung hinzufügen. Damit wäre kein Gewinn zu erzielen.delete
würde die Position nicht mehr unbedingt auf 0 gesetzt werden, sondern auf den Anfangswert.
Roman Frolov
medwedew1088