Programoptimalisering - Program optimization

I informatikk er programoptimalisering , kodeoptimalisering eller programvareoptimalisering prosessen med å endre et programvaresystem for å få et eller annet aspekt av det til å fungere mer effektivt eller bruke færre ressurser. Generelt kan et dataprogram optimaliseres slik at det kjøres raskere, eller gjør det i stand til å operere med mindre lagringsplass eller andre ressurser, eller trekke mindre strøm.

Generell

Selv om ordet "optimalisering" deler samme rot som "optimal", er det sjelden at optimaliseringsprosessen produserer et virkelig optimalt system. Et system kan generelt gjøres optimalt ikke i absolutte termer, men bare med hensyn til en gitt kvalitetsberegning, som kan stå i kontrast med andre mulige beregninger. Som et resultat vil det optimaliserte systemet vanligvis bare være optimalt i én applikasjon eller for ett publikum. Man kan redusere tiden det tar et program å utføre en oppgave til en pris for å få det til å bruke mer minne. I en applikasjon der det er god plass på minne, kan man bevisst velge en tregere algoritme for å bruke mindre minne. Ofte er det ingen "one size fits all" -design som fungerer bra i alle tilfeller, så ingeniører tar avgjørelser for å optimalisere attributtene av størst interesse. I tillegg er innsatsen som kreves for å gjøre et programvare helt optimalt - ute av stand til ytterligere forbedringer, nesten alltid mer enn rimelig for fordelene som ville opparbeides; så optimaliseringsprosessen kan stoppes før en helt optimal løsning er nådd. Heldigvis er det ofte slik at de største forbedringene kommer tidlig i prosessen.

Selv for en gitt kvalitetsmåling (for eksempel kjøringshastighet), forbedrer de fleste optimaliseringsmetodene bare resultatet; de har ingen påstand om å produsere optimal utgang. Superoptimalisering er prosessen med å finne virkelig optimal utgang.

Nivåer for optimalisering

Optimalisering kan skje på en rekke nivåer. Vanligvis har de høyere nivåene større innvirkning, og er vanskeligere å endre senere i et prosjekt, noe som krever betydelige endringer eller en fullstendig omskriving hvis de må endres. Dermed kan optimalisering vanligvis fortsette via forfining fra høyere til lavere, med innledende gevinster som er større og oppnås med mindre arbeid, og senere gevinster er mindre og krever mer arbeid. I noen tilfeller er den generelle ytelsen imidlertid avhengig av ytelsen til deler på et svært lavt nivå av et program, og små endringer på et sent tidspunkt eller tidlig vurdering av detaljer på lavt nivå kan ha stor effekt. Vanligvis blir det tatt hensyn til effektivitet gjennom et prosjekt - selv om dette varierer betydelig - men større optimalisering anses ofte som en forbedring som skal gjøres sent, om noen gang. På prosjekter med lengre løp er det vanligvis optimaliseringssykluser, der forbedring av ett område avslører begrensninger i et annet, og disse reduseres vanligvis når ytelsen er akseptabel eller gevinsten blir for liten eller kostbar.

Siden ytelse er en del av spesifikasjonen til et program-et program som er uvanlig tregt, er ikke egnet for formålet: et videospill med 60 Hz (bilder per sekund) er akseptabelt, men 6 bilder per sekund er uakseptabelt hakket- ytelse er en vurdering fra starten, for å sikre at systemet er i stand til å levere tilstrekkelig ytelse, og tidlige prototyper må ha omtrent akseptabel ytelse for at det skal være tillit til at det siste systemet (med optimalisering) vil oppnå akseptabel ytelse. Dette er noen ganger utelatt i troen på at optimalisering alltid kan gjøres senere, noe som resulterer i prototypesystemer som er altfor trege - ofte av en størrelsesorden eller mer - og systemer som til slutt er feil fordi de arkitektonisk ikke kan oppnå prestasjonsmålene sine, slik som som Intel 432 (1981); eller de som tar mange års arbeid for å oppnå akseptabel ytelse, for eksempel Java (1995), som bare oppnådde akseptabel ytelse med HotSpot (1999). I hvilken grad ytelsen endres mellom prototype og produksjonssystem, og hvor tilpasset den er for optimalisering, kan være en betydelig kilde til usikkerhet og risiko.

Designnivå

På høyeste nivå kan designet bli optimalisert for å utnytte de tilgjengelige ressursene best mulig, gitt mål, begrensninger og forventet bruk/belastning. Den arkitektoniske utformingen av et system påvirker ytelsen overveldende. For eksempel vil et system som er nettverksforsinkelsesbundet (der nettverksforsinkelse er hovedbegrensningen for generell ytelse) optimaliseres for å minimere nettverksreiser, helst foreta en enkelt forespørsel (eller ingen forespørsler, som i en push-protokoll ) i stedet for flere tur -retur. Valg av design avhenger av målene: når du designer en kompilator , hvis rask kompilering er hovedprioriteten, er en en-pass-kompilator raskere enn en flerpas-kompilator (forutsatt samme arbeid), men hvis hastigheten på utgangskoden er målet, en tregere multi-pass kompilator oppfyller målet bedre, selv om det tar lengre tid selv. Valg av plattform og programmeringsspråk forekommer på dette nivået, og endring av dem krever ofte en fullstendig omskriving, selv om et modulsystem kan tillate omskriving av bare noen komponenter-for eksempel kan et Python-program skrive om ytelseskritiske seksjoner i C. I en distribuert system, valg av arkitektur ( klient-server , node-til-node , etc.) forekommer på designnivå, og kan være vanskelig å endre, spesielt hvis alle komponenter ikke kan erstattes synkronisert (f.eks. gamle klienter).

Algoritmer og datastrukturer

Gitt en overordnet design, kommer et godt valg av effektive algoritmer og datastrukturer , og effektiv implementering av disse algoritmene og datastrukturer. Etter design påvirker valg av algoritmer og datastrukturer effektiviteten mer enn noe annet aspekt av programmet. Vanligvis er datastrukturer vanskeligere å endre enn algoritmer, ettersom en datastrukturantagelse og dens forutsetninger om ytelse brukes i hele programmet, selv om dette kan minimeres ved bruk av abstrakte datatyper i funksjonsdefinisjoner, og ved å holde de konkrete datastrukturdefinisjonene begrenset til noen få steder.

For algoritmer består dette først og fremst av å sikre at algoritmene er konstant O (1), logaritmisk O (log n ), lineær O ( n ), eller i noen tilfeller log-lineær O ( n log n ) i inngangen (begge i rommet og tid). Algoritmer med kvadratisk kompleksitet O ( n 2 ) klarer ikke å skalere, og til og med lineære algoritmer forårsaker problemer hvis de kalles gjentatte ganger, og erstattes vanligvis med konstant eller logaritmisk hvis mulig.

Utover asymptotisk vekstrekkefølge, betyr de konstante faktorene: en asymptotisk tregere algoritme kan være raskere eller mindre (fordi enklere) enn en asymptotisk raskere algoritme når de begge står overfor små input, noe som kan være tilfellet som skjer i virkeligheten. Ofte vil en hybridalgoritme gi den beste ytelsen, på grunn av at denne kompromissen endres med størrelsen.

En generell teknikk for å forbedre ytelsen er å unngå arbeid. Et godt eksempel er bruk av en rask bane for vanlige saker, forbedring av ytelsen ved å unngå unødvendig arbeid. For eksempel ved å bruke en enkel tekstlayoutalgoritme for latinsk tekst, bare bytte til en kompleks layoutalgoritme for komplekse skript, for eksempel Devanagari . En annen viktig teknikk er hurtigbufring, spesielt memoisering , som unngår overflødige beregninger. På grunn av viktigheten av hurtigbufring, er det ofte mange nivåer av hurtigbufring i et system, noe som kan forårsake problemer fra minnebruk og problemer med korrekthet fra foreldede cacher.

Kildekodenivå

Utover generelle algoritmer og implementering av dem på en abstrakt maskin, kan konkrete kildekodenivåvalg gjøre en betydelig forskjell. For eksempel, på tidlige C -kompilatorer, while(1)var tregere enn for(;;)for en ubetinget sløyfe, fordi while(1)evaluert 1 og deretter hadde et betinget hopp som testet om det var sant, mens det for (;;)hadde et ubetinget hopp. Noen optimaliseringer (som denne) kan i dag utføres ved å optimalisere kompilatorer . Dette avhenger av kildespråket, målmaskinspråket og kompilatoren, og kan være både vanskelig å forstå eller forutsi og endres over tid; Dette er et sentralt sted hvor forståelse av kompilatorer og maskinkode kan forbedre ytelsen. Loop-invariant kodebevegelse og returverdioptimalisering er eksempler på optimaliseringer som reduserer behovet for tilleggsvariabler og kan til og med resultere i raskere ytelse ved å unngå runde-om-optimaliseringer.

Bygg nivå

Mellom kilde- og kompileringsnivå kan direktiver og build -flagg brukes til å justere ytelsesalternativer i henholdsvis kildekoden og kompilatoren, for eksempel å bruke forhåndsbehandlingsdefinisjoner for å deaktivere unødvendige programvarefunksjoner, optimalisere for spesifikke prosessormodeller eller maskinvarefunksjoner, eller forutsi forgrening , for eksempel. Kildebaserte programvaredistribusjonssystemer som BSD 's Ports og Gentoo 's Portage kan dra fordel av denne formen for optimalisering.

Kompilere nivå

Bruk av en optimaliserende kompilator har en tendens til å sikre at det kjørbare programmet er optimalisert minst så mye som kompilatoren kan forutsi.

Monteringsnivå

På det laveste nivået kan det å skrive kode ved hjelp av et monteringsspråk , designet for en bestemt maskinvareplattform, produsere den mest effektive og kompakte koden hvis programmereren utnytter hele repertoaret av maskininstruksjoner . Mange operativsystemer som brukes på innebygde systemer har tradisjonelt blitt skrevet i assembler -kode av denne grunn. Programmer (andre enn veldig små programmer) skrives sjelden fra start til slutt i montering på grunn av tiden og kostnadene. De fleste er samlet ned fra et høyt språk til montering og håndoptimalisert derfra. Når effektivitet og størrelse er mindre viktig, kan store deler skrives på et språk på høyt nivå.

Med mer moderne optimaliseringskompilatorer og større kompleksitet i nyere CPUer , er det vanskeligere å skrive mer effektiv kode enn det kompilatoren genererer, og få prosjekter trenger dette "ultimate" optimaliseringstrinnet.

Mye kode skrevet i dag er ment å kjøre på så mange maskiner som mulig. Som en konsekvens benytter programmerere og kompilatorer ikke alltid de mer effektive instruksjonene fra nyere CPUer eller finesser fra eldre modeller. I tillegg kan monteringskode som er innstilt for en bestemt prosessor uten å bruke slike instruksjoner, fortsatt være suboptimal på en annen prosessor, og forvente en annen innstilling av koden.

Vanligvis i dag i stedet for å skrive på samlingsspråk, vil programmerere bruke en demonteringsanordning til å analysere utdataene fra en kompilator og endre kildekoden på høyt nivå slik at den kan kompileres mer effektivt, eller forstå hvorfor den er ineffektiv.

Kjøretid

Just-in-time kompilatorer kan produsere tilpasset maskinkode basert på kjøretidsdata, på bekostning av kompileringskostnader. Denne teknikken dateres til de tidligste regulære uttrykksmotorene , og har blitt utbredt med Java HotSpot og V8 for JavaScript. I noen tilfeller kan adaptiv optimalisering kunne utføre driftstidoptimalisering som overstiger kapasiteten til statiske kompilatorer ved dynamisk å justere parametere i henhold til den faktiske inngangen eller andre faktorer.

Profilstyrt optimalisering er en teknikk for optimalisering av kompilering på forhånd (AOT) basert på kjøretidsprofiler, og ligner en statisk "gjennomsnittlig case" -analog for den dynamiske teknikken for adaptiv optimalisering.

Selvmodifiserende kode kan endre seg som svar på driftstidens forhold for å optimalisere koden; dette var mer vanlig i samlingsspråklige programmer.

Noen CPU -design kan utføre noen optimaliseringer under kjøretid. Noen eksempler inkluderer utførelse uten ordre , spekulativ utførelse , instruksjonsrørledninger og grenforutsigere . Kompilatorer kan hjelpe programmet med å dra fordel av disse CPU -funksjonene, for eksempel gjennom planlegging av instruksjoner .

Plattformavhengige og uavhengige optimaliseringer

Kodeoptimalisering kan også i stor grad kategoriseres som plattformavhengige og plattformuavhengige teknikker. Selv om de sistnevnte er effektive på de fleste eller alle plattformer, bruker plattformavhengige teknikker spesifikke egenskaper til en plattform, eller er avhengige av parametere avhengig av den ene plattformen eller til og med den enkelte prosessoren. Det kan derfor være nødvendig å skrive eller produsere forskjellige versjoner av den samme koden for forskjellige prosessorer. For eksempel, når det gjelder optimalisering på kompileringsnivå, er plattformuavhengige teknikker generiske teknikker (for eksempel sløyfeutrulling , reduksjon i funksjonsanrop, minneeffektive rutiner, reduksjon av forhold, etc.), som påvirker de fleste CPU-arkitekturer i lignende vei. Et godt eksempel på plattformuavhengig optimalisering har blitt vist med indre for sløyfe, hvor det ble observert at en sløyfe med en indre for sløyfe utfører flere beregninger per tidsenhet enn en sløyfe uten den eller en med en indre mensløkke. Vanligvis tjener disse til å redusere den totale instruksjonsbanelengden som kreves for å fullføre programmet og/eller redusere total minnebruk under prosessen. På den annen side innebærer plattformavhengige teknikker instruksjonsplanlegging, parallellisme på instruksjonsnivå, parallellitet på datanivå, cache-optimaliseringsteknikker (dvs. parametere som er forskjellige mellom forskjellige plattformer) og optimal instruksjonsplanlegging kan være annerledes, selv på forskjellige prosessorer i samme arkitektur.

Styrkereduksjon

Beregningsoppgaver kan utføres på flere forskjellige måter med varierende effektivitet. En mer effektiv versjon med tilsvarende funksjonalitet er kjent som en styrkereduksjon . Vurder for eksempel følgende C -kodebit som har til hensikt å få summen av alle heltall fra 1 til N :

int i, sum = 0;
for (i = 1; i <= N; ++i) {
  sum += i;
}
printf("sum: %d\n", sum);

Denne koden kan (forutsatt at ingen aritmetisk overløp ) skrives om ved hjelp av en matematisk formel som:

int sum = N * (1 + N) / 2;
printf("sum: %d\n", sum);

Optimaliseringen, noen ganger utført automatisk av en optimaliserende kompilator, er å velge en metode ( algoritme ) som er mer beregningsmessig effektiv, samtidig som den beholder den samme funksjonaliteten. Se algoritmisk effektivitet for en diskusjon av noen av disse teknikkene. Imidlertid kan en betydelig forbedring av ytelsen ofte oppnås ved å fjerne ekstern funksjonalitet.

Optimalisering er ikke alltid en åpenbar eller intuitiv prosess. I eksemplet ovenfor kan den "optimaliserte" versjonen faktisk være tregere enn den opprinnelige versjonen hvis N var tilstrekkelig liten og den spesielle maskinvaren tilfeldigvis var mye raskere til å utføre tilleggs- og looping -operasjoner enn multiplikasjon og divisjon.

Avveininger

I noen tilfeller er optimalisering imidlertid avhengig av å bruke mer forseggjorte algoritmer, bruk av "spesialtilfeller" og spesielle "triks" og utførelse av komplekse avveier. Et "fullt optimalisert" program kan være vanskeligere å forstå og kan derfor inneholde flere feil enn uoptimaliserte versjoner. Utover å eliminere åpenbare antipatroner, reduserer noen optimaliseringer av kodenivå vedlikeholdsevnen.

Optimalisering vil generelt fokusere på å forbedre bare ett eller to aspekter ved ytelse: utførelsestid, minnebruk, diskplass, båndbredde, strømforbruk eller annen ressurs. Dette vil vanligvis kreve en avveining-der en faktor er optimalisert på bekostning av andre. For eksempel forbedrer størrelsen på hurtigbufferen ytelsen til kjøretiden, men øker også minneforbruket. Andre vanlige avveininger inkluderer klarhet i koden og konsistensen.

Det er tilfeller der programmereren som utfører optimaliseringen må bestemme seg for å gjøre programvaren bedre for noen operasjoner, men på bekostning av å gjøre andre operasjoner mindre effektive. Disse avveiningene kan noen ganger være av ikke-teknisk art-for eksempel når en konkurrent har publisert et referanseresultat som må slås for å forbedre kommersiell suksess, men kanskje kommer med byrden av å gjøre normal bruk av programvaren mindre effektiv. Slike endringer blir noen ganger spøkefullt referert til som pessimiseringer .

Flaskehalser

Optimalisering kan omfatte å finne en flaskehals i et system - en komponent som er den begrensende faktoren for ytelse. Når det gjelder kode, vil dette ofte være et hot spot  - en kritisk del av koden som er hovedforbrukeren av den nødvendige ressursen - selv om det kan være en annen faktor, for eksempel I/O -latens eller nettverksbåndbredde.

I informatikk, ressursforbruk følger ofte en form for makt lov distribusjon, og Pareto prinsippet kan brukes på ressursoptimalisering ved å observere at 80% av ressursene brukes vanligvis av 20% av operasjonene. I programvareteknikk er det ofte en bedre tilnærming at 90% av utførelsestiden til et dataprogram brukes på å utføre 10% av koden (kjent som 90/10 -loven i denne sammenhengen).

Mer komplekse algoritmer og datastrukturer fungerer godt med mange elementer, mens enkle algoritmer er mer egnet for små datamengder - oppsett, initialiseringstid og konstante faktorer for den mer komplekse algoritmen kan oppveie fordelen, og dermed en hybridalgoritme eller adaptiv algoritmen kan være raskere enn noen enkelt algoritme. En ytelsesprofil kan brukes til å begrense beslutninger om hvilken funksjonalitet som passer til hvilke forhold.

I noen tilfeller kan det å legge til mer minne bidra til å få et program til å kjøre raskere. For eksempel vil et filtreringsprogram vanligvis lese hver linje og filtrere og sende ut den linjen umiddelbart. Dette bruker bare nok minne til en linje, men ytelsen er vanligvis dårlig på grunn av ventetiden til hver disk som er lest. Lagring av resultatet er like effektivt, men krever også større minnebruk.

Når skal man optimalisere

Optimalisering kan redusere lesbarheten og legge til kode som bare brukes til å forbedre ytelsen . Dette kan komplisere programmer eller systemer, noe som gjør dem vanskeligere å vedlikeholde og feilsøke. Som et resultat utføres optimalisering eller ytelsesjustering ofte på slutten av utviklingsstadiet .

Donald Knuth kom med følgende to uttalelser om optimalisering:

"Vi bør glemme små effektiviteter, si omtrent 97% av tiden: for tidlig optimalisering er roten til alt ondt. Likevel bør vi ikke gå glipp av mulighetene våre i de kritiske 3%"

(Han tilskrev også sitatet til Tony Hoare flere år senere, selv om dette kan ha vært en feil da Hoare fraskriver seg å ha laget uttrykket.)

"I etablerte ingeniørfag blir en forbedring på 12%, lett oppnådd, aldri betraktet som marginal, og jeg tror det samme synspunktet bør gjelde innen programvareingeniør"

"For tidlig optimalisering" er et uttrykk som brukes for å beskrive en situasjon der en programmerer lar ytelseshensyn påvirke utformingen av et stykke kode. Dette kan resultere i et design som ikke er så rent som det kunne ha vært eller feil kode, fordi koden kompliseres av optimaliseringen og programmereren blir distrahert av optimalisering.

Når du skal bestemme om du vil optimalisere en bestemt del av programmet, bør Amdahls lov alltid vurderes: virkningen på det overordnede programmet avhenger veldig av hvor mye tid som faktisk blir brukt i den spesifikke delen, noe som ikke alltid er klart fra å se på koden uten en prestasjonsanalyse .

En bedre tilnærming er derfor å designe først, kode fra designet og deretter profilere / referere den resulterende koden for å se hvilke deler som bør optimaliseres. En enkel og elegant design er ofte lettere å optimalisere på dette stadiet, og profilering kan avsløre uventede ytelsesproblemer som ikke ville blitt løst ved for tidlig optimalisering.

I praksis er det ofte nødvendig å ha ytelsesmål i tankene når du først designer programvare, men programmereren balanserer målene med design og optimalisering.

Moderne kompilatorer og operativsystemer er så effektive at den tiltenkte ytelsen øker ofte ikke. Som et eksempel gir ikke bufferlagring av data på applikasjonsnivå som igjen blir bufret på operativsystemnivå forbedringer i utførelsen. Likevel er det et sjeldent tilfelle når programmereren vil fjerne mislykkede optimaliseringer fra produksjonskoden. Det er også sant at fremskritt innen maskinvare oftere enn ikke vil unngå potensielle forbedringer, men den skjulende koden vil fortsette inn i fremtiden lenge etter at formålet er blitt negert.

Makroer

Optimalisering under kodeutvikling ved bruk av makroer tar forskjellige former på forskjellige språk.

På noen prosessuelle språk, for eksempel C og C ++ , implementeres makroer med token -substitusjon. I dag kan inline -funksjoner i mange tilfeller brukes som et trygt alternativ. I begge tilfeller kan det skråstilte funksjonsorganet deretter gjennomgå ytterligere kompileringstidoptimaliseringer fra kompilatoren, inkludert konstant folding , noe som kan flytte noen beregninger til å kompilere tid.

I mange funksjonelle programmeringsspråk er makroer implementert ved bruk av parse-time substitusjon av analysetrær/abstrakte syntakstrær, som det hevdes gjør dem tryggere å bruke. Siden tolkning i mange tilfeller brukes, er det en måte å sikre at slike beregninger bare utføres ved parse-tid, og noen ganger den eneste måten.

Lisp stammer fra denne makrostilen, og slike makroer kalles ofte "Lisp-lignende makroer." En lignende effekt kan oppnås ved å bruke malmetaprogrammering i C ++ .

I begge tilfeller flyttes arbeidet til kompileringstid. Forskjellen mellom C- makroer på den ene siden, og Lisp-lignende makroer og C ++- metaprogrammering på den andre siden, er at sistnevnte verktøy tillater utførelse av vilkårlige beregninger ved kompileringstid/parse-tid, mens utvidelse av C- makroer ikke utfører noen beregning, og er avhengig av optimaliseringsevnen for å utføre den. I tillegg støtter ikke C -makroer rekursjon eller iterasjon direkte , så Turing er ikke fullført .

Som med all optimalisering er det imidlertid ofte vanskelig å forutsi hvor slike verktøy vil ha størst effekt før et prosjekt er ferdig.

Automatisert og manuell optimalisering

Se også Kategori: Kompilatoroptimaliseringer

Optimalisering kan automatiseres av kompilatorer eller utføres av programmerere. Gevinster er vanligvis begrenset for lokal optimalisering, og større for globale optimaliseringer. Vanligvis er den kraftigste optimaliseringen å finne en overlegen algoritme .

Å optimere et helt system utføres vanligvis av programmerere fordi det er for komplekst for automatiserte optimatorer. I denne situasjonen endrer programmerere eller systemadministratorer eksplisitt kode slik at det generelle systemet fungerer bedre. Selv om det kan gi bedre effektivitet, er det langt dyrere enn automatiserte optimaliseringer. Siden mange parametere påvirker programmets ytelse, er programoptimaliseringsområdet stort. Meta-heuristikk og maskinlæring brukes for å løse kompleksiteten i programoptimalisering.

Bruk en profiler (eller ytelsesanalysator ) for å finne delene av programmet som tar mest ressurser - flaskehalsen . Programmerere tror noen ganger at de har en klar ide om hvor flaskehalsen er, men intuisjon er ofte feil. Å optimalisere et uviktig stykke kode vil vanligvis gjøre lite for å hjelpe den generelle ytelsen.

Når flaskehalsen er lokalisert, starter optimalisering vanligvis med en revurdering av algoritmen som brukes i programmet. Oftere enn ikke kan en bestemt algoritme tilpasses spesielt et bestemt problem, noe som gir bedre ytelse enn en generisk algoritme. For eksempel blir oppgaven med å sortere en enorm liste med elementer vanligvis utført med en quicksort -rutine, som er en av de mest effektive generiske algoritmene. Men hvis noen av egenskapene til elementene er utnyttbare (for eksempel at de allerede er ordnet i en bestemt rekkefølge), kan en annen metode brukes, eller til og med en skreddersydd sorteringsrutine.

Etter at programmereren er rimelig sikker på at den beste algoritmen er valgt, kan kodeoptimalisering starte. Sløyfer kan rulles ut (for lavere sløyfe overhead, selv om dette ofte kan føre til lavere hastighet hvis det overbelaster CPU-bufferen ), kan datatyper så små som mulig brukes, heltallsregning kan brukes i stedet for flytende punkt, og så videre . (Se algoritmisk effektivitetsartikkel for disse og andre teknikker.)

Ytelsesflaskehalser kan skyldes språkbegrensninger i stedet for algoritmer eller datastrukturer som brukes i programmet. Noen ganger kan en kritisk del av programmet skrives om på et annet programmeringsspråk som gir mer direkte tilgang til den underliggende maskinen. For eksempel er det vanlig at språk på svært høyt nivå som Python har moduler skrevet i C for større hastighet. Programmer som allerede er skrevet i C kan ha moduler skrevet i forsamlingen . Programmer skrevet i D kan bruke den innebygde montøren .

Omskriving av seksjoner "lønner seg" under disse omstendighetene på grunn av en generell " tommelfingerregel " kjent som 90/10 -loven , som sier at 90% av tiden brukes i 10% av koden, og bare 10% av tiden i de resterende 90% av koden. Så å sette intellektuell innsats i å optimalisere bare en liten del av programmet kan ha stor effekt på den totale hastigheten - hvis den riktige delen (e) kan bli funnet.

Noen ganger har manuell optimalisering bivirkningen av å undergrave lesbarheten. Derfor bør kodeoptimaliseringer dokumenteres nøye (helst ved hjelp av kommentarer på linje), og deres effekt på fremtidig utvikling evalueres.

Programmet som utfører en automatisert optimalisering kalles en optimizer . De fleste optimatorer er innebygd i kompilatorer og fungerer under kompilering. Optimizers kan ofte skreddersy den genererte koden til spesifikke prosessorer.

I dag er automatiserte optimaliseringer nesten utelukkende begrenset til kompilatoroptimalisering . Fordi kompilatoroptimaliseringer vanligvis er begrenset til et fast sett med ganske generelle optimaliseringer, er det stor etterspørsel etter optimatorer som kan godta beskrivelser av problem- og språkspesifikke optimaliseringer, slik at en ingeniør kan angi tilpassede optimaliseringer. Verktøy som godtar beskrivelser av optimaliseringer kalles programtransformasjonssystemer og begynner å bli brukt på ekte programvaresystemer som C ++.

Noen språk på høyt nivå ( Eiffel , Esterel ) optimaliserer programmene sine ved å bruke et mellomspråk .

Grid computing eller distribuert databehandling har som mål å optimalisere hele systemet, ved å flytte oppgaver fra datamaskiner med høy bruk til datamaskiner med inaktiv tid.

Det tok tid for optimalisering

Noen ganger kan tiden det tar å foreta optimalisering deri være et problem.

Å optimalisere eksisterende kode legger vanligvis ikke til nye funksjoner, og verre, det kan legge til nye feil i tidligere arbeidskode (som enhver endring kan gjøre). Fordi manuelt optimalisert kode noen ganger kan ha mindre "lesbarhet" enn uoptimalisert kode, kan optimalisering også påvirke vedlikeholdbarheten av den. Optimalisering har en pris, og det er viktig å være sikker på at investeringen er verdt.

En automatisk optimering (eller optimaliseringskompilator , et program som utfører kodeoptimalisering) må kanskje selv optimaliseres, enten for å forbedre effektiviteten til målprogrammene ytterligere eller for å øke hastigheten på egen drift. En samling utført med optimalisering "slått på" tar vanligvis lengre tid, selv om dette vanligvis bare er et problem når programmer er ganske store.

Spesielt for just-in-time-kompilatorer er ytelsen til kjøretidskompileringskomponenten , som kjøres sammen med sin målkode, nøkkelen til å forbedre den totale kjøringshastigheten.

Referanser

  • Jon Bentley : Writing Efficient Programs , ISBN  0-13-970251-2 .
  • Donald Knuth : The Art of Computer Programming

Eksterne linker