نکات بهینه سازی gas در قراردادهای هوشمند

در این مقاله در مورد چگونگی برنامه نویسی با سالیدیتی جهت کاهش gas مصرفی توسط برنامه بحث می شود. کاهش gas مصرفی توسط قرارداد هوشمند در دو موقعیت اهمیت دارد:

  • هزینه استقرار قرارداد بر روی بلاکچین

  • هزینه فراخوانی توابع قرارداد

یکی از بهترین راه ها برای بهینه سازی gas مصرفی توسط قرارداد، کاهش عملیات های با هزینه بالا در توابع قرارداد هست. ایجاد و تغییر متغیرهای ذخیره سازی(storage variables) می تواند پرهزینه باشد.
به عنوان مثال 20,000 gas وقتی یک مقدار از صفر به غیر صفر تغییر می کند؛ 5,000 gas برای نوشتن در یک حافظه از قبل تعریف شده یا صفر کردن یک مقدار؛ و بازگشت 15,000 gas وقتی یک مقدار غیر صفر به صفر تغییر می کند.

دراینجا می توانید لیست تمام opcode ها و هزینه آن ها برحسب gas را مشاهده کنید.

قوانین Short Circuit

عملگرهای || و && از قوانین اتصال کوتاه پیروی می کنند. به این معنا که در عبارت f || g، اگر عبارت f در حالت true باشد، عبارت g ارزیابی نخواهد شد؛ حتی اگر اثرات جانبی داشته باشد. حال با توجه به قانون ذکر شده؛ برای کاهش gas چه تغییراتی در توابع زیر می بایست رخ دهد؟

function shortCircuit() public view returns(bool){
    if (oftenFalse || oftenTrue) {
        return true;
    }
}

function shortCircuit2() public view returns(bool){
    if(oftenTrue && oftenFalse) {
        return false;
    } else {
        return true;
    }
}

عملیات های پرهزینه در یک حلقه

تغییر مقادیر متغیرهای حافظه در یک حلقه می تواند بسیار پرهزینه باشد؛ بنابراین تاحد ممکن مگر در مواقع ضروری می بایست از آن اجتناب کرد.
مثال زیر یک تابع غیر بهینه است با توجه به اینکه نتغیر loops یک متغیر حافظه است. چه طور می توان این تابع را به صورت بهینه نوشت؟

function looping (uint x) public returns (bool) {
    for(uint i; i < x; i++){
        loops += 1;
    }
    return true;
}

کاهش تعداد حلقه ها

حالت ایده آل عدم استفاده از حلقه است. ولی گاهی اوقات مجبور به استفاده از حلقه هستیم. از آنجایی که حلقه ها هزینه gas بالایی دارند؛ آیا می توان تعداد حلقه ها را در توابع کاهش داد؟

function looping2 (uint x) public pure returns(bool){
    uint m = 0;
    uint v = 0;
    for(uint i = 0; i < x; i++){
        m += i;
    }
    for(uint j = 0; j < x; j++){
        v -= j;
    }
    return true;
}

آرایه هایی از بایت با طول ثابت

در سالیدیتی امکان استفاده از آرایه های بایتی به صورت []byte وجود دارد، ولی فضای زیادی از حافظه را حدر می دهد(31 بایت برای هز عنصر از آرایه). بنابراین بهتر است از نوع داده bytes
استفاده شود. به عنوان یک قانون بهتر است از bytes برای داده های از جنس بایت با طول دلخواه استفاده شود، و از string برای داده های رشته ای(UTF-8) با طول دلخواه استفاده شود.
در صورتی که می توانید داده خود را به تعداد مشخصی بایت محدود کنید؛ همیشه از یکی از نوع های bytes1,…bytes32 استفاده کنید. به این دلیل که هزینه بسیاز کمتری دارند.
با توجه به مظالب گفته شده، تابع زیر چگونه می تواند بهینه شود؟

function byteArray() public returns(uint){
    byte[] byteArray;
    return gasleft();
}

لینک های مرتبط

6 پسندیده