بهینهسازی برنامه
در علم کامپیوتر، بهینهسازی برنامه یا بهینهسازی نرمافزار، روند اصلاح یک سیستم نرمافزاری است تا بعضی از جنبههای آن را کارآمدتر کند یا از منابع کمتر استفاده کند.[1] بهطور کلی، یک برنامه کامپیوتری ممکن است بهینه شود تا سریعتر اجرا شود یا بتوان آن را با ذخیره حافظه کمتر یا با استفاده از منابع دیگر یا حتی قدرت کمتری خلق کرد
عمومی
اگر چه کلمه "بهینه سازی" ریشه در "بهینه"دارد، فرایند بهینهسازی برای تولید یک سیستم بهطور مطلوب نادر است. سیستم بهینه تنها در یک برنامه یا برای یک مخاطب مطلوب است. ممکن است مقدار زمانی که یک برنامه برای انجام برخی کارها طی میکند یا حافظه بیشتری که مصرف میکند را کاهش دهد. در یک برنامه کاربردی که فضای حافظه در اصل کم است، ممکن است به صورت عمدی یک الگوریتم کندتر را برای استفاده از حافظه کمتر انتخاب شود. اغلب طراحی "یک اندازه که به همه اختصاص داده میشود " وجود دارد که در همه موارد به خوبی کار میکند، بنابراین مهندسان به منظور بهینهسازی ویژگیهایی که بیشترین توجه را به خود جلب میکنند، در کنار هم قرار میگیرند.. علاوه بر این تلاش لازم برای ساختن یک قطعه نرمافزاری بهطور کامل بهینه است - حتی درصورت ناتوانی برای هر گونه پیشرفت در ان - تقریباً همیشه بیش از معقول بودن برای مزایایی است که شامل میشود؛ بنابراین فرایند بهینهسازی ممکن است قبل از رسیدن به یک راه حل کاملاً مطلوب متوقف شود. خوشبختانه، اغلب موارد این است که بیشترین پیشرفت در اوایل روند انجام میشود.
سطح بهینهسازی
بهینهسازی میتواند در سطوح مختلف رخ دهد. بهطور معمول، سطوح بالاتر تأثیر بیشتری دارند و بعداً در یک پروژه تغییر میکنند و نیاز به تغییرات قابل توجهی دارند و اگر مجبور باشند تغییرات لازم را انجام دهند بازنویسی کامل خواهند شد؛ بنابراین بهینهسازی میتواند بهطور معمول از طریق پالایش از بالا به پایین ادامه یابد، با دستاوردهای بزرگتر اولیه و با کار کمتر به دست میآید، و پس از آن دستاوردهای کوچکتر و نیاز به کار بیشتر. با این حال، در برخی موارد عملکرد کلی بستگی به عملکرد بخشهای بسیار پایین برنامه دارد و تغییرات کوچک در مراحل اولیه در مورد جزئیات پایین سطح میتواند تأثیرات ناخوشایندی داشته باشد. بهطور معمول به بهرهوری در یک پروژه توجه میشود - هرچند که این تغییرات قابل توجه است - اما بهینهسازی عمده اغلب به عنوان یک پالایش در نظر گرفته میشود که دیر یا زود انجام شود. در پروژههای در حال اجرا دیگر، بهطور معمول چرخه بهینهسازی وجود دارد، در حالیکه بهبود یک منطقه محدودیتهای دیگری را نشان میدهد و معمولاً زمانی کاهش مییابد که عملکرد قابل قبول باشد یا سودو هزینه بیش از حد کوچک باشد.
عملکرد بخشی از مشخصات برنامه است - یک برنامه که آهسته باشدبهطور غیرقابل اجتناب برای هدف مناسب نیست: یک بازی ویدیویی با ۶۰ هرتز (فریم در ثانیه) قابل قبول است، اما ۶ فریم در ثانیه غیرقابل قبول و خستهکننده است - عملکرد از ابتدا مورد بررسی قرار میگیرد تا اطمینان حاصل شود که سیستم قادر به ارائه عملکرد کافی میباشد و سیستم نهایی (با بهینهسازی) به عملکرد قابل قبول دست مییابد. این موضوع گاهی اوقات اینطور برداشت میشود که بهینهسازی همیشه میتواند دیر تر انجام شود، و در نتیجه سیستمهای نمونه اولیه که - اغلب به میزان زیاد یا بیشتر هستند - و حتی سیستمهایی که در نهایت خرابی هستند، زیرا از لحاظ معماری نمیتوانند به عملکردی که هدف خود قرار دادهاند دست یابند. مانند اینتل ۴۳2 (1981)که سالها همکاری را برای دستیابی به یک عملکرد قابل قبول مانند آن که (Java (1995 بتواند عملکرد قابل قبولی را با(HotSpot (1999 داشته باشد را انجام دادند. درجه ای که عملکرد بین نمونه اولیه و سیستم تولیدی را نشان میدهد تغییر میکند و چگونگی ارتقای آن بهینهسازی میشود، که میتواند منبع مهمی از عدم اطمینان و خطر نمونه باشد.
- سطح طراحی
در بالاترین سطح طراحی ممکن است بهینهسازی انجام شود تا بهترین استفاده را از منابع موجود، اهداف، محدودیتهای مورد انتظار داشته باشیم. طراحی معماری یک سیستم عمدتاً بر عملکرد آن تأثیر میگذارد. به عنوان مثال، یک سیستم که وابسته به زمان تأخیر شبکهها است (که در آن زمان تأخیر شبکه محدودیت اصلی عملکرد کلی است) بهینهسازی میشود تا زمان ان را به حداقل برساند، در حالت ایدئال یک درخواست واحد (یا هیچ درخواست، مانند یک پروتکل فشار) به جای چند دور دفعات صورت میگیرد در اصل نشان میدهد که انتخابها به اهداف طراحی بستگی دارد: هنگام طراحی یک کامپایلر، اگر تدوین سریع اولویت کلیدی باشد، طراحی یک کامپایلر یک گذر سریعتر از یک کامپایلر چند گذر (با فرض انجام کار مشابه) انجام میشود، اما اگر سرعت خروجی کد هدف باشد، یک کامپایلر چندتایی بهتر به هدف میرسد، حتی اگر طول بکشد. انتخاب پلاتفرم و زبان برنامهنویسی در این سطح رخ میدهد و تغییر آنها اغلب نیازمند بازنویسی کامل است، هرچند یک سیستم ماژولار ممکن است اجازه بازنویسی تنها برخی از مؤلفهها را بدهد - مثلاً یک برنامه پایتون ممکن است بخشهای عملکردی حیاتی را در C بازنویسی کند. سیستم، انتخاب معماری (client-server، peer-to-peer و غیره) در سطح طراحی رخ میدهد و ممکن است دشوار باشد، به خصوص اگر تمام اجزای سازنده را نمیتوان جایگزین کرد (به عنوان مثال مشتریان قدیمی).
- الگوریتمها و ساختارهای داده
با در نظر گرفتن یک طرح کلی، انتخاب خوبی از الگوریتمهای کارآمد و ساختار دادهها و اجرای کارآمد این الگوریتمها و ساختارهای دادهها، در ادامه آمدهاست. پس از طراحی، انتخاب الگوریتمها و ساختارهای داده بر کارایی بیش از هر جنبه دیگر برنامه تأثیر میگذارد. بهطور کلی، ساختار دادهها بیشتر از الگوریتمها تغییر میکند، زیرا فرضیه ساختار دادهها و فرضیههای عملکرد آن در طول برنامه استفاده میشود، هرچند این میتواند با استفاده از انواع دادههای انتزاعی در تعاریف تابع به حداقل برسد و تعاریف ساختار دادهها محدود شود به چند مکان
برای الگوریتمها این بهطور عمده شامل اطمینان از اینکه الگوریتمها ثابت (O (1، لگاریتمی (O(log n، خطی (O (n یا در برخی موارد (log-line O(n log nدر ورودی (هر دو در فضا و زمان). الگوریتمهایی با پیچیدگی درجه دوم(O (n 2 در مقیاس شکست مییابند، و حتی الگوریتمهای خطی باعث میشود که مشکلات به صورت مکرر فراخوانی شوند و بهطور معمول با احتمالات ثابت یا لگاریتمی جایگزین شوند.
فراتر از نظم نسبیت رشد، عوامل پایدار اهمیت دارد: الگوریتم کند تراز الگوریتم سریعتر یا کوچکتر (به عنوان سادهتر)، فقط زمانی هر دو با ورودی کوچک مواجه میشوند، که در واقعیت اتفاق بیفتد. اغلب الگوریتم ترکیبی بهترین عملکرد را به وجود میآورد، به این دلیل که این تغییرات با اندازه تغییر میکند.
یک روش کلی برای بهبود عملکرد، اجتناب از کار است. یک مثال خوب استفاده از یک مسیر سریع برای موارد معمول است، بهبود عملکرد با اجتناب از کار غیر ضروری. به عنوان مثال، با استفاده از یک الگوریتم طرح بندی متن ساده برای متن لاتین، تنها تغییر در الگوریتم طرح پیچیده برای اسکریپتهای پیچیده مانند Devanagari. یکی دیگر از روشهای مهم ذخیرهسازی، به ویژه memoization، که از محاسبات بیش از حد جلوگیری میکند. با توجه به اهمیت ذخیرهسازی، اغلب سطوح ذخیرهسازی در یک سیستم وجود دارد که میتواند مشکلاتی را از استفاده از حافظه ایجاد کند و مسائل مربوط به صحت از حافظههای غیرمجاز.
- سطح کد منبع
فراتر از الگوریتمها و اجرای آنها در انتزاعی و ماشین آلات بتن و کد منبع سطح انتخاب میتواند تفاوت قابل توجهی است. برای مثال در اوایل C کامپایلر (1)
شد و آهستهتر از for(;;)
به صورت بی قید و شرط حلقه زیرا در حالی که(1)
ارزیابی ۱ و سپس یک پرش شرطی که تست شده اگر درست شد در حالی که برای (;;)
تا به حال بی قید و شرط پرش. برخی از بهینهسازی (مثل این یکی) امروزه میتوان با بهینهسازی کامپایلر. این بستگی به منبع زبان دستگاه مورد نظر زبان و کامپایلر و میتواند دشوار به درک یا پیشبینی و تغییرات در طول زمان است و این کلید جایی که درک درستی از کامپایلر و کد دستگاه میتواند عملکرد را بهبود بخشد. حلقه-ناوردا کد حرکت و بازگشت ارزش بهینهسازی نمونههایی از بهینهسازی است که کاهش نیاز به کمکی متغیر و حتی میتواند منجر به اجرای سریع تر و با اجتناب از دور-در مورد بهینهسازی.
- سطح ساخت
بین منبع و سطح کامپایل، دستورالعملها و فلگهای ساخت را میتوان برای تنظیم گزینههای عملکرد در کد منبع و کامپایلر به ترتیب استفاده کرد، از جمله استفاده از پیش پردازنده تعریف میشود که برای مثال برای غیرفعال کردن ویژگیهای نرمافزار غیر ضروری، بهینهسازی برای مدلهای خاص پردازنده یا قابلیتهای سختافزاری، یا پیشبینی شاخه، سیستمهای توزیع نرمافزار مبتنی بر منبع مانند پورتهای BSD و انتقال جنتوها و این صورتی ازشکل بهینهسازی است.
- سطح کامپایل
استفاده از یک کامپایلر بهینهسازی شده مستلزم اطمینان از اینکه برنامه اجرایی حداقل به همان اندازه که کامپایلر میتواند پیشبینی کند بهینهسازی شده باشد.
- سطح مجمع
در پایینترین سطح، نوشتن کد با استفاده از یک زبان مونتاژ، طراحی شده برای یک پلتفرم سختافزاری خاص میتواند کارآمدترین و کمترین کد را تولید کند، در صورتی که برنامهنویس از رکورد کامل دستورالعملهای ماشین استفاده کند. به همین دلیل، بسیاری از سیستم عاملهای مورد استفاده در سیستمهای جاسازی شده بهطور سنتی در کد اسمبلر نوشته شدهاند. برنامهها (به غیر از برنامههای بسیار کوچک) به ندرت از زمان شروع و پایان در مونتاژ به علت زمان و هزینه درگیر میشوند. اکثر آنها از یک زبان سطح بالا به مونتاژ و دست بهینه شده از آنجا جمع شدهاند. هنگامی که بازده قطعات مهمتر از اندازه هستند، ممکن است در یک زبان سطح بالا نوشته شوند.
با کامپایلرهای پیشرفته تر بهینهسازیو پیچیدگی CPUهای اخیر، بیشتر است که کد کارآمدتری را از آنچه که کامپایلر تولید میکند، بنویسید و پروژههای کوچک به این روش بهینهسازی نهایی نیاز دارند.
امروزه کد نوشته میشود تا به عنوان ماشینهای بسیاری بتواند اجرا شود. به عنوان یک نتیجه، برنامه نویسان و کامپایلرها همیشه از دستورالعملهای کارآمدتر ارائه شده توسط پردازندههای جدیدتر یا پیش فرض مدلهای قدیمی تر استفاده نمیکنند. علاوه بر این، کدهای مونتاژ شده برای یک پردازنده خاص بدون استفاده از چنین دستورالعملهای تنظیم شدهاست که ممکن است در پردازنده دیگری غیرقطعی باشند، و انتظار تنظیم متفاوت از کد را دارند.
بهطور معمول امروز، به جای اینکه در زبان اسمبلی بنویسیم، برنامه نویسان از یک disassembler برای تجزیه و تحلیل خروجی یک کامپایلر استفاده میکنند و کد منبع بالا را تغییر میدهند تا بتوان آن را بهطور مؤثر تر کامپایل کرد یا متوجه شد که چرا آن ناکارآمد است.
- زمان اجرا
کامپایلرها فقط در زمان اجرا میتوانند کد ماشین را بر اساس دادههای زمان اجرا، تولید کنند. این تکنیک به اولین موتورهای بیان منظم میرسد و با Java HotSpot و V8 برای جاوا اسکریپت گسترش پبدا کردهاست. در برخی موارد بهینهسازی سازگاری ممکن است قادر به انجام بهینهسازی زمان اجرا بیش از توانایی کامپایلرهای استاتیک با تنظیم پارامترهای پویا با توجه به ورودیهای واقعی یا سایر عوامل باشد.
بهینهسازی پروفایل هدایت شده یک روش بهینهسازی تلفیقی پیش از زمان (AOT) بر اساس پروفایلهای زمان اجرا است و شبیه به یک متد استاتیکی «مورد متوسط» که از تکنیک پویا بهینهسازی سازگاری است.
کد خودمحور میتواند در پاسخ به شرایط زمان اجرا به منظور بهینهسازی کد تغییر کند. این در برنامههای زبان مونتاژ رایج بود.
برخی از طرحهای CPU میتوانند برخی از بهینهسازیها را در زمان اجرا انجام دهند. بعضی از نمونهها عبارتند Out-of-order execution , Speculative execution ,Instruction pipelines, و Branch predictors . کامپایلرها میتوانند به این برنامه کمک کنند که از ویژگیهای این CPU بهرهمند شوند، مثلاً از طریق برنامهریزی دستورالعمل.
بهینهسازی بستر وابسته و مستقل
بهینهسازی کد را میتوان همچنین بهطور گسترده به عنوان طبقهبندی پلت فرمهای وابسته و تکنیکهای مستقل از پلتفرم دانست. در حالی که آنهایی که در اکثر یا تمام سیستم عاملها مؤثر هستند، تکنیکهای وابسته به پلت فرم از ویژگیهای خاص یک پلت فرم استفاده میکنند یا به پارامترهای بسته به پلت فرم تک یا حتی بر روی پردازنده تک تک تکیه میکنند؛ بنابراین ممکن است نوشتن یا تولید نسخههای مختلف از همان کد برای پردازندههای مختلف مورد نیاز باشد. به عنوان مثال، در مورد بهینهسازی سطح کامپایل، تکنیکهای مستقل از پلت فرم، تکنیکهای عمومی هستند (مانند حلقه، کاهش تماسهای عملکردی، رویههای کارآمد حافظه، کاهش شرایط و غیره)، که اکثر معماریهای CPU را در یک مسیر مشابه را شامل میشود یک نمونه عالی از بهینهسازی مستقل از پلت فرم با حلقه درونی نشان داده شدهاست، جایی که یک حلقه با یک حلقه درونی مشاهده شد، محاسبات بیشتری را در هر واحد زمان انجام میدهد تا یک حلقه بدون آن یا یک حلقه داخلی در حالی که حلقه است به حساب آورد.[2] بهطور کلی، اینها به منظور کاهش کل مسیر آموزش مورد نیاز برای تکمیل برنامه یا کاهش کل استفاده از حافظه در طول فرایند خدمت میکنند. از سوی دیگر، تکنیکهای وابسته به پلت فرم شامل برنامهریزی دستورالعمل، هماهنگی در سطح دروس، سطح هماهنگی دادهها، تکنیکهای بهینهسازی حافظه پنهان (یعنی پارامترهای متفاوت در سیستم عاملهای مختلف) و برنامهریزی دستورالعمل مطلوب حتی در پردازندههای مختلف همان معماری ممکن است.
کاهش قدرت
وظایف محاسباتی را میتوان با روشهای مختلف با کارایی متفاوت انجام داد. یک نسخه کارآمد با قابلیت معادل به عنوان یک کاهش قدرت شناخته شدهاست. برای مثال، کد قطعه زیر C را در نظر بگیرید که قصد دارد مجموع اعداد صحیح را از ۱ به N بدست آورد:
int i, sum = 0;
for (i = 1; i <= N; ++i) {
sum += i;
}
printf("sum: %d\n", sum);
این کد (با فرض وجود سرریز حسی) میتواند با استفاده از یک فرمول ریاضی مانند:
int sum = N * (1 + N) / 2;
printf("sum: %d\n", sum);
بهینهسازی، گاهی اوقات توسط یک کامپایلر بهینهسازی انجام میشود، این است که یک روش (الگوریتم) را انتخاب کنید که کارایی بیشتری در محاسبات است، در حالی که حفظ همان کارکرد. هم مهم است برای رفع برخی از این تکنیکها، کارایی الگوریتمی را مشاهده میکنید. با این حال، بهبود قابل توجهی در عملکرد اغلب میتواند با حذف قابلیتهای بیرونی به دست آید.
بهینهسازی همیشه یک فرایند واضح یا بصری نیست. در مثال بالا، نسخه بهینه شده ممکن است در مقایسه با نسخه اصلی کندتر از نسخه اصلی باشد اگر N به اندازه کافی کوچک باشد و سختافزار خاص در انجام عملیات علاوه بر اضافه کردن و حلقه بیش از ضرب و تقسیم، بسیار سریعتر اتفاق میافتد.
Trade-offs
در برخی موارد، با این حال، بهینهسازی مبتنی بر استفاده از الگوریتمهای پیچیدهتر یا استفاده از «موارد خاص» و «حقههای ویژه» و انجام پیچیده تجارت است. برنامه بهطور کامل بهینه شده ممکن است دشوار باشد و از این رو ممکن است خطاهای بیشتری را نسبت به نسخههای غیرمتعارف داشته باشد. بعلاوه، حذف برخی از مشکلات واضح، برخی از بهینهسازیهای سطح کد باعث کاهش قابلیت نگهداری میشود.
بهینهسازی بهطور کلی بر بهبود تنها یک یا دو جنبه از عملکرد تمرکز میکند: زمان اجرا، استفاده از حافظه، فضای دیسک، پهنای باند، مصرف برق یا برخی از منابع دیگر. این معمولاً نیاز به یک trade-offدارد؛ که در آن یک عامل به هزینه دیگران بهینهسازی شدهاست. به عنوان مثال، افزایش حجم حافظه پنهان باعث بهبود عملکرد در زمان اجرا میشود، اما مصرف حافظه نیز افزایش مییابد. سایر موارد رایج شامل وضوح و مختصر کد است.
مواردی وجود دارد که برنامهنویس بهینه سا باید تصمیم بگیرد که نرمافزار را برای برخی عملیات بهتر کند، اما هزینه عملیات دیگر کمتر کارآمد است. این ترکیبات ممکن است گاهی غیر فنی باشد مانند زمانی که یک رقیب نتیجه یک معیار را منتشر کردهاست که باید به منظور بهبود موفقیت تجاری مورد ضرب و شتم قرار گیرد، اما احتمالاً با استفاده از نرمال بودن نرمافزار کمتر کارآمد میشود. چنین تغییراتی گاهی اوقات به شوخی به عنوان pessimizations اشاره شدهاست.
تنگنا
بهینهسازی ممکن است شامل یافتن یک تنگنا در یک سیستم باشد - یک جزء که عامل محدودکننده در عملکرد است. از لحاظ کد، این اغلب یک نقطه داغ است - قسمت بحرانی کد که مصرفکننده اولیه منابع مورد نیاز است - هرچند که این میتواند عامل دیگری مانند زمان تأخیر I / O یا پهنای باند شبکه باشد.
در علم کامپیوتر، مصرف منابع اغلب به نوعی توزیع قانون قدرت پیوستهاست و اصل پارتو را میتوان به بهینهسازی منابع با توجه به اینکه ۸۰٪ از منابع معمولاً با استفاده از ۲۰٪ از عملیات مورد استفاده قرار میگیرد.[3] در مهندسی نرمافزار اغلب یک تقریب بهتر است که ۹۰٪ زمان اجرای برنامه کامپیوتری صرف ۱۰٪ از کد انجام میشود (قانون ۹۰/۱۰ در این زمینه شناخته میشود).
الگوریتم پیچیدهتر و ساختار دادهها با بسیاری از اقلام خوب عمل میکند، در حالی که الگوریتمهای ساده برای مقادیر کوچک دادهها مناسب تر هستند - زمان راه اندازی، زمان شروع و عوامل ثابت الگوریتم پیچیدهتر میتواند مزیت را بیش از حد و در نتیجه الگوریتم ترکیبی یا سازگار الگوریتم ممکن است سریعتر از هر الگوریتم تک باشد. یک پروفیل عملکرد میتواند مورد استفاده قرار گیرد تا محدودیت تصمیمهای مربوط به عملکرد که مطابق با شرایط است.[4]
در برخی موارد، اضافه کردن حافظه بیشتر میتواند کمک کند که برنامه را سریعتر اجرا کند. به عنوان مثال، یک برنامه فیلتر معمولاً هر خط را مطالعه میکند و فوراً آن خط را خروجی میکند. این تنها با استفاده از حافظه کافی برای یک خط استفاده میکند، اما عملکرد بهطور معمول ضعیف است، به دلیل تأخیر هر خواندن دیسک. با خواندن کل فایل و سپس نوشتن نتیجه فیلتر شده، میتوان عملکرد را بسیار بهبود داد، هرچند که از حافظه بسیار بیشتری استفاده میکند. ذخیرهسازی نتایج مشابه نیز مؤثر است، هرچند که نیاز به استفاده از حافظه بزرگتر نیز دارد.
وقت بهینهسازی
بهینهسازی میتواند خوانایی را کاهش دهد و کد را اضافه کند که فقط برای بهبود عملکرد استفاده میشود. این ممکن است برنامهها یا سیستمها را پیچیدهتر کند و آنها را برای حفظ و اشکال زدایی سختتر کند. در نتیجه، بهینهسازی یا تنظیم عملکرد اغلب در پایان مرحله توسعه انجام میشود.
دونالد نوت دو اظهار نظر در مورد بهینهسازی انجام داد:
"ما باید در مورد بهره وری کوچک توجه داشته باشیم، حدود ۹۷٪ زمان میگویند: بهینه سازی زود هنگام ریشه همه شر است. با این حال ما نباید فرصتهایمان را در این ۳٪ بحرانی گذراندیم "
- (او همچنین نقل قول را به تونی هور چند سال بعد نقل کردهاست[5] هرچند ممکن است این خطا بود که Hoare مخالف این عبارت است.[6])
«در رشتههای مهندسی تأسیس شده، بهبود ۱۲٪، به راحتی به دست آمده، هرگز به عنوان حاشیه ای به حساب نمیآید و من معتقدم که همان دیدگاه باید در مهندسی نرمافزار غلبه کند»[7]
«بهینهسازی زود هنگام» یک عبارت است که برای توصیف وضعیتی که یک برنامهنویس اجازه میدهد ملاحظات عملکرد به طراحی یک قطعه کد برسد. این میتواند در نتیجه یک طراحی باشد که به اندازه کافی نبوده یا کدی باشد که نادرست است، زیرا کد با بهینهسازی پیچیده شدهاست و برنامه نویسان با بهینهسازی آن را پرت میکنند.
در هنگام تصمیمگیری در مورد اینکه آیا برای بهینهسازی بخش خاصی از برنامه، قانون قانون آمدال همیشه باید مورد توجه قرار گیرد: تأثیر بر برنامه کلی بسیار بستگی دارد به اینکه چقدر زمان واقعی در بخش خاصی صرف میشود، که همیشه از نگاه کردن به کد مشخص نیست بدون تجزیه و تحلیل عملکرد
یک روش بهتر این است که ابتدا کد را از طرح طراحی و سپس کد / کد نمایشی / معیار را ببینید تا ببینیم کدام قسمت باید بهینه شود. طراحی ساده و ظریف اغلب در این مرحله آسانتر میشود و پروفایل میتواند مشکلات عملکرد غیرمنتظره ای را نشان دهد که با بهینهسازی زود هنگام مواجه نخواهد شد.
در عمل، اغلب لازم است که در هنگام طراحی نرمافزار ابتدا اهداف عملکرد را ذکر کنید، اما برنامهنویس اهداف طراحی و بهینهسازی را متعادل میکند.
کامپایلرهای مدرن و سیستم عامل بسیار کارآمد هستند، زیرا عملکرد در نظر گرفته شده اغلب با شکست مواجه میشود. به عنوان مثال، دادههای ذخیره شده در سطح برنامه که مجدداً در سطح سیستم عامل ذخیره میشوند، در اجرای بهبود نمییابند. با این حال، هنگامی که برنامهنویس بهینهسازیهای شکست خورده را از کد تولید حذف میکند، یک مورد نادر است. درست است پیشرفتهای سختافزاری اغلب از هیچگونه پیشرفت بالقوه اجتناب ناپذیر است، با این وجود کد پوششی در آینده به مدت طولانی پس از پایان دادن به هدف آن، ادامه خواهد یافت.
ماکروها
بهینهسازی در طول توسعه کد با استفاده از ماکرو در فرمهای مختلف در زبانهای مختلف صورت میگیرد.
در بعضی از زبانهای رویه ای، مانند C و C ++، ماکروها با استفاده از جایگزینی نشانه اجرا میشوند. امروزه، توابع درون خطی میتواند به عنوان یک جایگزین امن در بسیاری موارد استفاده شود. در هر دو حالت، بدن تابع مجهز میتواند به وسیله کامپایلر بهینهسازی کامپایل زمان بیشتری را انجام دهد، از جمله تأخیر ثابت، که ممکن است برخی از محاسبات را برای زمان کامپایل حرکت کند.
در بسیاری از زبانهای برنامهنویسی کاربردی، ماکروها با استفاده از پارسا زمان جایگزینی درختان تجزیهکننده / درختهای انتزاعی نحو اجرا میشوند که ادعا میکند آنها را برای استفاده ایمن تر میکند. از آنجایی که در بسیاری از موارد از تفسیر استفاده میشود، این یکی از راههای اطمینان از این است که چنین محاسباتی تنها در زمان تجزیه انجام میشود و گاهی اوقات تنها راه است.
Lisp این سبک از ماکرو را ایجاد کرد و چنین ماکروهای اغلب به نام "ماکرو مانند Lisp" نامیده میشود. یک اثر مشابه را میتوان با استفاده از metaprogramming قالب در C ++ بدست آورد.
در هر دو مورد، کار به زمان کامپایل منتقل شدهاست. تفاوت بین C ماکروها در یک طرف، و Lisp مانند ماکروها و C ++ برنامهنویسی خودآرا در طرف دیگر، آن است که ابزارهای دوم اجازه میدهد انجام محاسبات دلخواه در زمان کامپایل / تجزیه زمان، در حالی که گسترش C ماکروها هیچ انجام نمیمحاسبات، و بر توانایی بهینهساز برای انجام آن متکی است. علاوه بر این، ماکرو C مستقیماً از بازگشت یا تکرار پشتیبانی نمیکند، بنابراین تورینگ کامل نیست.
با این حال، همانطور که با هر بهینهسازی، اغلب پیشبینی میشود که این ابزار تا زمانی که پروژه کامل شود، بیشترین تأثیر را دارد، مشکل است.
بهینهسازی خودکار و دستی
همچنین نگاه کنید به رده: بهینهسازی کامپایلر
بهینهسازی میتواند توسط کامپایلرها یا توسط برنامه نویسان انجام شود. دستاوردها معمولاً برای بهینهسازی محلی محدود میشوند و برای بهینهسازی جهانی بزرگتر است. بهطور معمول، قویترین بهینهسازی، یافتن یک الگوریتم برتر الگوریتم است.
بهینهسازی یک سیستم کلی معمولاً توسط برنامه نویسان انجام میشود، زیرا برای بهینه سازان خودکار بسیار پیچیدهاست. در این وضعیت، برنامه نویسان یا مدیران سیستم به صراحت کد را تغییر میدهند تا سیستم کلی بهتر عمل کند. اگر چه میتواند کارایی بهتر را تولید کند، اما به مراتب گرانتر از بهینهسازی خودکار است. از آنجایی که پارامترهای بسیاری بر عملکرد برنامه تأثیر میگذارند، فضای بهینهسازی برنامه بزرگ است. فراشناخت و یادگیری ماشین برای رفع پیچیدگی بهینهسازی برنامه استفاده میشود.[8]
از بخش پروفایل (یا آنالیز کننده عملکرد) برای پیدا کردن بخشهایی از برنامه استفاده میکنید که بیشترین منابع را میگیرند - تنگنا گاهی اوقات برنامهنویسان معتقدند که ایده ای روشن از جایی که تنگنا است، اما شهود اغلب اشتباه است. بهینهسازی قطعه ای غیر مهم از کد بهطور معمول برای انجام کار کلی کمک نمیکند.
هنگامی که تنگنا محلی سازی میشود، بهینهسازی معمولاً با بازخوانی الگوریتم مورد استفاده در برنامه آغاز میشود. اغلب اوقات الگوریتم خاصی میتواند بهطور خاص برای یک مشکل خاص طراحی شده و کارایی بهتر از یک الگوریتم ژنریک را فراهم کند. برای مثال، کار مرتبسازی لیست عظیمی از اقلام معمولاً با یک روش مرتبسازی سریع انجام میشود که یکی از کارآمدترین الگوریتمهای عمومی است. اما اگر برخی از ویژگیهای مورد استفاده قابل بهرهبرداری باشد (به عنوان مثال، آنها قبلاً در یک نظم خاص مرتب شدهاند)، روش دیگری میتواند مورد استفاده قرار گیرد یا حتی یک مرتبسازی مرتبسازی سفارشی.
پس از اینکه برنامهنویس متوجه شد که بهترین الگوریتم انتخاب شدهاست، بهینهسازی کد میتواند شروع شود. حلقهها میتوانند به صورت غیرمجاز (برای سربار حلقه پایینتر، اگر چه اغلب میتواند به سرعت پایینتر در صورتی که بیش از حد حافظه نهان سیپییو)، نوع دادهها به عنوان کوچک ممکن است میتواند استفاده شود، ریاضی عدد صحیح میتواند به جای نقطه شناور استفاده شود، و غیره. (نگاه کنید به مقاله کارایی الگوریتمی برای این و سایر تکنیکها)
تنگناهای عملکرد میتواند به دلیل محدودیتهای زبان به جای الگوریتمها یا ساختارهای داده مورد استفاده در برنامه باشد. گاهی اوقات یک بخش مهم از برنامه را میتوان دوباره در یک زبان برنامهنویسی متفاوت نوشت که دسترسی مستقیم به دستگاه پایه را فراهم میکند. برای مثال، برای زبانهای بسیار سطح مانند Python معمول است که ماژولهایی که در C نوشته شدهاند برای سرعت بیشتر باشد. برنامههایی که قبلاً در C نوشته شدهاند میتوانند ماژولهایی را که در مجمع نوشته شدهاند داشته باشند. برنامههای نوشته شده در دی میتوانند از مونتاژ خطی استفاده کنند.
بخشهای بازنویسی "پرداخت میشود" در این شرایط به دلیل یک قاعده کلی "شناخته شده به عنوان قانون ۹۰/۱۰، که میگوید ۹۰ درصد از زمان در ۱۰٪ از کد صرف، و تنها ۱۰٪ از زمان در ۹۰٪ باقی مانده از کد؛ بنابراین، تلاش فکری برای بهینهسازی فقط بخش کوچکی از برنامه میتواند تأثیر بزرگی بر سرعت کلی داشته باشد - اگر قسمت درست(ها) قابل نصب باشد.
بهینهسازی دستی گاهی اوقات عواقبی ناشی از خواندن را مختل میکند؛ بنابراین بهینهسازی کد باید با دقت مستندسازی شود (ترجیحا با استفاده از نظرات درون خطی)، و تأثیر آنها بر پیشرفت آینده مورد ارزیابی قرار میگیرد.
برنامه ای که بهینهسازی خودکار را انجام میدهد بهینهسازی شدهاست. اکثر بهینهسازها در کامپایلرها جاسازی شده و در طول تدوین کار میکنند. بهینه سازان اغلب میتوانند کد تولید شده را به پردازندههای خاصی متصل کنند.
امروزه بهینهسازی خودکار تقریباً بهطور انحصاری به بهینهسازی کامپایلر محدود می شود. با این حال، به دلیل اینکه بهینهسازی کامپایلر معمولاً به یک مجموعه ثابت و بهینهسازی بهطور کلی محدود میشود، تقاضای قابل توجهی برای بهینه سازان وجود دارد که میتوانند توصیفهای مربوط به بهینهسازیهای مربوط به مشکل و زبان را بپذیرند و به مهندس اجازه میدهد تا بهینهسازیهای سفارشی را مشخص کنند. ابزارهایی که توصیفهای بهینهسازی را میپذیرند، سیستمهای تبدیل سیستم نامیده میشوند و شروع به استفاده از سیستمهای واقعی نرمافزار مانند C ++ میشوند.
برخی از زبانهای سطح بالا (ایفل، استرل) برنامههای خود را با استفاده از زبان متوسط بهینهسازی میکنند.
محاسبات گرید یا محاسبات توزیع شده به منظور بهینهسازی کل سیستم، با حرکت دادن وظایف از رایانههای با استفاده بالا به رایانهها با زمان بیکاری است.
زمان برای بهینهسازی
گاهی اوقات، زمان انجام شده برای انجام بهینهسازی در خود ممکن است یک مسئله باشد.
بهینهسازی کد موجود معمولاً ویژگیهای جدیدی را اضافه نمیکند و بدتر از آن ممکن است اشکالات جدیدی در کد قبلی ایجاد شده باشد (همانطور که هر تغییر ممکن است). از آنجا که ممکن است کد بهینهسازی شده به صورت دستی ممکن است «قابل خواندن» کمتر از کد غیرقطعی باشد، بهینهسازی ممکن است باعث حفظ قابلیت نگهداری آن شود. بهینهسازی میآید در قیمت و مهم است که مطمئن شوید که سرمایهگذاری ارزشمند است.
بهینهسازی خودکار (یا کامپایلر بهینهسازی، برنامه ای که بهینهسازی کد را انجام میدهد) خود باید بهینهسازی شده باشد، یا به منظور بهبود کارایی برنامههای هدف خود یا افزایش سرعت عملیات خود. یک کامپایل انجام شده با بهینهسازی «روشن» معمولاً طول میکشد طول میکشد، اگر چه این معمولاً تنها مشکل زمانی که برنامهها بسیار بزرگ است.
بهطور خاص، برای کامپایلرهای فقط در زمان، اجزای اجزای کامپایل زمان اجرای اجرا، همراه با کد هدف خود، کلید بهبود سرعت اجرای کلی است.
منابع
- Jon Bentley: Writing Efficient Programs, شابک ۰−۱۳−۹۷۰۲۵۱−۲ .
- Donald Knuth: The Art of Computer Programming
- Robert Sedgewick، الگوریتم، 1984، ص. 84
- ساخت برنامه حلقه داخلی: یک راه سریع تر برای اجرای برنامه
- Wescott, Bob (2013). The Every Computer Performance Book, Chapter 3: Useful laws. CreateSpace. ISBN 1-4826-5775-9.
- "Performance Profiling with a Focus". Retrieved 15 August 2017.
- خطاهای تکس در نرمافزار تمرین و تجربه، جلد 19، شماره 7 (ژوئیه 1989)، ص. 607-685، در کتاب "برنامه ریزی سواد" (ص 276) دوباره چاپ شدهاست
- تونی هار، یک ایمیل 2004
- Memeti, Suejb; Pllana, Sabri; Binotto, Alécio; Kołodziej, Joanna; Brandic, Ivona (26 April 2018). "Using meta-heuristics and machine learning for software optimization of parallel computing systems: a systematic literature review". Computing. Springer Vienna. doi:10.1007/s00607-018-0614-9. Retrieved 27 April 2018.
پیوند به بیرون
- How To Write Fast Numerical Code: A Small Introduction
- "What Every Programmer Should Know About Memory" by Ulrich Drepper — explains the structure of modern memory subsystems and suggests how to utilize them efficiently
- "Linux Multicore Performance Analysis and Optimization in a Nutshell", presentation slides by Philip Mucci
- Programming Optimization by Paul Hsieh
- Writing efficient programs ("Bentley's Rules") by Jon Bentley
- "Performance Anti-Patterns" by Bart Smaalders