اطلاعیه

Collapse
No announcement yet.

برنامه بهینه

Collapse
X
 
  • فیلتر
  • زمان
  • Show
Clear All
new posts

    برنامه بهینه

    سلام دوستان. بنظر شما بین دو شرط زیر فرقی وجود داره؟

    if (!w) {
    i = 0;
    } else {
    ++i;
    }


    // with:
    if (w) {
    ++i;
    } else {
    i = 0;
    }
    جدیدترین ویرایش توسط digi-rx; ۱۰:۵۷ ۱۳۹۷/۱۲/۱۰.
    تاپیک ویدیوهای آموزشی حواشی آردوینو و ESP

    #2
    پاسخ : برنامه بهینه

    خوب. ظاهرا نتیجه هر دو یکسان هست هرچند در کدشون فرق هست. فرقش هم این که در اولین if میاد اول متغیر رو not میکنه و بعد شرط رو مقایسه میکنه.
    اما در عمل بستگی داره که این کد کجای برنامه قرار بگیره. همانطور که دیدیم باوجود یکسان بودن خروجی در اولینif یک عمل not اضافه انجام می شود.
    حال اگر این شرط در توابعی مثل تابع setup یا توابع دیگر که فقط قرار هست یکبار (یا بتعداد کمی) اجرا می شوندفرقی نمیکند که از این نوع if استفاده شود یا if دومی. اما:
    فرض کنید که برای هر عمل not میکرو یک کلاک مصرف کند و ما این شرط not شده رو در تابع loop اصلی برنامه گذاشتیم . با فرض اینکه تابع لوپ برنامه ما در هر ثانیه 1000بار اجرا می شود ( البته در میکروهای سرعت بالاتر مثل stm یا ماژول esp8266 این عدد چندین هزار بار خواهد بود) پس ما براحتی با یک not غیر ضروری بیش از 1000 کلاک در ثانیه از زمان پردازش میکرو را هدر داده ایم !
    تاپیک ویدیوهای آموزشی حواشی آردوینو و ESP

    دیدگاه


      #3
      پاسخ : برنامه بهینه

      حال فرض کنید ما یک سنسور دما داریم که خروجی آنرا تبدیل به یک عدد صحیح int کرده ایم و قصد داریم آنرا به بازه هایی تقسیم کرده و در این بازه ها دستورات مدنظرمون رو اجرا کنیم (مثل خاموش روشن کردن فن) روشی که معمولا میبینم که خیلیها انجام میدن به صورت زیر است:

      if ((temp >= 0) && (temp < 12)) {
      // statment 1
      } else if ((temp >= 12) && (temp < 17)) {
      // statment 2
      } else if ((temp >= 17) && (temp < 23)) {
      // statment 3
      } else if ((temp >= 23) && (temp < 28)) {
      // statment 4
      } else if ((temp >= 28) && (temp < 38)) {
      // statment 5
      }

      بیاید ببینیم میکرو برای پردازش این دستورات چند عمل را باید انجام دهد:

      من اعمال و مقایسه های شرط اول رو فقط در تصویر مشخص کردم درنتیجه باید این تعداد در 5 ضرب شود. همانطور که میبینید تقریبا تعداد 24 عملیات رو باید انجام دهد. (آخرین خط else ندارد)
      آیا راه بهتری هست؟
      تاپیک ویدیوهای آموزشی حواشی آردوینو و ESP

      دیدگاه


        #4
        پاسخ : برنامه بهینه

        نوشته اصلی توسط digi-rx نمایش پست ها
        حال فرض کنید ما یک سنسور دما داریم که خروجی آنرا تبدیل به یک عدد صحیح int کرده ایم و قصد داریم آنرا به بازه هایی تقسیم کرده و در این بازه ها دستورات مدنظرمون رو اجرا کنیم (مثل خاموش روشن کردن فن) روشی که معمولا میبینم که خیلیها انجام میدن به صورت زیر است:

        if ((temp >= 0) && (temp < 12)) {
        // statment 1
        } else if ((temp >= 12) && (temp < 17)) {
        // statment 2
        } else if ((temp >= 17) && (temp < 23)) {
        // statment 3
        } else if ((temp >= 23) && (temp < 28)) {
        // statment 4
        } else if ((temp >= 28) && (temp < 38)) {
        // statment 5
        }

        بیاید ببینیم میکرو برای پردازش این دستورات چند عمل را باید انجام دهد:

        من اعمال و مقایسه های شرط اول رو فقط در تصویر مشخص کردم درنتیجه باید این تعداد در 5 ضرب شود. همانطور که میبینید تقریبا تعداد 24 عملیات رو باید انجام دهد. (آخرین خط else ندارد)
        آیا راه بهتری هست؟
        سلام من هم از همین روش استفاده میکنم ولی حدس میزنم روش سوییچ کیس بهتر باشه حالا دوستانی که با Switch/ Case کار کردن بیان بگن






        امیرحسین ضیا

        دیدگاه


          #5
          پاسخ : برنامه بهینه

          نوشته اصلی توسط azjaguar نمایش پست ها
          سلام من هم از همین روش استفاده میکنم ولی حدس میزنم روش سوییچ کیس بهتر باشه حالا دوستانی که با Switch/ Case کار کردن بیان بگن
          خیلی ممنون آقای مهندس از اینکه تو بحث شرکت می فرمایید. ما هرچقدر بتونیم دستورات و یا توابعی که در برنامه درون حلقه قرار می گیرند (تکرار می شوند) رو کمتر کرده و یا حتی در صورت امکان به خارج از حلقه منتقل کنیم فشار پردازشی میکرو کمتر شده و بجاش می توانیم با همان میکرو کارهای دیگر و بیشتری انجام دهیم.
          (هدف از این تاپیک این هست که هر یک ازدوستان که براشون مقدور باشه تجربیاتشون رو در این زمینه اینجا به اشتراک بگذارند. ما هم یاد می گیریم.)
          هم با if میشه هم با switch. من تو این پست کد بهینه if رو قرار میدم و در پست بعد switch رو.

          if (temp < 38) {
          if (temp >= 28) {
          // statment 5
          } else if (temp >= 23) {
          // statment 4
          } else if (temp >= 17) {
          // statment 3
          } else if (temp >= 12) {
          // statment 2
          } else if (temp >= 0) {
          // statment 1
          }
          }

          بنظرم توی پست قبل تعداد عملیات رو کم شمردم. در هر صورت با مقایسه این کد با کد قبلی مشخصه که حدود 9 یا 10 عملیات در این کد کمتر انجام می شود.
          تاپیک ویدیوهای آموزشی حواشی آردوینو و ESP

          دیدگاه


            #6
            پاسخ : برنامه بهینه

            ;کد زیر کد switch مربوط به همان مثال بالا هست که باید تصحیح کنم که بهینه نمی باشد: (در آردوینو)

            int temp = ...;
            switch (temp) {
            case 0 ... 11:
            // statment 1
            break;
            case 12 ... 16:
            // statment 2
            break;
            case 17 ... 23:
            // statment 3
            break;
            case 23 ... 27:
            // statment 4
            break;
            case 28 ... 37:
            // statment 5
            break;
            }

            در حالتی که احتمال true شدن یکی از case ها بیشتر از سایرین هست باید اون case رو بالاتر از بقیه قرار داد. به این ترتیب مثلا در هزار بار اجرای switch فوق بصورت میانگین زمان کمتری از میکرو مصرف می شود.
            اگر احتمال وقوع بیشتر یک case وجود نداشته باشد بهتر است از همان گد بهینه if استفاده شود.
            البته در حالتهایی که تعداد شرط ها زیاد باشد در حالتی که مقدور باشد بهتر است از ساختارهای درختی استفاده گردد.
            تاپیک ویدیوهای آموزشی حواشی آردوینو و ESP

            دیدگاه


              #7
              پاسخ : برنامه بهینه

              می خواهیم درمورد استفاده از String در برنامه و نتایج استفاده از آن در حافظه صحبت کنیم. این مطلب یمقدار طولانی هست و در چند پست ارسال میشه.
              ابتدا این رو بصورت ستاره دار و ویژه بگم که ما دو جور string داریم که نوعی که معمولا در برنامه هامون ازش استفاده می کنیم در زمان تعریف متغیر با واژه کلیدی String تعریف میشه. این نوع استرینگ یک نوع شیء (Object) هست و جدای از اینکه تعداد کاراکترهای ذخیره شده در آن چقدر باشد ، 20 بایت بیشتر حافظه اشغال می کند. یعنی اگر تنها یک کلمه پنج کاراکتری درآن ذخیره کرده باشیم درواقع 25 بایت فضا از حافظه اشغال میکنه. اما این فقط بخشی از ماجراست.
              در تمام صحبتهای بعدیم لطفا این مورد رو هم (20بایت اضافه) در ذهنتون داشته باشید.
              گفتیم حافظه. RAM در آردوینو (مفاهیمی که در این تا\یک عرض میشه با بیس آردوینو و esp هست اما در بیشتر زبانهای برنامه نویسی صدق میکنه) به قسمتهای مختلفی برای مقاصد مختلف تقسیم میشه. قسمتی که درآن تمام متغیرهای گلوبال و استاتیک ذخیره میشه (BSS). قسمتی دیگر که متغیرهای محلی و توابع در اون ذخیره می شوند (stack) و درآخر قسمتی بنام heap که متغیرهای داینامیک در اون ذخیره می شوند. (یه تعریف و آشنایی تقریبی با حافظه بود)
              قسمتی که در این مبحث نگرانشیم همین حافظه heap هست که درصورت پر شدن میتونه عواقب بدی برامون داشته باشه که با ریست شدن میکروشروع میشه.
              غرض کنید تعدادی کارتن یک اندازه و مکعبی رو به ما دهند تا دریک فضا مثل انبار جا بدیم. مشکلی نیست و براحتی جای می گیرند. اما حالا فرض کنید که این بار کارتنهایی با ابعاد و اشکال مختلف رو دراون محل قرار باشه جا بدیم.مسلما علاوه بر تلاش زیادی که این بار خواهیم کرد تعداد زیادی فضای پرت بین کارتونها خواهد ماند که هیچ کاریش نمیشه کرد. درنتیجه تعداد کارتنهای کمتری در اون فضا جا خواهند گرفت. این دقیقا همان کاری هست که متغیرهای String سر حافظه heap می آورند.
              اصلا بهینه نیست. قبول دارید؟ دلیلش هم این هست که این متغیرها اصلا در زمان تعریف طول ثابت و معیینی ندارند. در حافظه stack طول متغیرها مشخص بوده و متغیرها به ترتیب ذخیره و حذف می شوند. طبعا فضایی هم خالی و پرت نمی ماند. اما در حافظه heap هر زمان که متغیری می خواهد اضافه شود بسته به طول آن اولین فضای خالی که برابر یا بیشتر از طول مورد نیاز متغیر هست متغییر ذخیره میشود. طبعا اگر فضای خالی ای باشد که طولش کمتر از طول مورد نیاز باشد بحال خود رها می شود. حال براحتی متوجه می شوید که اگر تعداد این اضافه و کم شدنها خیلی زیاد باشد با توجه به محدودیت حافظه درمیکرو ، حافظه پر ازفضاهای خالی غیر قابل استفاده خواهد شد و طاهرا پر خواهد شد. که همانطوری که عرض شد نتایج بسیار بدی خواهد داشت.
              جدیدترین ویرایش توسط digi-rx; ۱۳:۱۶ ۱۳۹۸/۰۴/۰۷.
              تاپیک ویدیوهای آموزشی حواشی آردوینو و ESP

              دیدگاه


                #8
                پاسخ : برنامه بهینه

                خوب همونطور که عرض شد مطلب چند پست طول می کشه. امیدوارم که بتونه بکار دوستان بیاد.

                برای ساخت متغیرهای داینامیک باید درخواستی به روتین مموری منیجمنت (memory management) ارسال بشه تا قسمتی از حافظه رو برای این منظور در اختیار بگذاره. طبعا زمانی که کار ما با متغیر تمام شد باید حافظه گرفته شده آزاد بشه تا بتونه دوباره توسط قسمت دیگری از برنامه مورد استفاده قرار بگیره. روتین هایی که این کارها رو برای ما انجام می ذهند عبارتند از malloc() ، free() و realloc() که اولی درخواست بلوک مموری هست و دومی برگردوندن اون. سومی هم میگه "این بلوک مموری خیلی کوچیکه بجاش یک حافظه بیشتری بده"
                زمانیکه شما درخواست مموری می کنید مموری منیجمنت سعی میکنه یک قسمت خالی در حافظه heap پیدا کنه که بتونه برای درخواست مورد نظرمصرف بشه و کافی باشه. اگر شما (برنامه شما) درخواست مقدار حافظه بیشتری از حافظه باقیمانده داشته باشید (realloc) و در قسمتی که الان درنظر گرفته شده برای این منظور (اون قسمت خالی که برای ما پیدا کرده) این اندازه حافظه موجود نباشه ، از اون قسمت عبور میکنه و دوباره شروع میکنه به گشتن برای اختصاص حافظه مورد نیاز به شما (allocation) * اینجا یک مفهومی نهفته هست. یعنی هرچی حافظه باقیمانده کمتر میشه پردازنده درگیرتر میشه *
                حال که با مشکل اختصاص حافظه داینامیک (allocation) آشنا شدیم ، این رو هم بیاد بیاورید که کلاس String حافظه داینامیک زیادی هم مصرف می کنه (20 بایت بیشتر)
                مشکل اصلی و بزرگ زمانی شدیدا خودنمایی می کنه که شما از یسری اوپراتورهای معمول و همیشگی استفاده می کنید و این کار شما سبب میشه تا آبجکت های String های جدیدی ساخته شوند که درواقع شما به اونها نیاز ندارید. طبعا هر کدوم از آنها یک gap در حافظه ایجاد خواهند کرد. (منظورم همان فضاهای خالی هست که دیگر چیزی در آن جا نمی گیره)
                به این مثال دقت کنید:

                String hi = "Hello";
                String name = "Ali";
                String res = hi + " " + name;

                برخلاف تصورتون باید بگم که در این سه خط پنج متغیر تعریف شده !! سه تاش که مشخص هست اما چرا:
                درخط سوم ابتدا متغیر سوم یعنی res ساخته شده و داده های درون متغیر hi داخلش ریخته میشه (مقدار دهی میشه)
                سپس یک متغیر موقت ساخته شده و تعداد بیشتری کاراکتر یعنی کاراکترهای داخل متغیر hi به اضافه یک کاراکتر فاصله درون متغیر جدید ریخته میشه. و آدرس متغیر جدید در پوینتر مربوط به متغیر res ریخته میشه (این یعنی متغیر قبلی بدون حذف شدن و رهاسازی حافظه بحال خود رها می شود.)
                حال یک متغیر موقت دیگر ساخته شده و دوباره مرحله بالا تکرار می شود.
                چند نکته رو دوباره یادآوری می کنم که ما برای ریختن عبارت Hello Ali درون یک متغیر درمجموع 5 متغیر ساخیتم که هرکدام 20 بایت بیشتر فضا اشغال می کند و درضمن تعداد دو متغیر رها شده داریم که درونشان (بجز اون 20 بایت) در مجموع 11 کاراکتر ذخیره شده. (5+6)
                حالا جمع می زنیم:
                (5*20)+11= 111 byte
                111 بایت بیشتر !!
                همونطور که می بینید فاجعه هست. وای بحال اینکه این کدها درون حلقه یا بدتر از اون حلقه اصلی برنامه باشد و هر بار تکرار شود. (تازه ما اینجا از تک کلمه استفاده کردیم)
                جدیدترین ویرایش توسط digi-rx; ۲۱:۵۱ ۱۳۹۸/۰۴/۱۰.
                تاپیک ویدیوهای آموزشی حواشی آردوینو و ESP

                دیدگاه


                  #9
                  پاسخ : برنامه بهینه

                  چند نکته هست که بهتره مد نظر قرار بگیره مخصوصا زمانی که داریم در مورد میکروکنترلرها صحبت میکنیم :

                  1) میزان بهینه بودن یا نبودن، تعداد کلاک ها و غیره فقط از روی اسمبلی لیست تولید شده (فایل با پسوند .ls یا .lst ) ملاکه نه از روی کد C
                  ملاکی که شما قرار دادین، برای میکروکنترلرهای مختلف، کامپایلرهای مختلف برای یک میکروی خاص، و حتا حالت های مختلف اپتیمایزنش برای یه کامپایلر خاص برای یه میکروی خاص متفاوته. ینی با همین کد، میشه چندین حالت مختلف داشت. یه کد که بنظر شما 10 کلاک زمان بخواد، در واقعیت با حالت های مختلف میتونه بین "مثلا" 8 تا 30 تا کلاک لازم داشته باشه. دقت کنید مثال زدم 8، یعنی حتا کمتر از چیزی که شما فک میکردید.
                  شما برای سنجش تعداد کلاک های یه کد، گام به گام باید اونو بنویسید، فایل اسمبلر رو بعد از کامپایل باز کنید و کد مربوط به اون قسمت رو پیدا کنید، هر دستورالعمل رو که اونجا نوشته، بردارید ببرید تو دیتاشیت میکروکنترلر تو بخش اینستراکشن ست، و نگاه کنید که هر اینستراکشن چند تا کلاک تو اون میکروی خاص میبره. بعد جمع بزنید و بگید فلان کار با این حالت اپتیمایزشن با این کامپایلر برای این میکرو اینقدر کلاک میبره.


                  2) هیپ تو مدیریت حافظه زمانی وجود داره که یه سیستم عامل یا یه مفسر سطح بالا وجود داشته باشه برای کنترلش و از طرفی خود حافظه هم خیلی زیاد باشه که امکان الوکیشن بلوک های متفاوت باشه . اما برای میکروکنترلرهای کوچیک dynamic allocation چیز اضافه ایه خیلی وقتا نشدنی و خیلی زمان ها هم شدنی، اما بشدت بیهوده. خیلی وقتا توابع مربوط به dynamic memory توی stdlib برای کامپایلرهای میکروکنترلر ها اصن وجود نداره. داینامیک الوکیشن برای میکروکنترلر مث این میمونه که برای یه دهکده که فقط 3 تا خونه کوچیک داره، 2 تا دفتر معاملات املاک بزنی.


                  3) چیزی که در مورد استرینگ گفته شده، مث عملگر + برای چسبوندن استرینگ ها به هم، در اصل std::string ه. یعنی یکی از کلاس های بزرگ مربوط به C++ که اصن تو خیلی از کامپایلرهای میکروکنترلر ها پشتیبانی نمیشه. کامپایلرهای مربوط به میکروکنترلرها، حتا وقتی که از C++ پشتیبانی میکنن، برای استرینگ، از C-String استفاده میکنن که اونم استرینگ رو به شکل یه آرایه از کاراکتر در نظر میگیره به همراه یه کاراکتر نول اختیاری در انتهای آرایه. همه ی کتابخونه های C++ که قرار نیست برای همه جای عالم قابل استفاده باشن و مثلا ما با یه کتابخونه openGL تو اتمل استودیو برای ات تاینی 13 یه بازی سه بعدی بسازیم.
                  Si vis pacem, para bellum

                  دیدگاه


                    #10
                    پاسخ : برنامه بهینه

                    ممنون از اینکه اطلاعاتتون رو در اختیار گداشتید و برای کمک بما زمان می گذارید.
                    1. در زمینه بهینه سازی و آپتیمایز کامپایلر که می فرمایید اطلاعاتم کامل و مستند نیست که بتونم خدمتتون دقیق و با اطمینان چیزی بگم اما صحبتم اینجا تعداد کلاک (در کامپایلرهای مختلف یا میکروهای مختلف) نبود مهندس جان. صحبت این هست که برای مثال (در پست یک ) حذف یک not و یا ( در پستهای 3 و 5 ) حذف تعداد زیادی “&& “ و “<” قطعا باعث خواهد شد تا پردازنده زمانی برای پردازش این موارد صرف نکند (چه یک کلاک برای هرکدام یا 10 کلاک باشد. کلا حذف می شود)
                    2. رجوع به مورد 3
                    3. من همانطور که در پستهای بالا عنوان کردم بیشتر منظورم آردوینو وعلی الخصوص esp هست ( که درواقع جهت دهی اصلی مباحثم برای سری مطالبی که در آینده قرار خواهم داد بیشتر برای همین esp خواهد بود) البته شاید تعمیم دادنش به میکروهای دیگه مثل avr با توجه به صحبتهای شما اشتباه باشه. در esp در تمام منابعی که من دیدم صریحا عنوان شده که تا جای ممکن از String (بصورت object) استفاده نکنید. بجاش از آرایه های کاراکتری و پوینتر و ... استفاده کنید که تمام این موارد در پستهای آینده بترتیب عنوان خواهد شد ( چون در esp بخاطز قابلیت وایفای معمولا زیاد از متن و استرینگ استفاده می کنند). طبق فرمایشات شما حتی اگر استثنائا متغیر string در یکسری از میکرو ها بصورت آرایه های کاراکتری لحاظ شوند باز هم کلیت این مطالب در مورد احتیاط در تعریف متغیرهای آبجکت و لزوم استفاده از پوینتر و .. که در آینده گفته خواهد شد برقرار است.(بخاطر حفظ فضای حافظه)

                    کلا چون عوض کردن عادتهای برنامه نویسی مشکل هست بخاطر همین بنظر من بهتره که خودمون رو به نوشتن کد با یک روش ثابت و استاندارد عادت بدهیم تا با عوض کردن میکرو و کامپایلر کمتر بمشکل بخوریم. (تاکید میکنم این نظر شخصی هست)
                    تاپیک ویدیوهای آموزشی حواشی آردوینو و ESP

                    دیدگاه

                    لطفا صبر کنید...
                    X