Programoptimering - Program optimization
Inden for datalogi er programoptimering , kodeoptimering eller softwareoptimering processen med at ændre et softwaresystem for at få et eller andet aspekt af det til at fungere mere effektivt eller bruge færre ressourcer. Generelt kan et computerprogram optimeres, så det udføres hurtigere eller gør det i stand til at fungere med mindre hukommelseslagring eller andre ressourcer eller trække mindre strøm.
Generel
Selvom ordet "optimering" deler samme rod som "optimalt", er det sjældent, at optimeringsprocessen producerer et virkelig optimalt system. Et system kan generelt gøres optimalt ikke i absolutte tal, men kun med hensyn til en given kvalitetsmåling, hvilket kan være i kontrast til andre mulige metrik. Som følge heraf vil det optimerede system typisk kun være optimalt i én applikation eller for én målgruppe. Man kan reducere den tid, et program tager for at udføre en opgave til prisen for at få det til at forbruge mere hukommelse. I en applikation, hvor hukommelsesplads er til en overkommelig pris, kan man bevidst vælge en langsommere algoritme for at bruge mindre hukommelse. Ofte er der ikke noget "one size fits all" -design, der fungerer godt i alle tilfælde, så ingeniører foretager afvejninger for at optimere de attributter, der har størst interesse. Desuden er den indsats, der kræves for at gøre et stykke software helt optimalt - ude af stand til yderligere forbedringer, næsten altid mere end rimeligt for de fordele, der ville blive påløbet; så optimeringsprocessen kan standses, før en helt optimal løsning er nået. Heldigvis er det ofte sådan, at de største forbedringer kommer tidligt i processen.
Selv for en given kvalitetsmetrik (f.eks. Eksekveringshastighed) forbedrer de fleste optimeringsmetoder kun resultatet; de har ingen form for at producere optimalt output. Superoptimering er processen med at finde virkelig optimal output.
Optimeringsniveauer
Optimering kan forekomme på en række niveauer. Typisk har de højere niveauer større indflydelse og er sværere at ændre senere i et projekt, hvilket kræver betydelige ændringer eller en fuldstændig omskrivning, hvis de skal ændres. Således kan optimering typisk foregå via forfining fra højere til lavere, hvor de indledende gevinster er større og opnås med mindre arbejde, og senere gevinster er mindre og kræver mere arbejde. I nogle tilfælde afhænger den generelle ydeevne imidlertid af ydeevnen på meget lave niveauer af et program, og små ændringer på et sent tidspunkt eller tidlig overvejelse af detaljer på lavt niveau kan have stor effekt. Typisk tages der en vis overvejelse af effektivitet i hele et projekt - selvom dette varierer betydeligt - men større optimering betragtes ofte som en forfining, der skal foretages sent, hvis nogensinde. På længerevarende projekter er der typisk optimeringscykler, hvor forbedring af et område afslører begrænsninger i et andet, og disse er typisk indskrænket, når ydelsen er acceptabel, eller gevinster bliver for små eller dyre.
Da ydeevne er en del af specifikationen for et program-et program, der er ualmindeligt langsomt, er ikke egnet til formålet: et videospil med 60 Hz (billeder pr. Sekund) er acceptabelt, men 6 billeder i sekundet er uacceptabelt hakket- ydeevne er en overvejelse fra starten for at sikre, at systemet er i stand til at levere tilstrækkelig ydeevne, og tidlige prototyper skal have nogenlunde acceptabel ydeevne, for at der er tillid til, at det endelige system (med optimering) opnår acceptabel ydeevne. Dette er undertiden udeladt i den tro, at optimering altid kan foretages senere, hvilket resulterer i prototypesystemer, der er alt for langsomme - ofte af en størrelsesorden eller mere - og systemer, der i sidste ende er fejl, fordi de arkitektonisk ikke kan nå deres præstationsmål, f.eks. som Intel 432 (1981); eller dem, der tager mange års arbejde for at opnå acceptabel ydeevne, f.eks. Java (1995), som kun opnåede acceptabel ydeevne med HotSpot (1999). I hvilken grad ydeevnen ændres mellem prototype og produktionssystem, og hvor modtagelig den er til optimering, kan være en betydelig kilde til usikkerhed og risiko.
Design niveau
På det højeste niveau kan designet optimeres til bedst at udnytte de tilgængelige ressourcer, givne mål, begrænsninger og forventet brug/belastning. Det arkitektoniske design af et system påvirker overvældende dets ydeevne. For eksempel ville et system, der er netværkslatens-bundet (hvor netværkslatens er hovedbegrænsningen for den samlede ydelse) optimeres for at minimere netværksture, ideelt set foretage en enkelt anmodning (eller ingen anmodninger, som i en push-protokol ) frem for flere rundrejser. Valg af design afhænger af målene: Ved design af en kompilator , hvis hurtig kompilering er nøgleprioriteten, er en enpas-kompilator hurtigere end en multi-pass-kompilator (forudsat samme arbejde), men hvis outputkodens hastighed er målet, en langsommere multi-pass-compiler opfylder målet bedre, selvom det tager længere tid selv. Valg af platform og programmeringssprog forekommer på dette niveau, og ændring af dem kræver ofte en komplet omskrivning, selvom et modulsystem muligvis kun omskriver nogle komponenter-for eksempel kan et Python-program omskrive præstationskritiske sektioner i C. I en distribueret system, valg af arkitektur ( klient-server , peer-to-peer osv.) forekommer på designniveau og kan være svært at ændre, især hvis alle komponenter ikke kan udskiftes synkroniseret (f.eks. gamle klienter).
Algoritmer og datastrukturer
I betragtning af et overordnet design kommer et godt valg af effektive algoritmer og datastrukturer og effektiv implementering af disse algoritmer og datastrukturer derefter. Efter design påvirker valget af algoritmer og datastrukturer effektiviteten mere end noget andet aspekt af programmet. Generelt er datastrukturer sværere at ændre end algoritmer, da en datastrukturantagelse og dens præstationsforudsætninger bruges i hele programmet, selvom dette kan minimeres ved brug af abstrakte datatyper i funktionsdefinitioner og ved at holde de konkrete datastrukturdefinitioner begrænsede et par steder.
For algoritmer består dette primært af at sikre, at algoritmer er konstant O (1), logaritmisk O (log n ), lineær O ( n ) eller i nogle tilfælde log-lineær O ( n log n ) i input (begge i rummet og tid). Algoritmer med kvadratisk kompleksitet O ( n 2 ) undlader at skalere, og selv lineære algoritmer forårsager problemer, hvis de gentagne gange kaldes, og erstattes typisk med konstant eller logaritmisk, hvis det er muligt.
Ud over asymptotisk vækstorden er de konstante faktorer vigtige: En asymptotisk langsommere algoritme kan være hurtigere eller mindre (fordi enklere) end en asymptotisk hurtigere algoritme, når de begge står over for små input, hvilket kan være tilfældet, der forekommer i virkeligheden. Ofte vil en hybridalgoritme give den bedste ydeevne, på grund af at denne afvejning ændrer sig med størrelsen.
En generel teknik til at forbedre ydeevnen er at undgå arbejde. Et godt eksempel er brugen af en hurtig vej til almindelige sager, forbedring af ydeevnen ved at undgå unødvendigt arbejde. For eksempel ved at bruge en simpel tekstlayoutalgoritme til latinsk tekst, kun skifte til en kompleks layoutalgoritme for komplekse scripts, f.eks. Devanagari . En anden vigtig teknik er caching, især memoisering , som undgår redundante beregninger. På grund af vigtigheden af caching er der ofte mange niveauer af caching i et system, som kan forårsage problemer ved brug af hukommelse og problemer med korrekthed fra forældede cacher.
Kildekodeniveau
Udover generelle algoritmer og deres implementering på en abstrakt maskine kan konkrete valg af kildekodeniveau gøre en betydelig forskel. For eksempel på tidlige C -kompilatorer while(1)var det langsommere end for(;;)for en ubetinget sløjfe, fordi while(1)evalueret 1 og derefter havde et betinget spring, som testede om det var sandt, mens det for (;;)havde et ubetinget spring. Nogle optimeringer (som denne) kan i dag udføres ved at optimere kompilatorer . Dette afhænger af kildesproget, målmaskinsproget og kompilatoren og kan være både svært at forstå eller forudsige og ændres over tid; dette er et centralt sted, hvor forståelse af kompilatorer og maskinkode kan forbedre ydeevnen. Loop-invariant kodebevægelse og optimering af returværdi er eksempler på optimeringer, der reducerer behovet for hjælpevariabler og endda kan resultere i hurtigere ydeevne ved at undgå rundoptimeringer.
Byg niveau
Mellem kilde- og kompileringsniveau kan direktiver og buildflag bruges til at justere ydelsesmuligheder i henholdsvis kildekoden og kompilatoren, f.eks. Ved hjælp af præprocessor -defineringer til at deaktivere unødvendige softwarefunktioner, optimere til specifikke processormodeller eller hardwarefunktioner eller forudsige forgrening , for eksempel. Kildebaserede softwaredistributionssystemer som BSD 's Ports og Gentoo 's Portage kan drage fordel af denne form for optimering.
Kompilér niveau
Brug af en optimerende kompilator har en tendens til at sikre, at det eksekverbare program optimeres mindst lige så meget, som kompilatoren kan forudsige.
Monteringsniveau
På det laveste niveau kan skrive kode ved hjælp af et samlingssprog , der er designet til en bestemt hardwareplatform, producere den mest effektive og kompakte kode, hvis programmøren udnytter det fulde repertoire af maskininstruktioner . Mange operativsystemer, der bruges på integrerede systemer, er traditionelt blevet skrevet i assembler -kode af denne grund. Programmer (andre end meget små programmer) skrives sjældent fra start til slut i samlingen på grund af den tid og omkostninger, der er forbundet med det. De fleste er samlet fra et sprog på højt niveau til samling og håndoptimeret derfra. Når effektivitet og størrelse er mindre vigtige, kan store dele skrives på et sprog på højt niveau.
Med mere moderne optimeringskompilatorer og større kompleksitet i de seneste CPU'er er det sværere at skrive mere effektiv kode end hvad kompilatoren genererer, og få projekter har brug for dette "ultimative" optimeringstrin.
Meget kode skrevet i dag er beregnet til at køre på så mange maskiner som muligt. Som en konsekvens drager programmerere og kompilatorer ikke altid fordel af de mere effektive instruktioner fra nyere CPU'er eller finesser fra ældre modeller. Derudover kan samlingskode, der er indstillet til en bestemt processor uden brug af sådanne instruktioner, stadig være suboptimal på en anden processor og forvente en anden tuning af koden.
Typisk i dag frem for at skrive på samlingssprog, vil programmører bruge en adskiller til at analysere output fra en kompilator og ændre kildekoden på højt niveau, så den kan kompileres mere effektivt eller forstå, hvorfor den er ineffektiv.
Køretid
Just-in-time- kompilatorer kan producere tilpasset maskinkode baseret på data i løbetid, på bekostning af overhead til kompilering. Denne teknik stammer fra de tidligste regulære ekspressionsmotorer og er blevet udbredt med Java HotSpot og V8 til JavaScript. I nogle tilfælde kan adaptiv optimering muligvis udføre driftstidsoptimering, der overstiger statisk kompilers kapacitet ved dynamisk at justere parametre i henhold til det faktiske input eller andre faktorer.
Profilstyret optimering er en forudgående (AOT) kompilationsoptimeringsteknik baseret på køretidsprofiler og ligner en statisk "gennemsnitlig case" -analog til den dynamiske teknik til adaptiv optimering.
Selvmodificerende kode kan ændre sig selv som reaktion på driftstidsbetingelser for at optimere koden; dette var mere almindeligt i samlingsprogrammer.
Nogle CPU -designs kan udføre nogle optimeringer i løbetid. Nogle eksempler omfatter udførelse uden for ordre , spekulativ udførelse , instruktionsrørledninger og filialforudsigere . Kompilatorer kan hjælpe programmet med at drage fordel af disse CPU -funktioner, f.eks. Gennem instruktionsplanlægning .
Platformafhængige og uafhængige optimeringer
Kodeoptimering kan også bredt kategoriseres som platformafhængige og platformuafhængige teknikker. Selvom sidstnævnte er effektive på de fleste eller alle platforme, bruger platformafhængige teknikker specifikke egenskaber for en platform eller er afhængige af parametre afhængigt af den enkelte platform eller endda den enkelte processor. Derfor kan det være nødvendigt at skrive eller producere forskellige versioner af den samme kode til forskellige processorer. For eksempel i tilfælde af optimering på kompileringsniveau er platformuafhængige teknikker generiske teknikker (såsom sløjfeudrulning , reduktion i funktionsopkald, hukommelseseffektive rutiner, reduktion af forhold osv.), Der påvirker de fleste CPU-arkitekturer i en lignende vej. Et godt eksempel på platformuafhængig optimering er blevet vist med indre for loop, hvor det blev observeret, at en loop med en indre for loop udfører flere beregninger pr. Tidsenhed end en loop uden den eller en med en indre while loop. Generelt tjener disse til at reducere den samlede instruktionssti længde, der kræves for at fuldføre programmet og/eller reducere det samlede hukommelsesforbrug under processen. På den anden side involverer platformafhængige teknikker instruktionsplanlægning, parallelisme på instruktionsniveau, parallelisme på dataniveau, cacheoptimeringsteknikker (dvs. parametre, der adskiller sig fra forskellige platforme), og den optimale instruktionsplanlægning kan være anderledes, selv på forskellige processorer af samme arkitektur.
Styrke reduktion
Beregningsopgaver kan udføres på flere forskellige måder med varierende effektivitet. En mere effektiv version med tilsvarende funktionalitet er kendt som en styrkereduktion . Overvej f.eks. Følgende C -kodestykke, hvis hensigt er at opnå summen af alle heltal fra 1 til N :
int i, sum = 0;
for (i = 1; i <= N; ++i) {
sum += i;
}
printf("sum: %d\n", sum);
Denne kode kan (forudsat at der ikke er et aritmetisk overløb ) omskrives ved hjælp af en matematisk formel som:
int sum = N * (1 + N) / 2;
printf("sum: %d\n", sum);
Optimeringen, der undertiden udføres automatisk af en optimerende compiler, er at vælge en metode ( algoritme ), der er mere beregningsmæssigt effektiv, samtidig med at den bevarer den samme funktionalitet. Se algoritmisk effektivitet for en diskussion af nogle af disse teknikker. Imidlertid kan en betydelig forbedring af ydeevnen ofte opnås ved at fjerne ekstern funktionalitet.
Optimering er ikke altid en oplagt eller intuitiv proces. I eksemplet ovenfor kan den "optimerede" version faktisk være langsommere end den originale version, hvis N var tilstrækkeligt lille, og den særlige hardware tilfældigvis var meget hurtigere til at udføre addition og looping -operationer end multiplikation og division.
Afvejninger
I nogle tilfælde er optimering imidlertid afhængig af at bruge mere detaljerede algoritmer, brug af "special cases" og særlige "tricks" og udførelse af komplekse afvejninger. Et "fuldt optimeret" program kan være vanskeligere at forstå og kan derfor indeholde flere fejl end uoptimerede versioner. Ud over at eliminere indlysende antipatroner, reducerer nogle optimeringer på kodeniveau vedligeholdelsesevnen.
Optimering vil generelt fokusere på at forbedre kun et eller to aspekter af ydeevnen: udførelsestid, hukommelsesforbrug, diskplads, båndbredde, strømforbrug eller en anden ressource. Dette vil normalt kræve en afvejning-hvor en faktor er optimeret på andres bekostning. For eksempel øger størrelsen på cachen forbedret driftstid, men øger også hukommelsesforbruget. Andre almindelige afvejninger omfatter kodeklarhed og kortfattethed.
Der er tilfælde, hvor programmøren, der udfører optimeringen, skal beslutte at gøre softwaren bedre til nogle operationer, men på bekostning af at gøre andre operationer mindre effektive. Disse afvejninger kan undertiden være af ikke-teknisk karakter-f.eks. Når en konkurrent har offentliggjort et benchmarkresultat, der skal slåes for at forbedre kommerciel succes, men måske kommer med byrden ved at gøre normal brug af softwaren mindre effektiv. Sådanne ændringer kaldes undertiden i spøg som pessimiseringer .
Flaskehalse
Optimering kan omfatte at finde en flaskehals i et system - en komponent, der er den begrænsende faktor for ydeevne. Med hensyn til kode vil dette ofte være et hot spot - en kritisk del af koden, der er den primære forbruger af den nødvendige ressource - selvom det kan være en anden faktor, såsom I/O -latenstid eller netværksbåndbredde.
I datalogi, ressourceforbrug følger ofte en form for magt lov fordeling, og Pareto-princip kan anvendes på ressourceoptimering ved at observere, at 80% af de ressourcer, der typisk anvendes af 20% af operationerne. I software engineering er det ofte en bedre tilnærmelse, at 90% af udførelsestiden for et computerprogram bruges på at udføre 10% af koden (kendt som 90/10 loven i denne sammenhæng).
Mere komplekse algoritmer og datastrukturer fungerer godt med mange elementer, mens simple algoritmer er mere velegnede til små datamængder - opsætning, initialiseringstid og konstante faktorer for den mere komplekse algoritme kan opveje fordelen og dermed en hybrid algoritme eller adaptiv algoritme kan være hurtigere end nogen enkelt algoritme. En ydelsesprofil kan bruges til at indsnævre beslutninger om, hvilken funktionalitet der passer til hvilke betingelser.
I nogle tilfælde kan tilføjelse af mere hukommelse hjælpe med at få et program til at køre hurtigere. For eksempel vil et filtreringsprogram normalt læse hver linje og filtrere og udsende denne linje med det samme. Dette bruger kun tilstrækkelig hukommelse til en linje, men ydeevnen er typisk dårlig på grund af forsinkelsen for hver læst disk. Resultatet i cachelagring er på samme måde effektivt, men kræver også større hukommelsesbrug.
Hvornår skal man optimere
Optimering kan reducere læsbarheden og tilføje kode, der kun bruges til at forbedre ydelsen . Dette kan komplicere programmer eller systemer, hvilket gør dem sværere at vedligeholde og fejlsøge. Som et resultat udføres optimering eller performance tuning ofte i slutningen af udviklingsfasen .
Donald Knuth fremsatte følgende to udsagn om optimering:
"Vi bør glemme de små effektiviteter, siger cirka 97% af tiden: For tidlig optimering er roden til alt ondt. Alligevel bør vi ikke gå glip af vores muligheder i de kritiske 3%"
- (Han tilskrev også citatet til Tony Hoare flere år senere, selvom dette kunne have været en fejl, da Hoare afviser at have opfundet sætningen.)
"I etablerede ingeniørdiscipliner betragtes en forbedring på 12%, let opnået, aldrig som marginal, og jeg mener, at det samme synspunkt bør herske inden for software engineering"
"For tidlig optimering" er en sætning, der bruges til at beskrive en situation, hvor en programmør lader præstationsovervejelser påvirke designet af et stykke kode. Dette kan resultere i et design, der ikke er så rent, som det kunne have været, eller kode, der er forkert, fordi koden kompliceres af optimeringen, og programmøren bliver distraheret af optimering.
Når man beslutter, om man vil optimere en bestemt del af programmet, bør Amdahls lov altid overvejes: virkningen på det overordnede program afhænger meget af, hvor meget tid der faktisk bruges på den specifikke del, hvilket ikke altid er klart ved at se på koden uden en performance analyse .
En bedre tilgang er derfor først at designe, kode fra designet og derefter profilere / benchmark den resulterende kode for at se, hvilke dele der skal optimeres. Et enkelt og elegant design er ofte lettere at optimere på dette stadium, og profilering kan afsløre uventede ydelsesproblemer, som ikke ville have været løst ved for tidlig optimering.
I praksis er det ofte nødvendigt at have præstationsmål i tankerne, når softwaren først designes, men programmøren balancerer målene med design og optimering.
Moderne kompilatorer og operativsystemer er så effektive, at de tilsigtede ydelsesstigninger ofte ikke bliver til noget. Som et eksempel giver cachedata på applikationsniveau, der igen cachelagres på operativsystemniveau, ikke forbedringer i udførelsen. Alligevel er det et sjældent tilfælde, hvor programmøren fjerner mislykkede optimeringer fra produktionskoden. Det er også rigtigt, at fremskridt inden for hardware oftere end ikke fjerner eventuelle forbedringer, men alligevel vil den tilslørende kode fortsætte i fremtiden, længe efter at dens formål er blevet negeret.
Makroer
Optimering under kodeudvikling ved hjælp af makroer antager forskellige former på forskellige sprog.
I nogle proceduremæssige sprog, f.eks. C og C ++ , implementeres makroer ved hjælp af token -substitution. I dag kan inline -funktioner i mange tilfælde bruges som et typesikkert alternativ. I begge tilfælde kan det skråede funktionslegeme derefter gennemgå yderligere kompileringstidsoptimeringer af kompilatoren, herunder konstant foldning , hvilket kan flytte nogle beregninger til at kompilere tid.
I mange funktionelle programmeringssprog implementeres makroer ved hjælp af parse-time substitution af parse træer/abstrakte syntaks træer, hvilket det hævdes gør dem mere sikre at bruge. Da der i mange tilfælde bruges tolkning, er det en måde at sikre, at sådanne beregninger kun udføres på parse-tid, og nogle gange den eneste måde.
Lisp stammer fra denne makrostil, og sådanne makroer kaldes ofte "Lisp-lignende makroer." En lignende effekt kan opnås ved at bruge skabelonmetaprogrammering i C ++ .
I begge tilfælde flyttes arbejdet til kompileringstid. Forskellen mellem C- makroer på den ene side og Lisp-lignende makroer og C ++ skabelonmetaprogrammering på den anden side er, at sidstnævnte værktøjer tillader udførelse af vilkårlige beregninger ved kompileringstid/parse-tid, mens udvidelse af C- makroer ikke udfører nogen beregning og er afhængig af optimeringsevnen til at udføre den. Derudover understøtter C -makroer ikke direkte rekursion eller iteration , så Turing er ikke fuldført .
Som med enhver optimering er det dog ofte svært at forudsige, hvor sådanne værktøjer vil have størst indflydelse, før et projekt er færdigt.
Automatiseret og manuel optimering
Se også Kategori: Compiler -optimeringer
Optimering kan automatiseres af kompilatorer eller udføres af programmører. Gevinster er normalt begrænset til lokal optimering og større til globale optimeringer. Normalt er den mest effektive optimering at finde en overlegen algoritme .
Optimering af et helt system foretages normalt af programmører, fordi det er for komplekst til automatiserede optimeringsprogrammer. I denne situation ændrer programmører eller systemadministratorer eksplicit kode, så det overordnede system fungerer bedre. Selvom det kan producere bedre effektivitet, er det langt dyrere end automatiserede optimeringer. Da mange parametre påvirker programmets ydeevne, er programoptimeringsrummet stort. Meta-heuristik og maskinlæring bruges til at løse kompleksiteten af programoptimering.
Brug en profiler (eller ydelsesanalysator ) til at finde de sektioner i programmet, der tager flest ressourcer - flaskehalsen . Programmerere tror nogle gange, at de har en klar idé om, hvor flaskehalsen er, men intuition er ofte forkert. Optimering af et uvæsentligt stykke kode vil typisk ikke gøre meget for at hjælpe den samlede ydelse.
Når flaskehalsen er lokaliseret, starter optimering normalt med en nytænkning af algoritmen, der bruges i programmet. Oftere end ikke kan en bestemt algoritme skræddersyes specifikt til et bestemt problem, hvilket giver bedre ydeevne end en generisk algoritme. For eksempel udføres opgaven med at sortere en kæmpe liste med emner normalt med en quicksort -rutine , som er en af de mest effektive generiske algoritmer. Men hvis et eller andet kendetegn ved genstandene kan udnyttes (f.eks. Er de allerede arrangeret i en bestemt rækkefølge), kan en anden metode bruges eller endda en skræddersyet sorteringsrutine.
Efter at programmøren er rimelig sikker på, at den bedste algoritme er valgt, kan kodeoptimering starte. Loops kan afrulles (for lavere loop overhead, selvom dette ofte kan føre til lavere hastighed, hvis det overbelaster CPU-cachen ), kan datatyper så små som muligt bruges, heltal aritmetik kan bruges i stedet for floating-point osv. . (Se algoritmisk effektivitetsartikel for disse og andre teknikker.)
Ydeevne flaskehalse kan skyldes sprogbegrænsninger frem for algoritmer eller datastrukturer, der bruges i programmet. Nogle gange kan en kritisk del af programmet omskrives på et andet programmeringssprog, der giver mere direkte adgang til den underliggende maskine. For eksempel er det almindeligt , at sprog på meget højt niveau som Python har moduler skrevet i C for større hastighed. Programmer, der allerede er skrevet i C kan have moduler skrevet i samlingen . Programmer skrevet i D kan bruge den inline assembler .
Omskrivning af afsnit "betaler sig" under disse omstændigheder på grund af en generel " tommelfingerregel " kendt som 90/10 -loven , der siger, at 90% af tiden bruges i 10% af koden og kun 10% af tiden i de resterende 90% af koden. Så at sætte intellektuel indsats i at optimere kun en lille del af programmet kan have en enorm effekt på den samlede hastighed - hvis den / de korrekte dele kan findes.
Manuel optimering har undertiden den bivirkning, at det undergraver læsbarheden. Således bør kodeoptimeringer omhyggeligt dokumenteres (helst ved hjælp af in-line kommentarer), og deres effekt på fremtidig udvikling evalueres.
Det program, der udfører en automatiseret optimering, kaldes en optimizer . De fleste optimeringsprogrammer er integreret i kompilatorer og fungerer under kompilering. Optimizers kan ofte skræddersy den genererede kode til bestemte processorer.
I dag er automatiserede optimeringer næsten udelukkende begrænset til kompileroptimering . Men fordi kompileroptimeringer normalt er begrænset til et fast sæt af temmelig generelle optimeringer, er der stor efterspørgsel efter optimeringsprogrammer, der kan acceptere beskrivelser af problem- og sprogspecifikke optimeringer, så en ingeniør kan angive tilpassede optimeringer. Værktøjer, der accepterer beskrivelser af optimeringer, kaldes programtransformationssystemer og begynder at blive anvendt på rigtige softwaresystemer som C ++.
Nogle sprog på højt niveau ( Eiffel , Esterel ) optimerer deres programmer ved hjælp af et mellemliggende sprog .
Grid computing eller distribueret computing har til formål at optimere hele systemet ved at flytte opgaver fra computere med høj brug til computere med inaktiv tid.
Det tager tid at optimere
Nogle gange kan den tid, det tager at foretage optimering deri, være et problem.
Optimering af eksisterende kode tilføjer normalt ikke nye funktioner, og værre, det kan tilføje nye fejl i tidligere arbejdskode (som enhver ændring kan gøre). Fordi manuelt optimeret kode nogle gange kan have mindre "læsbarhed" end ikke -optimeret kode, kan optimering også påvirke vedligeholdelsen af den. Optimering har en pris, og det er vigtigt at være sikker på, at investeringen er værd.
En automatisk optimering (eller optimering af kompilatoren , et program, der udfører kodeoptimering), må muligvis selv optimeres, enten for yderligere at forbedre effektiviteten af sine målprogrammer eller for at fremskynde sin egen drift. En kompilering udført med optimering "slået til" tager normalt længere tid, selvom dette normalt kun er et problem, når programmer er ret store.
Især for just-in-time-kompilatorer er ydeevnen for driftstiden- kompileringskomponenten, der udføres sammen med dens målkode, nøglen til at forbedre den samlede eksekveringshastighed.
Referencer
- Jon Bentley : Writing Efficient Programs , ISBN 0-13-970251-2 .
- Donald Knuth : The Art of Computer Programming
eksterne links
- Sådan skriver du hurtig numerisk kode: En lille introduktion
- "Hvad enhver programmør bør vide om hukommelse" af Ulrich Drepper - forklarer strukturen i moderne hukommelsessystemer og foreslår, hvordan de kan udnyttes effektivt
- "Linux Multicore Performance Analysis and Optimization in a Nutshell" , præsentation dias af Philip Mucci
- Programmeringsoptimering af Paul Hsieh
- Skrive effektive programmer ("Bentleys regler") af Jon Bentley
- "Performance Anti-Patterns" af Bart Smaalders