Remove ads
From Wikipedia, the free encyclopedia
У информатици, оптимизација програма или оптимизација софтвера је процес модификације софтвера система како би се направио приступ ефикаснијег рада или да се користи мање извора.[1] Углавном, рачунарски програм може бити извршен чешће, или је у могућности да ради са мањим капацитетом меморије или других ресурса, или да вуче мању снагу.
Иако реч "оптимизација" има исти корен као "оптимално", ретко је да процес оптимизације продукује прави оптимални систем. Оптимизован систем ће углавном бити оптималан само у једној апликацији или за једну публику. Један може смањити количину времена које је потребно програму да изврши неки задатак по цени трошења више меморије. У апликацији у којој је простор меморије битан, може намерно изабрати спорији алгоритам да би се користило мање меморије. Често постоји "један одговара за све" дизајн који добро ради у свим случајевима, тако да инжењери имају компромисе да оптимизирају атрибуте највећих интереса. Додатно, напор који се захтева да се начини део софтвера који је комплетно оптималан — немоућ за даља унапређења — је често јаснији за бенефиције које би биле обрачунате; тако да процес оптимизације може бити заустављен пре него што је комплетна оптимална солуција постигнута. Срећом, често је случај да највећа унапређења долазе у ранијем процесу.
Оптимизација се може јавити на више нивоа. Углавном највиши нивои имају највећи утицај, и тешко се мењају касније у пројекту, захтевају битне промене или комплетно поновно писање ако је потребно да буду промењени. Тако оптимизација се може наставити преко дорађивања са виших на ниже, са иницијалним додацима повећавања и постигнућем са мање посла, и гасније добици постају мањи и захтевају више рада. Међутим, у неким случајевима општи перформанс зависи од перформанса веома ниских-нивоа порција програма, и мале промене при касном фазом или ранијим разматрањем детаља ниских-нивоа може имате претерано велики утицај. Углавном нека разматрања су дата ефикасно кроз пројекат - иако ово варира доста - али главна оптимизација се често сматра дорадом која се мора десити касније, икада. При дужим пројектима постоје типична кружења оптимизације, где побољшање једне области открива ограничења у другој, а ови су углавном ограничени када је перформанс прихватљив или добици постану премали или прескупи.
Пошто је перформанс део спецификације програма - програм који је много спор неодговара свири : видео игра са 60Hz (оквира-по-секунди) је прихватиљива, али 6 fps-а је неприхватиљиво исецкано - перформанс је разматрање од старта, да се осигура да је систем у могућности да омогући одређени перформанс, и ранији прототипови морају имати грубо прихватљив перформанс да би било самоуверења да ће коначни систем (са оптимизацијом) постигнути прихватљив перформанс. Ово је понекад изостављено при веровању да се оптимизација може увек извршити касније, што резултује системима прототипа који су много спорији - често по реду величина (фактор 10х) или више - и системи који су ултимативни омашаји зато што атрхитектурално не могу постићи своје циљеве перформанса, као што је Intel 432 (1981); или они за које су потребне године рада како би се постигао прихватљив перформанс, као што је Јава (1995), која је само постигла прихватљив перформанс са HotSpot-ом (1999). Степен при коме се перформанс мења између прототипа и продукционог система, и како је подложан за оптимизацију, може бити значајан извор несигурности и ризика.
При највећем нивоу, дизајн може бити оптимизован да најбоље користи доступне ресурсе, дате циљеве, ограничења, и неочекивана коришћења/оптерећења. Архитектуални дизајн система у највећој мери утиче на његов перформанс. На пример, систем код кога је мрежа ограничена кашњењем (где је кашњење мреже главно ограничење при општем перформансу) би била оптимизована да смањи путовања мреже, идеално правећи један захтев (или ниједан захтев, као у пуш протоколу) радије него више кружних путовања. Избор дизајна зависи од циљева: када се дизајнира компилатор, ако је брза компилација кључ приоритета, компилатор једног-пролаза је бржи од компилатора са више-пролаза (претпостављајући исти посао), али ако је брзина излазећег кода циљ, спорији компилатор са више-пролаза одговара циљу боље, иако му треба дуже времена. Избор платформе и програмског језика се јавља при овом нивоу, и њихово често мењање захтева потпуно поновно писање, иако модуларни систем може дозволити само поновно писање неке компоненте - на пример, Пајтонов програм може поново написати секције критичног-перформанса у С-у. У дистрибуираном систему, избор архитектуре (клиент-сервер, вршњак-до-вршњака, итд.) се јавља при нивоу дизајна, и може бити тежак за мењање, посебно ако се све компоненте не могу променити у синхронизацији (нпр., стари клијенти).
Имајући у виду укупан дизајн, добар избор ефикасних алгоритама и структура података, и ефикасних имплементација тих алгоритама и структура података долази следеће. После дизајна, избор алгоритама и структура података утиче на ефикасност више него било који аспект програма. Углавном структуре података су теже за мењање него алгоритми, пошто претпоставка структуре података и њене претпоставке перформанса се користе кроз програм, иако ово може бити умањено коришћењем апстрактног типа података у дефиницијама функције, и задржава конкретне дефиниције структуре података ограничене за неколико места.
За алгоритме, ово примарно постоји за осигуравање да су алгоритми константни О (log n), линеарни О (n), или у неким случајевима log-линеарни О (n log n) при уносу (и у времену и у простору). Алгоритми са квадратном комплексношћу О(n2) не успевају да се измере, и чак линеарни алгоритми проузрокују проблеме ако се позивају више пута, и углавном су замењени константама или логаритмима ако је могуће.
Осим асимптотског реда раста, питање константних фактора: асимптотски спорији алгоритам може бити бржи или мањи (јер је једноставнији) од асимптотски бржег алгоритма када су обоје окренути га мањем улазу, што може бити случај који се јавља у реалности. Често ће хибридни алгоритам пружити најбољи перформанс, приликом ове размене величине.
Генерална техника за побољшање перформанса је да се избегне посао. Добар пример је коришћење брзог пута за честе случајеве, што побољшава перформанс избегавајући непотребни посао. На пример, коришћењем једноставног распореда текста алгоритма за текст на Латиници, само мењање на комплексни распоред алгоритма за комплексне скрипте, као што је Давангари. Још једна важна техника је кешовање, делимична меморизација, што избегава сувишне компутације. Због важности хватања, постоје често многи нивои кешовања у систему, што може проузроковати проблеме коришћења меморије, и тачност проблема од остатака кеша.
Осим генералних алгоритама и њихових имплементација над апстрактном машином, конкретни избори нивоа изворног кода може учинити битну разлику. На пример, на ранијим С-овим компилаторима, while(1)
је био спорији од for(;;)
за безусловну петљу, зато што је while(1)
процењен са 1 и онда је имао условни скок који је тестиран ако је тачан, док јеfor (;;)
имао безусловни скок . Неке оптимизације (као што је ова) се могу данас извршити преко оптимизације компилатора. Ово зависи од језика извора, циљаног машинског језика, и од компилатора, и може бити тежак за разумевање или за прогнозирање и мења се током времена; ово кључ места где је разумевање компилатора и машинског кода може побољшати перформанс. Петља-инваријанта кретање кода и повратна вредности оптимизације су примери оптимизације који смањују потребу за помоћне променљиве и може чак резултовати бржим перформансом избегавајући кружну оптимизацију.
Између извора и нивоа компилације, директиве и заставе грађења се могу користити да употпуни перформанс опција у изворном коду и компилатору респективно, као што се коришћење препроцесора дефинише да би се уклониле безпотребне карактеристике софтвера, оптимизовање за специфичне моделе софтвера или могућности хардвера, или предвиђање грањања, на пример. Софтвер базиран на извору дистрибуционог система као што је BSD Port и Џенту Portage може искористити предност ове форме оптимизације.
Коришћењем оптимизације компилатора се тежи да се осигура да извршни модул буде оптимизован барем толико колико може компилатор предвидети.
При најнижем нивоу, писање кода користећи асембл језиком, дизајнираног за одређену платформу хардвера може произвести најефикаснији компактни код ако програмер искористи предности пуног репертоара машинских инструкција. Многи оперативни системи коришћени код уграђених система су традиционално исписани у асемблер коду из овог разлога. Програми (други од веома малих програма) су ретко исписани из почетка да заврше у асемблеру приликом кога су време и цена укључени. Многи су компилирани од језика високог нивоа до асемблера и ручно оптимизовани одатле. Када су ефикасност и величина мање важни велики делови могу бити исписани у језику високог-нивоа.
Са модернијим оптимизацијама компилатора и већом комплексношћу новијих процесора, теже је написати ефикаснији код од онога што компилатор генерише, и неколико пројеката имају потребу за "ултимативним" кораком оптимизације.
Многи исписани кодови данас имају намеру да раде на што више машина што је могуће. Као последица, програмери и компилатори не користе увек предност ефикаснијих инструкција који су пружени од стране новијих процесора или непрецизности старијих модела. Као додатак, подешени асемблер код за одређени процесор без коришћења таквих инструкција може идаље бити подоптималан на различитом процесору, очекује је различито подешавање кода.
Данас ће радије него да пишу у асемблер језику, програмери користити дисасемблер како би анализирали излаз компилатора и променили висок-ниво изворног кода тако да би могао бити компајлиран ефикасније, или да би се схватило зашто није ефикасан .
У-правом-тренутку компилатори могу произвести произвољни машински код базиран на рантајм подацима, по цени главе. Ово техника датира од ранијих мотора регуларних излаза, и постала је широко коришћена преко Јава ХотСпота и В8 за Јаваскрипту. У неким случајевима адаптивна оптимизација може извршити рантајм оптимизацију продужавајући могућност статичних компилатора подешавајући динамично параметре према правом уносу или осталим факторима.
Профилно-вођена оптимизација је испред-времена (AOT) компилација оптимизације техника базираних на рантајм профилима, и слично је статичним "просечним случајевима" аналогно динамичној техници адаптивне оптимизације.
Само-модификујући код може изменити сам себе у одговору на рантајм услове да би се оптимизовао код; ово је најчешће у асемблеровим језицима програма.
Неки дизајни процесора могу извршити неке оптимизације при рантајму. Вандредно извршење, спекулативно извршење, инструкциони цевоводи, и предвиђање гранања. Компилатори могу да помогну програму да узме корист ових карактеристика процесора, на пример кроз распоред инструкција.
Оптимизација кода може такође бити широко категоризована као зависна-платформа и технике независне-платформе. Док су каснији ефектнији код већи или код свих платформи, технике зависне-платформе користе специфичне карактеристике једне платформе, или се ослањају на параметре у зависности од једне платформе или од једног процесора. Писање или производња различитих верзија истог кода за различите процесоре може бити потребно. На пример, у случају компилирање-нивоа оптимизације, технике независне-платформе су генеричке технике (као што је одмотавање петље, редукција при позивима фунцкије, рутине ефикасности меморије, редукција услова, итд.), који утичу на већину архитектура процесора на сличан начин. Углавном, ово служи да смањи тоталну дужину пута инструкције која се захтева за употпуњавање програма и/или да смању укупно коришћење меморије током процеса. Са друге стране, технике зависне-платформе укључују распоред инструкције, инструкција-ниво паралелизам, податак-ниво паралелизам, технике оптимизације кеша (тј., параметри који се разликују при различитим платформама) и оптимални распоред инструкције може бити другачији чак и на различитим процесорима исте архитектуре.
Рачунарски задаци могу бити извршени на неколико различитих начин са варирајућом ефикасношћу. Ефикаснија верзија са истом функционалношћу је позната као смањивање снаге. На пример, размотрити следећи С код прегледати чија је намера да добије суму свих целих бројева од 1 до 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 веома мало и деси се да одређени хардвер буде много бржи при извршењу адиције у операција петљања него множење и дељење.
У неким случајевима, међутим, оптимизација се ослања на коришћењу разрађених алгоритама, коришћење "специјалних случајева" и специјалних "трикова" и извршавање комплексних компромиса. "Потпуно оптимизован" програм може бити тежи за разумевање и отуда може имати више грешака него неоптимизоване верзије. Осим елиминације очигледних антишаблона, неки биво код оптимизација смањује способност снабдевања.
Оптимизација ће се углавном фокусирати на побољшање само једног или два аспекта перформанса: време извршења, коришћење меморије, простор на диску, проток, конзумирање снаге или неки други ресурс. Ово ће углавном захтевати компромис — где је један фактор оптимизован по цену других. На пример, повећањем величине кеша побољшава рантајм перформанс, али такође повећава конзумирање меморије. Остали чести компромиси укљулују јасноћу кода и концизност.
Постоје инстанце где програмер који извршава оптимизацију мора одлучити да начини софтвер бољим за неке операције али по цену тога да чини остале операције мање ефикаснијим. Ови компромиси могу понекад бити не-техничке природе — као када конкурент објави бенчмарк резултат који мора бити побеђен како би се побољшао комерцијални успех али можда долази са теретом да чини нормално коришћење софтвера мање ефикасним. Такве промене се понекад на шалу наивају песимизације.
Оптимизација може укључивати проналажење уског грла у систему - компоненте која је ограничава фактор перформанса. У захтевима кода, ово ће често бити врућа тачка - критични део кода који је примарни потрошач потребно ресурса - иако може да буде други фактор, као што је I/O кашњење или мрежни проток.
У рачунарској науци, потрошња ресурса обично прати форму јаког закона дистрибуције, и Парето принцип може бити примењен на оптимизацију ресурса посматрањем да је 80% ресурса типично коришћено од стране 20% операција.[2] У софтверском инжењерству, често је боља апроксимација да 90% времена извршења рачунарског програма је потрошено на извршење 10% кода (познато као 90/10 закон у овом контексту).
Комплекснији алгоритми и структуре података раде добро са многим ставкама, док су једноставни алгоритми погоднији за мање количине података — подешавање, време инсталације, и константни фактори комплекснијег алгоритма могу превагнути корист, и тако хибридни алгоритам или адаптивни алгоритам може бити бржи од једног алгоритма.
У неким случајевима, додавање више меморије може помоћи програму да ради брже. На пример, филтриран програм ће често читати сваку линију и филтер и избациће ту линију одмах. Ово само користи довољно меморије за једну линију, али перформанс је типично сиромашан, због гашњења сваког читања диска. Перформанс може бити доста побољшан читањем читавог фајла него писањем филтрираног резултата, иако ово користи много више меморије. Кешовање резултата је слично ефективан, иако такође захтева веће коришћење меморије.
Оптимизација може смањити читкост и да дода код који је коришћен само како би се побољшао перформанс. Ово може закомпликовати програме или системе, чинећи их тежим за одржавање и дебаговање. Као резултат, оптимизација или перформанс подешавања је често извршена при крају развојног стадијума.
Доналд Кнут је сачинио следеће две излаве за оптимизацију:
"Требало би да заборавимо о малим ефикасностима, рецимо око 97% времена: преурањена оптимизација је корен свог зла. И даље би требало да пропуштамо наше прилике у критичних 3%"[3]
"У утврђеном инжењерингу дисцимплинује се 12% побољшања, лако постигнутих, никада није сматран маргинално и ја верујем да исто гледиште треба да превлада у софтверском инжењерству"[3]
"Преурањена оптимизација" је фраза коришћена да се опише ситуација где програмер допушта разматрањима перформанса да утичу на дизајн дела кода. Ово може да резултује дизајну који није чист као што би могао да буде или код који није тачан, зато што је код компликован због оптимизације и програмеру је одвучена пажња оптимизацијом.
Када се одлучује да ли да се оптимизује део програма, Амдалов закон треба увек да се има на уму: утицај на целокупни програм зависи доста од тога колико је времена уставари потрошено при специфичном делу, што није увек јасно гледањем кода без анализе перформанса.
Бољи приступ је дакле прво дизајн, код из дизајна и онда профил/бенчмарк резултујући код како би се видело који делови би требало да се оптимизују. Једноставан и елегантан дизајен је често лакши за оптимизовање при овом стадијуму, и профилисање може открити ненадане проблеме перформанса које не би биле адресиране од стране преурањене уптимизације.
У пракси, често је неопходно задржати циљеве перформанса на уму када се прво дизајнира софтвер, али програмер баланцира циљеве дизајна и оптимизације.ж
Оптимизација током развоја кода користећи макрое узима различите форме у различитим језицима.
Код неких процедуралних језика, као што је С и С++, макрои су имплементовани коришћењем знаковне супституције. Данас, инлајн функције могу бити коришћене као алтернатива сигурних типова у многим случајевима. У оба случаја, тело инлајн функције може проћи даље компилирано-време оптимизација од стране компилатора, укључујући константно савијање, које може померити прорачуне при компилираном времену.
У многим језицима функционалног програмирања макрои су имплементовани коришћењем супституција рашчлањеног-времена рашчлањених стабала/апстрактних синтакса стабала, што како се тврди их чини сигурнијим за коришћење. Пошто је у многим случајевима интерпретација коришћења, то је један начин да се осигурају такви прорачуни који се само извршавају при рашчлањеном-времену, и понекад једини начин.
Lisp је измислио овај стил за макро, и такви макрои се често називају "макрои као-Lisp". Сличан ефекат се може постићи коришћењем шаблонског метапрограмирања у С++-у.
У оба случаја, посао је премештен на компилирано-време. Разлика између С макроа на једној страни, и Lisp-ових макроа и С++ шаблонског метапрограмирања на другој страни, јесте тај да касније алатке дозвољавају извршавање алгебарских израчунавања при компилираном-времену/рашчлањеном-времену, док С експанзија макроа не ради никаква израчунавања, и ослања се могућност оптимизера да то изврши. Додатно, С макрои не подржавају директно рекурзију или итерацију, тако да нису Тјуринг потпуни.
Као и са било којом оптимизацијом, међутим, често је тешко предвидети где ће такве алатке имати највећи утицај пре него што је пројекат завршен.
Види још Категорија: Компилатроске оптимизације
Оптимизација може бити аутоматизована преко компилатора или извршена преко програмера. Добици су углавном ограничени за локалну оптимизацију, и већи за глобалне оптимизације. Обично, најјала оптимизације је начи супериоран алгоритам.
Оптимизовање целог систем је обично предузет од стране програмера зато што је превише комплексно за аутоматизоване оптимизере. У овој ситуацији, програмери или системски администратори експлицитно мењају код тако да општи систем ради боље. Иако може произвести бољу ефикасност, далеко је скупљи него аутоматизоване оптимизације.
Користити профилера (или анализатора перформанса) како би се пронашле секције програма који узимају највише ресурса —уско грло. Програмери понекад верују да имају идеју шта је уско грло, али интуиција је у доста случајева нетачна. Оптимизовање небитног дела кода ће често мало урадити да би се помогло око општег перформанса.
Када је уско грло локализовано, оптимизација обично почиње са промишљањем алгоритма који је коришћен у програму. Чешће да него не, одређени алгоритам може бити посебно прилагођен за одређени проблем, попуштање бољег перформанса него генерички алгоритам. На пример, задатак сортирања велике листе ставке је често извршена са квиксорт рутином, која је једна од најефикаснијих генеричних алгоритама. Али ако је нека карактеристика ставки искористива (на пример, већ су уређени у неком одређеном реду), друга метода се може користити, или чак произвољно-сачињена рутира сортирања.
Касније када је програмер са разлогом сигуран да је најбољи алгоритам селектован, оптимизација код може да почне. Петље се могу одвити (за ниже петље, иако ово често може довести до ниже брзине ако преоптерети кеш процесора), типове података што је мање могуће за коришћење, целобројни рачун се може користити уместо децималне-тачке, и тако даље.(Видети алгоритамску ефикасност артикал за ово и остале технике.)
Уска грла перформанса се могу јавити због ограничења језика пре него код алгоритама или структура података коришћених у програму. Понекад, критични део програма може бити поново исписан у другом програмском језику који даје директнији приступ основној машини. На пример, често је за веома високог-нивоа језике као што је Пајтон да има исписане модуле у С-у за већу брзину. Програми који су већи исписану у С-у могу имати модуле исписане у асемблеру. Програми исписани у Д-у могу користити инлајн асемблер.
Поновно писање секција "исплаћује се" у овим условима због генералног "правила палца" познатог као 90/10 закон, који каже да 90% времена потрошеног у 10% кода, и само 10% времена у осталих 90% кода. Тако да, улагање интелектуалног напора у оптимизовање само малог дела програма може имати велики утација на општу брзину — ако тачни делови могу бити лоцирани.
Мануална оптимизација понекад има контра ефекат нарушавајући читкост. Тако да оптимизације кода треба да буду опрезно документоване (препоручено користећи коментаре), и њихов ефекат треба бити у будућем развоју процењен.
Програм који извршава аутоматизовану оптимизацију се назива оптимизер. Већина оптимизера су убачени у компилаторе и раде током компилације. Оптимизери могу често кројити генералисани код за специфичне процесоре.
Данас, аутоматизоване оптимизације су скоро ексклузивно ограничене на оптимизацију компилатора. Међутим, зато што су компилаторске оптимизације обично ограничене на фиксирану групу радије генералних оптимизација, постоји разматрајући захтев за оптимизере који може прихватити описе проблема и језички-специфичне оптимизације, дозвољавајући инжењеру да специфира произвољне оптимизације. Алатке које прихватају описе оптимизација се називају програмске трансформације система и почињу да се примењују на праве системе софтвера као што је С++.T
Неки језици високог нивоа (Ајфел, Естерел) оптимизирају програме користећи међујезик.
Мрежно рачунање или расподељено израчунавање има циљ да оптимизира цео систем, померајући задатке са рачинара са високим коришћењем на рачунаре са idle временом.
Понекад, време потребно да се предузме оптимизација у томе сама од себе може представљати проблем.
Оптимизација постојећег кода обично не додаје нове карактеристике, и још горе, може додати нове багове у претходном коду (као што би било која промена могла). Зато што ручно оптимизирану код може неки пут имати слабију "читкост" од неоптимизованог кода, оптимизација може утицати на погодност одржавања такође. Оптимизација долази по са ценом и важно је бити сугран да је инвестиција вредна.
Аутоматски оптимизер (или оптимизација компилатора, програм који извршава оптимизацију кода) мора сам од себе да буде оптимизован, или да даље побољша ефикасност циљаних програма или да убрза сопствену операцију. Компилација извршена са оптимизацијом "укључена" обично траје дуже, иако је ово једини проблем када су програми прилично велики.
Посебно, за у-правом-тренутку компилаторе перформанс рантајм компајл компоненте, извршава се заједно са циљаним кодом, је кључ за повољшавање опште брзине извршења.
Seamless Wikipedia browsing. On steroids.
Every time you click a link to Wikipedia, Wiktionary or Wikiquote in your browser's search results, it will show the modern Wikiwand interface.
Wikiwand extension is a five stars, simple, with minimum permission required to keep your browsing private, safe and transparent.