Zum Hauptinhalt springen
BlockchainMar 28, 2026

Deep EVM #7: Gaseffiziente Schleifen und Verzweigungen in Yul

OS
Open Soft Team

Engineering Team

Schleifen in der EVM sind teuer

Jede Iteration einer Schleife in der EVM kostet Gas fuer JUMPI (10 Gas), den Vergleich (3 Gas), die Inkrementierung (3+3 Gas fuer ADD+MSTORE/DUP) und die eigentliche Arbeit. Bei Tausenden von Iterationen summiert sich das schnell.

In Solidity generiert der Compiler relativ effizienten Schleifencode, aber er kann die Semantik Ihrer Schleife nicht aendern. In Yul haben Sie die volle Kontrolle.

Grundlegende Schleifenoptimierung

Rueckwaerts zaehlen statt vorwaerts

// Vorwaerts (standard):
for { let i := 0 } lt(i, n) { i := add(i, 1) } {
    // LT-Vergleich: 3 Gas pro Iteration
}

// Rueckwaerts (optimiert):
for { let i := n } i { i := sub(i, 1) } {
    // ISZERO implizit im Bedingungstest: spart 1 Opcode
}

Warum ist Rueckwaertszaehlen schneller? Der Bedingungstest i ist aequivalent zu iszero(iszero(i)), was der EVM-Compiler zu einem einfachen Nulltest optimiert. lt(i, n) erfordert einen expliziten LT-Opcode.

Loop-Unrolling

// Statt 4 Iterationen:
for { let i := 0 } lt(i, 4) { i := add(i, 1) } {
    sstore(add(baseSlot, i), 0)
}

// Entrollt (spart JUMP/JUMPI-Overhead):
sstore(baseSlot, 0)
sstore(add(baseSlot, 1), 0)
sstore(add(baseSlot, 2), 0)
sstore(add(baseSlot, 3), 0)

Bei kleinen, bekannten Iterationszahlen spart Unrolling die JUMP/JUMPI-Kosten (10 Gas pro Iteration) und den Schleifenoverhead.

Verzweigungen optimieren

Switch statt if-else-Ketten

// Schlecht: if-else-Kette (O(n) Vergleiche)
if eq(selector, 0xa9059cbb) { /* transfer */ }
if eq(selector, 0x70a08231) { /* balanceOf */ }
if eq(selector, 0x095ea7b3) { /* approve */ }

// Besser: switch-Anweisung
switch selector
case 0xa9059cbb { /* transfer */ }
case 0x70a08231 { /* balanceOf */ }
case 0x095ea7b3 { /* approve */ }
default { revert(0, 0) }

Der Yul-Compiler optimiert switch-Anweisungen besser als verschachtelte if-Anweisungen, obwohl auf EVM-Ebene beides zu Vergleichen und JUMPIs kompiliert.

Batch-Verarbeitung

Mehrere Storage-Writes buendeln

// Statt einzelner Schreibvorgaenge:
function batchWrite(slot, values_ptr, count) {
    for { let i := 0 } lt(i, count) { i := add(i, 1) } {
        let value := mload(add(values_ptr, mul(i, 32)))
        sstore(add(slot, i), value)
    }
}

Parallele Vergleiche mit Bitmasks

// Pruefe mehrere Bedingungen gleichzeitig:
let flags := or(
    mul(eq(a, target_a), 1),   // Bit 0
    mul(eq(b, target_b), 2)    // Bit 1
)
if eq(flags, 3) {
    // Beide Bedingungen erfuellt
}

Gasvergleich: Solidity vs. Yul

Ein konkretes Beispiel — Summierung eines Arrays:

// Solidity: ~250 Gas fuer 10 Elemente
function sumSolidity(uint256[] calldata arr) external pure returns (uint256 total) {
    for (uint256 i = 0; i < arr.length; i++) {
        total += arr[i];
    }
}
// Yul: ~180 Gas fuer 10 Elemente
function sumYul(uint256[] calldata arr) external pure returns (uint256 total) {
    assembly {
        let len := arr.length
        let ptr := arr.offset
        for { let i := 0 } lt(i, len) { i := add(i, 1) } {
            total := add(total, calldataload(add(ptr, mul(i, 32))))
        }
    }
}

Die Yul-Version spart ~30% Gas, hauptsaechlich durch Vermeidung von Soliditys Bounds-Checking und Stack-Management-Overhead.

Fortgeschrittene Techniken

Branchless Minimum/Maximum

// Minimum ohne Verzweigung (spart JUMPI):
// min(a, b) = b ^ ((a ^ b) & -(a < b))
function branchlessMin(a, b) -> result {
    let diff := xor(a, b)
    let mask := sub(0, lt(a, b))  // 0xfff...fff wenn a < b, sonst 0
    result := xor(b, and(diff, mask))
}

Effiziente Modulo-Operationen fuer Zweierpotenzen

// x % 8 (teuer):
let r := mod(x, 8)  // 5 Gas

// x & 7 (guenstig):
let r := and(x, 7)  // 3 Gas

Fazit

Gaseffiziente Schleifen und Verzweigungen in Yul erfordern ein Verstaendnis der zugrunde liegenden Opcode-Kosten. Rueckwaerts zaehlen, Loop-Unrolling, Batch-Verarbeitung und Branchless-Techniken koennen den Gasverbrauch erheblich reduzieren. Im naechsten und letzten Artikel dieser Serie werden wir einen vollstaendigen Token-Swap in reinem Yul implementieren und alle bisher gelernten Techniken anwenden.