Deep EVM #7: الحلقات والشروط الفعالة من حيث الغاز في Yul
Engineering Team
تدفق التحكم في Yul
Yul يوفر ثلاث بنى للتحكم في التدفق: if، switch، وfor. على عكس Solidity، لا يوجد else مباشر — تستخدم switch بدلاً من ذلك.
الحلقات في Yul
بنية for الأساسية
assembly {
for { let i := 0 } lt(i, 10) { i := add(i, 1) } {
// جسم الحلقة
}
}
يتم ترجمة هذا إلى أكواد تشغيل:
- تهيئة
i := 0 - فحص الشرط
lt(i, 10)-> JUMPI - جسم الحلقة
- التحديث
i := add(i, 1) - JUMP للعودة إلى الفحص
كل تكرار يكلف: JUMP (8 غاز) + JUMPI (10 غاز) + LT (3 غاز) + ADD (3 غاز) = ~24 غاز كحد أدنى.
تحسين: العد التنازلي
assembly {
// العد التنازلي أرخص بغاز واحد لكل تكرار
for { let i := 10 } gt(i, 0) { i := sub(i, 1) } {
// استخدم sub(i, 1) للفهرس المعتمد على الصفر
}
}
لماذا أرخص؟ iszero(i) (3 غاز) مقابل lt(i, n) حيث n يحتاج PUSH إضافي.
فك الحلقات
إذا كان عدد التكرارات معروفاً في وقت الترجمة، يمكن فك الحلقة:
assembly {
// بدلاً من حلقة 4 تكرارات (96+ غاز للتحكم)
// فك يدوياً (0 غاز للتحكم):
let sum := calldataload(0x04)
sum := add(sum, calldataload(0x24))
sum := add(sum, calldataload(0x44))
sum := add(sum, calldataload(0x64))
}
التوفير: لا JUMP ولا JUMPI = توفير ~24 غاز لكل تكرار مُزال.
جداول Switch
switch في Yul هو أكثر كفاءة من سلاسل if/else المتعددة:
assembly {
switch shr(224, calldataload(0))
case 0x6d4ce63c /* get() */ {
mstore(0, sload(0))
return(0, 32)
}
case 0x60fe47b1 /* set(uint256) */ {
sstore(0, calldataload(4))
stop()
}
default {
revert(0, 0)
}
}
أنماط الشرط
تجنب الفروع عند الإمكان
العمليات الحسابية أحياناً تحل محل الشروط:
assembly {
// بدلاً من:
// if gt(a, b) { result := sub(a, b) }
// else { result := sub(b, a) }
// القيمة المطلقة بدون فروع:
let diff := sub(a, b)
let mask := sar(255, diff) // 0x00..00 أو 0xff..ff
result := xor(add(diff, mask), mask)
}
الحد الأقصى والأدنى بدون فروع
assembly {
// max(a, b) بدون فرع
result := xor(a, mul(xor(a, b), gt(b, a)))
// min(a, b) بدون فرع
result := xor(a, mul(xor(a, b), lt(b, a)))
}
تحسين حلقات التكرار على المصفوفات
assembly {
let len := sload(arraySlot)
mstore(0x00, arraySlot)
let dataSlot := keccak256(0x00, 0x20)
let sum := 0
for { let i := 0 } lt(i, len) { i := add(i, 1) } {
sum := add(sum, sload(add(dataSlot, i)))
}
}
مقارنة الغاز: Solidity مقابل Yul
| النمط | Solidity | Yul |
|---|---|---|
| حلقة 10 تكرارات | ~400 غاز | ~280 غاز |
| if/else متداخل | ~50 غاز | ~35 غاز |
| switch 4 حالات | ~80 غاز | ~50 غاز |
| جمع مصفوفة | ~600 غاز | ~400 غاز |
التوفير يتراكم بشكل كبير في الحلقات والعمليات المتكررة.
الخلاصة
الحلقات والشروط في Yul تمنحك تحكماً دقيقاً في تكلفة الغاز. العد التنازلي، فك الحلقات، جداول switch، والعمليات بدون فروع هي أدوات أساسية في ترسانة مطور EVM المتقدم. في المقالة التالية سنطبق هذه المفاهيم لبناء مبادل رموز كامل في Yul خالص.