بهینه‌سازی (۵) – Cache کردن اشیا

شروع موضوع توسط yaa110 ‏25 جولای 2013 در انجمن برنامه فلش Flash

کلمات کلیدی:
  1. yaa110

    yaa110 کاربر فعال

    ارسال‌ها:
    206
    تشکر شده:
    507
    امتیاز دستاورد:
    16
    یکی از روش های ساده و کارامد برای استفاده بهینه از حافظه سیستم و جلوگیری از هدر رفتن آن، استفاده دوباره از اشیای پیش ساخته و بعبارتی cache کردن اشیا و استفاده مجدد از آنها به جای ایجاد چندباره آنهاست. به دو مثال زیر دقت کنید، در هر دو مثال تعداد زیادی شی از یک کلاس دلخواه، پشت سر هم و برای دفعات طولانی ساخته و به stage اضافه می شود و سپس کل اشیا حذف می شود و در ادامه مجددا اشیا دوباره به stage اضافه می شوند.
    مثال اول: بهینه نشده

    [درصورتیکه کدها به هم ریخته است، به سایت منبع مراجعه کنید.]
    کد (Text):
    [FONT=Courier 10 Pitch]import flash.system.System;[/FONT]
    for (var i:int = 0; i < 313; i++) {    this.removeChildren();    for (var j:int = 0; j < 100; j++) {        var myObject:myCostumObjectClass = new myCostumObjectClass();        addChild(myObject);    } [FONT=Courier 10 Pitch]}[/FONT]
    مثال دوم: نسخه بهینه شده مثال اول

    [درصورتیکه کدها به هم ریخته است، به سایت منبع مراجعه کنید.]
    کد (Text):
    [COLOR=#222222][FONT=Tahoma]import flash.system.System;[/FONT][/COLOR]

    var chachedObjects:Array = [];


    for (var i:int = 0; i < 313; i++) {
        this.removeChildren();
        for (var j:int = 0; j < 100; j++) {
            if (chachedObjects[j] is myCostumObjectClass) {
                addChild(chachedObjects[j]);
            } else {
                var myObject:myCostumObjectClass = new myCostumObjectClass();
                chachedObjects.push(myObject);
            }
        }
    [COLOR=#222222][FONT=Tahoma]}[/FONT][/COLOR]
    در مثال اول در هر مرحله بعد از حذف تمامی اشیا با دستور removeChildren مجددا اشیای جدید ساخته می شوند و به stage اضافه می شوند. اما در مثال دوم (بهینه شده) تنها در مرحله اول اشیا ساخته می شوند که بعد از ساخته شدن در یک آرایه قرار می گیرند، بنابراین در مراحل بعدی بعد از حذف تمامی اشیا از stage، اشیای جدید مجددا ساخته نمی شوند و از همان اشیای قبلی که در آرایه cache شده اند، استفاده می شود (یعنی اشیای cache شده به stage اضافه می شوند). خروجی هر دو مثال کاملا با هم مشابه است، اما تفاوت زیادی در استفاده از مموری در این دو مثال وجود دارد که این تفاوت در شکل1 دیده می شود.

    [​IMG]شکل1- مقدار حافظه مصرف شده در هر مرحله بعد از حذف کامل اشیا و اضافه کردن مجدد آنها (خط قرمز: منحنی مثال اول و خط سیاه: مثال بهینه شده).

    همانطور که در شکل1 مشخص است، مقدار مموری مصرف شده در تمامی مراحل در مثال بهینه شده، تقریبا ثابت مانده است (خط سیاه)، درحالیکه در مثال اول (بهینه نشده) افزایش شدیدی در طی مراحل مختلف دیده می شود که منجر به نوسانی شدن منحنی آن شده است (خط قرمز). علت کاهش مقدار منحنی مصرف شده بعد از افزایش شدید آن در مثال اول به زمان اجرای Garbage Collection در Flash Player و Air Debugger مربوط می شود. لازم به ذکر است که میزان حافظه مصرف شده در هر مرحله توسط کد زیر ثبت شده است:
    کد (Text):
    trace(System.totalMemoryNumber);

    • آشنایی با Garbage Collection
    عملیات Garbage Collection یا به اختصار GC عملیاتی است که به صورت خودکار و در پشت صحنه و در زمان های نامشخصی برای آزادسازی بلوک هایی از حافظه که توسط اشیای بدون مصرف (غیرفعال) اشغال شده اند، اجرا می شود. منظور از اشیای بی مصرف، اشیایی هستند که در یک مدت زمانی معین توسط اشیای فعال استفاده نمی شوند (مانند اشیای حذف شده در هر مرحله از مثال های بالا). البته علمی تر این است بگوییم اشیای غیرفعال اشیایی هستند که با هیچ مرجعی به root متصل نمی شوند (در ادامه توضیحات بیشتری ارائه می شود). لازم به ذکر است که متغیرهای Primitive (شامل int, uint, String, Number, Boolean) درحالت عادی در بین اشیای بی مصرف (غیرفعال) قرار نمی گیرند. برای درک مفهوم عملیات GC توجه به این نکته الزامی است وقتی که یک شی ساخته و نامگذاری می شود، این نام مرجعی برای آن شی است که می توان به کمک همین نام مقدار شی مفروض را فراخوانی کرد. بنابراین زمانی که یک شی را حذف می کنیم، در واقع مرجع آن شی را حذف کرده ایم نه خود آن شی را (یعنی آن شی هنوز در حافظه وجود دارد و تنها نام (مرجع) آن حذف شده است). به مثال زیر دقت کنید:

    [درصورتیکه کدها به هم ریخته است، به سایت منبع مراجعه کنید.]

    کد (Text):
    [FONT=Courier 10 Pitch]var a:Object = {_propert:"value"};[/FONT]
    var b:Object = a;a = null; [FONT=Courier 10 Pitch]trace(b._propert); // Output: value[/FONT]
    همانطور که در این مثال مشخص است، حتی بعد از این که a تبدیل به null می شود، اما هنوز هم b به شی اصلی متصل است و می تواند مقدار خواسته شده را ارائه کند. درواقع در این مثال a و b هر دو مراجعی هستند که به شی ساخته شده از نوع Object متصل هستند. پس تا زمانی که b وجود دارد، شی ساخته از Object، بی مصرف (غیرفعال) نخواهد بود. برای شناسایی اشیای غیرفعال، از دو روش توسط Flash Player و Air Debugger استفاده می شود. در روش اول اشیایی که تعداد مراجع ان صفر باشد، به صورت غیرفعال شناخته می شود و بنابراین توسط عملیات GC از حافظه حذف خواهد شد. طبق این روش شی ساخته شده از Object در مثال قبل تنها زمانی از حافظه حذف می شود که مرجع b نیز null شده باشد. این روش را اصطلاحا Reference Counting می نامند. ویژگی روش Reference Counting این است که به سادگی انجام می شود و بنابراین CPU به مقدار کمی اشغال خواهد شد. اما عیب اصلی آن زمانی رخ می دهد که هر کدام از اشیا در مرجع دیگری نیز باشد (cross-reference). به مثال زیر توجه کنید:

    [درصورتیکه کدها به هم ریخته است، به سایت منبع مراجعه کنید.]

    کد (Text):
    [FONT=Courier 10 Pitch]var a:Object = {};[/FONT]
    var b:Object = {_property : a};a._property = b;a = null; [FONT=Courier 10 Pitch]b = null;[/FONT]
    در این مثال، GC با روش Reference Counting نمی تواند اشیای ساخته شده را از حافظه پاک کند. بنابراین در این حالت برای آزادسازی حافظه به روش دیگری نیاز است که از Flash Player 8 به بعد در دسترس است. این روش دوم را اصطلاحا Mark Sweeping می نامند. در این روش Flash Player از اشیای root شروع می کند و از طریق مراجع در بین اشیا حرکت می کند و به هر شی که رسید آن را علامتگذاری می کند و در هر مرحله همین کار را از طریق اشیای علامتگذاری شده ادامه می دهد. درنهایت Flash Player تمامی اشیایی را که علامتگذاری نشده اند، از حافظه پاک می کند چون در حقیقت هیچ مرجعی نداشته اند. این عملیات به خوبی در شکل2 دیده می شود.
    [​IMG]شکل2- روش Mark Sweeping برای یافتن اشیای بی مصرف (غیرفعال). در این شکل در نهایت اشیای مشخص شده با رنگ سفید حذف خواهند شد (با این که تعداد مراجع آن ها صفر نیست و هرکدام در مرجع دیگری وجود دارد).

    روش Mark Sweeping روش خیلی دقیقی است، اما عیب اصلی آن اشغال زیاد CPU است. در Flash Player 9 به بعد برای کاهش بار زیادی که به CUP تحمیل می شود، این عملیات به جای اینکه در یک لحظه انجام شود در طول چند حلقه فریم انجام می گیرد. توجه به این نکته الزامی است که عملیات GC به صورت لحظه ای انجام نمی شود، یعنی به محض غیرفعال شدن اشیا (با حذف مراجع آن)، عملیات GC اجرا نخواهد شد. این عملیات زمانی اجرا خواهد شد که Flash Player با بررسی حافظه احساس کند که مقدار فضای آزاد در حافظه درحدی است که نیاز است این عملیات اجرا شود. پس بعنوان یک توسعه دهنده باید بپذیرید که راهی برای اطلاع از زمان اجرای عملیات GC ندارید. همچنین باید بدانید که اشیای غیرفعال تا زمانی که در حافظه هستند (حتی اگر مرجعی نداشته باشند)، پردازش می شوند، رویداد enterFrame آنها ارسال می شود، صداهایشان پخش می شوند، عملیاتهای بارگزاری در آنها ادامه می یابند و همچنین تمامی رویدادهایی که برایشان تعریف کرده اید نیز ارسال می شوند. در Flash Player های اخیر کد زیر برای مجبور کردن Flash Player برای اجرای GC اضافه شده است، اما تجربه نشان می دهد که این کد نیز نمی تواند زمان مشخصی را برای اجرای عملیات GC تعیین کند، حتی گاهی موجب می شود که اپلیکیشن از کار بیفتد و به طور ناگهانی بسته شود.

    [درصورتیکه کدها به هم ریخته است، به سایت منبع مراجعه کنید.]

    کد (Text):
    [FONT=Courier 10 Pitch]import flash.system.System;[/FONT]
     [FONT=Courier 10 Pitch]System.gc();[/FONT]
    برای اثبات این موضوع، مثال اول را با اضافه کردن System.gc در حلقه for داخلی تکرار کردیم:
    کد (Text):
    [/FONT][/COLOR]import flash.system.System;
    for (var i:int = 0; i < 313; i++) {
        this.removeChildren();
        for (var j:int = 0; j < 100; j++) {
            var myObject:myCostumObjectClass = new myCostumObjectClass();
            addChild(myObject);
        }
        System.gc();

    }[COLOR=#333333][FONT=Georgia]
    در شکل3 نحوه تغییرات حافظه در این مثال دیده می شود که اثبات کننده بی فایده بودن استفاده از کد System.gc برای اجبار کردن عملیات GC می باشد.
    [​IMG]شکل3- تغییرات حافظه بعد از اضافه کردن کد System.gc به مثال اول​

    بنابراین می توان نتیجه گرفت که به جای اینکه منتظر اجرای GC بمانیم بهتر است طوری کدنویسی کنیم که کمترین اشیای بی مصرف (غیرفعال) حاصل شود تا بتوان از حافظه استفاده بهینه داشت.


    توجه:
    درصورتیکه با مفاهیم اولیه مانند حلقه فریم آشنایی ندارید، بخش اول از مجموعه بهینه سازی را مطالعه کنید.

    منبع: http://flashcenter.ir/fa/1392/05/03/بهینه-سازی-cache-کردن-اشیا/
     
    آخرین ویرایش: ‏25 جولای 2013
    نوشته شده توسط yaa110 در ‏25 جولای 2013
    r.miri19، BehrouzPc، scup و یک نفر دیگر از این ارسال تشکر کرده اند.

به اشتراک بگذارید