Programoptimering - Program optimization

Inom datavetenskap är programoptimering , kodoptimering eller mjukvaruoptimering processen att modifiera ett mjukvarusystem för att få en del av det att fungera mer effektivt eller använda färre resurser. I allmänhet kan ett datorprogram optimeras så att det körs snabbare, eller för att det ska fungera med mindre minneslagring eller andra resurser, eller dra mindre ström.

Allmän

Även om ordet "optimering" har samma rot som "optimalt", är det sällsynt att optimeringsprocessen ger ett riktigt optimalt system. Ett system kan generellt göras optimalt inte i absoluta termer, utan endast med avseende på en given kvalitetsmätning, vilket kan stå i kontrast med andra möjliga mätvärden. Som ett resultat kommer det optimerade systemet vanligtvis bara att vara optimalt i en applikation eller för en publik. Man kan minska den tid som ett program tar för att utföra en uppgift till priset av att få det att konsumera mer minne. I en applikation där minnesutrymmet är högt kan man medvetet välja en långsammare algoritm för att använda mindre minne. Ofta finns det ingen "one size fits all" -design som fungerar bra i alla fall, så ingenjörer gör avvägningar för att optimera attributen av störst intresse. Dessutom är ansträngningen som krävs för att göra en mjukvara helt optimal - oförmögen till ytterligare förbättringar - nästan alltid mer än rimligt för de fördelar som skulle uppkomma; så optimeringsprocessen kan stoppas innan en helt optimal lösning har uppnåtts. Lyckligtvis är det ofta så att de största förbättringarna kommer tidigt i processen.

Även för en given kvalitetsmätning (t.ex. exekveringshastighet) förbättrar de flesta optimeringsmetoder bara resultatet; de har ingen anspråk på att producera optimal effekt. Superoptimering är processen för att hitta verkligt optimal produktion.

Optimeringsnivåer

Optimering kan ske på ett antal nivåer. Vanligtvis har de högre nivåerna större inverkan och är svårare att ändra senare i ett projekt, vilket kräver betydande förändringar eller en fullständig omskrivning om de behöver ändras. Således kan optimering vanligtvis fortsätta via förfining från högre till lägre, med initiala vinster som är större och uppnås med mindre arbete, och senare vinster är mindre och kräver mer arbete. Men i vissa fall beror den övergripande prestandan på prestanda för mycket låga delar av ett program, och små förändringar i ett sent skede eller tidigt övervägande av detaljer på låg nivå kan ha stor påverkan. Normalt beaktas effektivitet under ett projekt - även om detta varierar kraftigt - men större optimering anses ofta vara en förfining som ska göras sent, om någonsin. På projekt med längre löpning finns det vanligtvis optimeringscykler, där förbättring av ett område avslöjar begränsningar i ett annat, och dessa begränsas vanligtvis när prestanda är acceptabel eller vinster blir för små eller dyra.

Eftersom prestanda är en del av specifikationen för ett program-ett program som är ovanligt långsamt är inte lämpligt för syftet: ett tv-spel med 60 Hz (bildrutor per sekund) är acceptabelt, men 6 bilder per sekund är oacceptabelt hackigt- prestanda är ett övervägande från början, för att säkerställa att systemet kan leverera tillräcklig prestanda, och tidiga prototyper måste ha ungefär acceptabel prestanda för att det ska finnas förtroende för att det slutliga systemet (med optimering) kommer att uppnå acceptabel prestanda. Detta utelämnas ibland i tron ​​att optimering alltid kan göras senare, vilket resulterar i prototypsystem som är alldeles för långsamma - ofta av en storleksordning eller mer - och system som i slutändan är misslyckanden eftersom de arkitektoniskt inte kan uppnå sina prestationsmål, t.ex. som Intel 432 (1981); eller sådana som tar år av arbete för att uppnå acceptabel prestanda, till exempel Java (1995), som endast uppnådde acceptabel prestanda med HotSpot (1999). I vilken utsträckning prestanda förändras mellan prototyp och produktionssystem, och hur mottaglig det är för optimering, kan vara en betydande källa till osäkerhet och risk.

Designnivå

På högsta nivå kan designen optimeras för att bäst utnyttja tillgängliga resurser, givna mål, begränsningar och förväntad användning/belastning. Den arkitektoniska utformningen av ett system påverkar överväldigande dess prestanda. Till exempel skulle ett system som är nätverkslatensbundet (där nätverkslatens är huvudbegränsningen för övergripande prestanda) optimeras för att minimera nätverksresor, helst göra en enda begäran (eller inga förfrågningar, som i ett push-protokoll ) snarare än flera Rundturer. Valet av design beror på målen: när en kompilator designas , om snabb kompilering är nyckelprioriteringen, är enpassningskompilatorn snabbare än en flerpassskompilator (förutsatt samma arbete), men om utmatningskodens hastighet är målet, en långsammare multi-pass-kompilator uppfyller målet bättre, även om det tar längre tid själv. Val av plattform och programmeringsspråk sker på denna nivå, och att ändra dem kräver ofta en fullständig omskrivning, även om ett modulsystem endast tillåter omskrivning av en del komponenter-till exempel kan ett Python-program skriva om prestationskritiska avsnitt i C. I en distribuerad system, val av arkitektur ( klient-server , peer-to-peer , etc.) sker på designnivå och kan vara svårt att ändra, särskilt om alla komponenter inte kan ersättas i synkronisering (t.ex. gamla klienter).

Algoritmer och datastrukturer

Med tanke på en övergripande design kommer ett bra val av effektiva algoritmer och datastrukturer och effektiv implementering av dessa algoritmer och datastrukturer nästa. Efter design påverkar valet av algoritmer och datastrukturer effektiviteten mer än någon annan aspekt av programmet. Generellt är datastrukturer svårare att ändra än algoritmer, eftersom ett datastrukturantagande och dess prestationsantaganden används i hela programmet, även om detta kan minimeras genom användning av abstrakta datatyper i funktionsdefinitioner och att hålla de konkreta datastrukturdefinitionerna begränsade till några ställen.

För algoritmer består detta främst av att se till att algoritmer är konstant O (1), logaritmisk O (log n ), linjär O ( n ) eller i vissa fall log-linjär O ( n log n ) i ingången (både i rymden och tid). Algoritmer med kvadratisk komplexitet O ( n 2 ) misslyckas med att skala, och även linjära algoritmer orsakar problem om de upprepade gånger anropas och ersätts vanligtvis med konstant eller logaritmisk om möjligt.

Utöver asymptotisk tillväxtordning spelar de konstanta faktorerna roll: en asymptotiskt långsammare algoritm kan vara snabbare eller mindre (eftersom den är enklare) än en asymptotiskt snabbare algoritm när de båda står inför små input, vilket kan vara fallet som uppstår i verkligheten. Ofta ger en hybridalgoritm bästa prestanda, på grund av att denna avvägning förändras med storleken.

En allmän teknik för att förbättra prestanda är att undvika arbete. Ett bra exempel är att använda en snabb väg för vanliga fall, förbättra prestanda genom att undvika onödigt arbete. Till exempel att använda en enkel textlayoutalgoritm för latinsk text, bara växla till en komplex layoutalgoritm för komplexa skript, till exempel Devanagari . En annan viktig teknik är cachning, särskilt memoization , som undviker redundanta beräkningar. På grund av vikten av cachning finns det ofta många nivåer av cachning i ett system, vilket kan orsaka problem med minnesanvändning och problem med korrekthet från inaktuella cacheminnen.

Källkodsnivå

Utöver allmänna algoritmer och deras implementering på en abstrakt maskin kan konkreta val av källkodsnivå göra en betydande skillnad. Till exempel, på tidiga C -kompilatorer, while(1)var långsammare än for(;;)för en ovillkorlig slinga, eftersom while(1)utvärderade 1 och sedan hade ett villkorligt hopp som testade om det var sant, medan det for (;;)hade ett ovillkorligt hopp. Vissa optimeringar (som den här) kan numera utföras genom att optimera kompilatorer . Detta beror på källspråket, målmaskinens språk och kompilatorn, och kan vara både svårt att förstå eller förutsäga och förändras över tiden; detta är en viktig plats där förståelse av kompilatorer och maskinkod kan förbättra prestanda. Loop-invariant kodrörelse och returvärdeoptimering är exempel på optimeringar som minskar behovet av hjälpvariabler och kan till och med resultera i snabbare prestanda genom att undvika rundoptimeringar.

Byggnivå

Mellan käll- och kompileringsnivå kan direktiv och byggflaggor användas för att ställa in prestandealternativ i källkoden respektive kompilatorn, till exempel att använda förprocessordefinieringar för att inaktivera onödiga programvarufunktioner, optimera för specifika processormodeller eller hårdvarufunktioner eller förutsäga förgrening , till exempel. Källbaserade programvarudistributionssystem som BSD : s portar och Gentoo 's Portage kan dra nytta av denna form av optimering.

Kompilera nivå

Användning av en optimerande kompilator tenderar att säkerställa att det körbara programmet optimeras minst lika mycket som kompilatorn kan förutsäga.

Monteringsnivå

På lägsta nivå kan kodning med hjälp av ett monteringsspråk , utformat för en viss hårdvaruplattform, producera den mest effektiva och kompakta koden om programmeraren utnyttjar hela repertoaren av maskininstruktioner . Många operativsystem som används på inbäddade system har traditionellt skrivits i assembler -kod av denna anledning. Program (andra än mycket små program) skrivs sällan från början till slut vid montering på grund av tid och kostnad. De flesta sammanställs från ett språk på hög nivå till montering och handoptimeras därifrån. När effektivitet och storlek är mindre viktiga kan stora delar skrivas på ett språk på hög nivå.

Med modernare optimering av kompilatorer och större komplexitet i de senaste processorerna är det svårare att skriva mer effektiv kod än vad kompilatorn genererar, och få projekt behöver detta "ultimata" optimeringssteg.

Mycket kod skriven idag är avsedd att köras på så många maskiner som möjligt. Som en konsekvens drar programmerare och kompilatorer inte alltid nytta av de mer effektiva instruktionerna från nyare processorer eller finesser i äldre modeller. Dessutom kan monteringskod som är inställd för en viss processor utan att använda sådana instruktioner fortfarande vara suboptimal på en annan processor och förvänta sig en annan inställning av koden.

Vanligtvis idag i stället för att skriva på monteringsspråk, kommer programmerare att använda en demonterare för att analysera utmatningen från en kompilator och ändra källkoden på hög nivå så att den kan kompileras mer effektivt eller förstå varför den är ineffektiv.

Körtid

Just-in-time- kompilatorer kan producera anpassad maskinkod baserad på körtidsdata, på bekostnad av kompileringskostnader. Denna teknik dateras till de tidigaste reguljära uttrycksmotorerna och har blivit utbredd med Java HotSpot och V8 för JavaScript. I vissa fall adaptiv optimering kan ha möjlighet att utföra körning optimering som överstiger förmågan av statiska kompilatorer genom att dynamiskt justera parametrar enligt den faktiska ingång eller andra faktorer.

Profilstyrd optimering är en teknik för optimering av kompilering i förväg (AOT) baserad på körtidsprofiler och liknar en statisk "genomsnittlig fall" -analog för den dynamiska tekniken för adaptiv optimering.

Självmodifierande kod kan förändra sig själv som svar på körtidsförhållanden för att optimera koden; detta var mer vanligt i monteringsspråkprogram.

Vissa CPU -konstruktioner kan utföra vissa optimeringar vid körning. Några exempel inkluderar exekveringsstörning , spekulativ körning , instruktionsrörledningar och filialprediktorer . Kompilatorer kan hjälpa programmet att dra fördel av dessa CPU -funktioner, till exempel genom schemaläggning av instruktioner .

Plattformsberoende och oberoende optimeringar

Kodoptimering kan också i stort sett kategoriseras som plattformsberoende och plattformsoberoende tekniker. Medan de senare är effektiva på de flesta eller alla plattformar, använder plattformsberoende tekniker specifika egenskaper hos en plattform, eller förlitar sig på parametrar beroende på den enda plattformen eller till och med på den enda processorn. Därför kan det vara nödvändigt att skriva eller producera olika versioner av samma kod för olika processorer. Till exempel, när det gäller optimering på kompilnivå, är plattformsoberoende tekniker generiska tekniker (som slingning av slingor , minskning av funktionssamtal, minneseffektiva rutiner, minskning av förhållanden, etc.), som påverkar de flesta CPU-arkitekturer i en liknande sätt. Ett bra exempel på plattformsoberoende optimering har visats med inner for loop, där det observerades att en loop med en inner för loop utför fler beräkningar per tidsenhet än en loop utan den eller en med en inre while loop. I allmänhet tjänar dessa till att minska den totala instruktionsvägslängden som krävs för att slutföra programmet och/eller minska den totala minnesanvändningen under processen. Å andra sidan involverar plattformsberoende tekniker instruktionsschemaläggning , parallellitet på instruktionsnivå, parallellitet på datanivå, cacheoptimeringstekniker (dvs. parametrar som skiljer sig mellan olika plattformar) och den optimala instruktionsschemaläggningen kan vara annorlunda även på olika processorer i samma arkitektur.

Styrka minskning

Beräkningsuppgifter kan utföras på flera olika sätt med varierande effektivitet. En mer effektiv version med motsvarande funktionalitet kallas en styrka minskning . Tänk till exempel på följande C -kodavsnitt vars avsikt är att få summan av alla heltal från 1 till N :

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

Denna kod kan (förutsatt att inget aritmetiskt överflöd ) skrivas om med en matematisk formel som:

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

Optimeringen, som ibland utförs automatiskt av en optimerande kompilator, är att välja en metod ( algoritm ) som är mer beräkningseffektiv och samtidigt behålla samma funktionalitet. Se algoritmisk effektivitet för en diskussion om några av dessa tekniker. Men en betydande förbättring av prestanda kan ofta uppnås genom att ta bort främmande funktioner.

Optimering är inte alltid en självklar eller intuitiv process. I exemplet ovan kan den "optimerade" versionen faktiskt vara långsammare än den ursprungliga versionen om N var tillräckligt liten och den specifika hårdvaran råkar vara mycket snabbare att utföra addition- och loop -operationer än multiplikation och division.

Avvägningar

I vissa fall är optimering dock beroende av att använda mer detaljerade algoritmer, att använda sig av "specialfall" och speciella "knep" och att utföra komplexa avvägningar. Ett "helt optimerat" program kan vara svårare att förstå och kan därför innehålla fler fel än ooptimerade versioner. Utöver att eliminera uppenbara antimönster minskar vissa kodnivåoptimeringar underhållbarheten.

Optimering kommer i allmänhet att fokusera på att förbättra bara en eller två aspekter av prestanda: körningstid, minnesanvändning, diskutrymme, bandbredd, strömförbrukning eller någon annan resurs. Detta kräver vanligtvis en avvägning-där en faktor är optimerad på andras bekostnad. Till exempel ökar storleken på cacheminnet förbättrad körtidsprestanda, men ökar också minnesförbrukningen. Andra vanliga avvägningar inkluderar kodklarhet och kortfattadhet.

Det finns fall där programmeraren som utför optimeringen måste bestämma sig för att göra programvaran bättre för vissa operationer men på bekostnad av att göra andra operationer mindre effektiva. Dessa avvägningar kan ibland vara av icke-teknisk karaktär-till exempel när en konkurrent har publicerat ett riktmärke- resultat som måste slås för att förbättra kommersiell framgång, men kanske medför bördan att göra normal användning av programvaran mindre effektiv. Sådana förändringar kallas ibland på skämt pessimiseringar .

Flaskhalsar

Optimering kan innefatta att hitta en flaskhals i ett system - en komponent som är den begränsande faktorn för prestanda. När det gäller kod kommer detta ofta att vara en hot spot  - en kritisk del av koden som är den primära konsumenten för den resurs som behövs - även om det kan vara en annan faktor, till exempel I/O -latens eller nätverksbandbredd.

Inom datavetenskap följer resursförbrukningen ofta en form av kraftlagsfördelning , och Pareto -principen kan tillämpas på resursoptimering genom att observera att 80% av resurserna vanligtvis används av 20% av operationerna. Inom mjukvaruteknik är det ofta en bättre approximation att 90% av körningstiden för ett datorprogram går åt till att köra 10% av koden (känd som 90/10 -lagen i detta sammanhang).

Mer komplexa algoritmer och datastrukturer fungerar bra med många objekt, medan enkla algoritmer är mer lämpliga för små mängder data - installationen, initialiseringstiden och konstanta faktorer för den mer komplexa algoritmen kan uppväga nyttan, och därmed en hybridalgoritm eller adaptiv algoritm kan vara snabbare än någon enskild algoritm. En prestandaprofiler kan användas för att begränsa beslut om vilken funktionalitet som passar vilka villkor.

I vissa fall kan tillägg av mer minne bidra till att ett program körs snabbare. Till exempel kommer ett filtreringsprogram vanligtvis att läsa varje rad och filtrera och mata ut den raden direkt. Detta använder bara tillräckligt med minne för en rad, men prestandan är vanligtvis dålig på grund av latensen för varje diskläsning. Att cacha resultatet är lika effektivt, men kräver också större minnesanvändning.

När ska man optimera

Optimering kan minska läsbarheten och lägga till kod som endast används för att förbättra prestandan . Detta kan komplicera program eller system, vilket gör dem svårare att underhålla och felsöka. Som ett resultat utförs ofta optimering eller prestandajustering i slutet av utvecklingsstadiet .

Donald Knuth gjorde följande två uttalanden om optimering:

"Vi bör glömma bort små effektiviteter, säg ungefär 97% av tiden: för tidig optimering är roten till allt ont. Ändå ska vi inte missa våra möjligheter i de kritiska 3%"

(Han tillskrev också citatet till Tony Hoare flera år senare, även om detta kan ha varit ett fel eftersom Hoare avstår från att ha myntat frasen.)

"Inom etablerade ingenjörsgrenar anses en förbättring på 12%, lätt att få, aldrig vara marginell och jag tror att samma synvinkel bör råda inom mjukvaruteknik"

"För tidig optimering" är en fras som används för att beskriva en situation där en programmerare låter prestandahänsyn påverka utformningen av en kodbit. Detta kan resultera i en design som inte är så ren som den kunde ha varit eller fel kod, eftersom koden kompliceras av optimeringen och programmeraren distraheras av optimering.

När man beslutar om man vill optimera en specifik del av programmet bör Amdahls lag alltid beaktas: effekten på det övergripande programmet beror mycket på hur mycket tid som faktiskt läggs ner på den specifika delen, vilket inte alltid är klart från att titta på koden utan en prestationsanalys .

Ett bättre tillvägagångssätt är därför att designa först, koda från designen och sedan profilera / benchmarka den resulterande koden för att se vilka delar som ska optimeras. En enkel och elegant design är ofta lättare att optimera i detta skede, och profilering kan avslöja oväntade prestandaproblem som inte skulle ha åtgärdats genom för tidig optimering.

I praktiken är det ofta nödvändigt att ha prestandamål i åtanke när man först designar programvara, men programmeraren balanserar målen med design och optimering.

Moderna kompilatorer och operativsystem är så effektiva att de avsedda prestandahöjningarna ofta inte blir verklighet. Som ett exempel ger cachedata på applikationsnivå som igen cachas på operativsystemnivå inte förbättringar i körningen. Ändå är det ett sällsynt fall när programmeraren kommer att ta bort misslyckade optimeringar från produktionskoden. Det är också sant att framsteg inom hårdvara oftare än inte undanröjer eventuella förbättringar, men den döljande koden kommer att bestå i framtiden långt efter att dess syfte har förnekats.

Makron

Optimering under kodutveckling med hjälp av makron tar olika former på olika språk.

På vissa procedurspråk, till exempel C och C ++ , implementeras makron med hjälp av token -substitution. Numera kan inline -funktioner användas som ett typsäkert alternativ i många fall. I båda fallen kan den lutade funktionskroppen sedan genomgå ytterligare kompileringstidsoptimeringar av kompilatorn, inklusive konstant vikning , vilket kan flytta vissa beräkningar till kompileringstid.

I många funktionella programmeringsspråk implementeras makron med parse-time substitution av parse-träd/abstrakta syntax-träd, vilket det påstås gör dem säkrare att använda. Eftersom tolkning i många fall används, är det ett sätt att säkerställa att sådana beräkningar endast utförs vid parstid, och ibland det enda sättet.

Lisp härstammar från denna makrostil, och sådana makron kallas ofta "Lisp-liknande makron". En liknande effekt kan uppnås genom att använda mallmetaprogrammering i C ++ .

I båda fallen flyttas arbetet till kompileringstid. Skillnaden mellan C- makron på ena sidan och Lisp-liknande makron och C ++ mallmetaprogrammering på den andra sidan är att de senare verktygen gör det möjligt att utföra godtyckliga beräkningar vid kompileringstid/parningstid, medan expansion av C- makron inte utför någon beräkning och förlitar sig på optimeringsförmågan för att utföra den. Dessutom stöder inte C -makron rekursion eller iteration direkt , så Turing är inte komplett .

Som med all optimering är det dock ofta svårt att förutse var sådana verktyg kommer att få störst effekt innan ett projekt är klart.

Automatiserad och manuell optimering

Se även kategori: Kompilatoroptimeringar

Optimering kan automatiseras av kompilatorer eller utföras av programmerare. Vinster är vanligtvis begränsade för lokal optimering och större för globala optimeringar. Vanligtvis är den mest kraftfulla optimeringen att hitta en överlägsen algoritm .

Optimering av ett helt system utförs vanligtvis av programmerare eftersom det är för komplext för automatiserade optimerare. I denna situation ändrar programmerare eller systemadministratörer uttryckligen kod så att det övergripande systemet fungerar bättre. Även om det kan ge bättre effektivitet är det mycket dyrare än automatiserade optimeringar. Eftersom många parametrar påverkar programmets prestanda är programoptimeringsutrymmet stort. Meta-heuristik och maskininlärning används för att hantera komplexiteten i programoptimering.

Använd en profiler (eller prestandaanalysator ) för att hitta de avsnitt i programmet som tar mest resurser - flaskhalsen . Programmerare tror ibland att de har en klar uppfattning om var flaskhalsen är, men intuition är ofta fel. Att optimera en oviktig kodbit hjälper vanligtvis inte mycket till den totala prestandan.

När flaskhalsen är lokaliserad börjar optimering vanligtvis med en omprövning av algoritmen som används i programmet. Oftare kan en särskild algoritm skräddarsys specifikt för ett visst problem, vilket ger bättre prestanda än en generisk algoritm. Till exempel görs uppgiften att sortera en enorm lista med artiklar vanligtvis med en snabbsortrutin , som är en av de mest effektiva generiska algoritmerna. Men om någon egenskap hos objekten är exploaterbar (till exempel är de redan ordnade i någon viss ordning) kan en annan metod användas, eller till och med en skräddarsydd sorteringsrutin.

Efter att programmeraren är någorlunda säker på att den bästa algoritmen är vald kan kodoptimering starta. Slingor kan rullas ut (för lägre slingor, även om detta ofta kan leda till lägre hastighet om det överbelastar CPU-cacheminnet ), kan datatyper så små som möjligt användas, heltalsaritmetik kan användas istället för flytpunkt osv. . (Se algoritmisk effektivitetsartikel för dessa och andra tekniker.)

Prestanda flaskhalsar kan bero på språkbegränsningar snarare än algoritmer eller datastrukturer som används i programmet. Ibland kan en kritisk del av programmet skrivas om på ett annat programmeringsspråk som ger mer direkt åtkomst till den underliggande maskinen. Till exempel är det vanligt att språk på mycket hög nivå som Python har moduler skrivna i C för högre hastighet. Program som redan skrivits i C kan ha moduler skrivna i sammansättning . Program skrivna i D kan använda den inbyggda assemblern .

Omskrivning av avsnitt "lönar sig" under dessa omständigheter på grund av en allmän " tumregel " som kallas 90/10 -lagen , som säger att 90% av tiden spenderas i 10% av koden och endast 10% av tiden i de återstående 90% av koden. Så att lägga intellektuell ansträngning på att optimera bara en liten del av programmet kan ha en enorm effekt på den totala hastigheten - om rätt del (er) kan hittas.

Manuell optimering har ibland en bieffekt av att undergräva läsbarheten. Således bör kodoptimeringar noggrant dokumenteras (helst med hjälp av in-line kommentarer), och deras effekt på framtida utveckling utvärderas.

Programmet som utför en automatiserad optimering kallas en optimerare . De flesta optimerare är inbäddade i kompilatorer och fungerar under kompilering. Optimerare kan ofta skräddarsy den genererade koden till specifika processorer.

Idag är automatiserade optimeringar nästan uteslutande begränsade till kompilatoroptimering . Men eftersom kompilatoroptimeringar vanligtvis är begränsade till en fast uppsättning ganska generella optimeringar, finns det stor efterfrågan på optimerare som kan acceptera beskrivningar av problem- och språkspecifika optimeringar, så att en ingenjör kan ange anpassade optimeringar. Verktyg som accepterar beskrivningar av optimeringar kallas programtransformationssystem och börjar tillämpas på riktiga mjukvarusystem som C ++.

Vissa språk på hög nivå ( Eiffel , Esterel ) optimerar sina program genom att använda ett mellanliggande språk .

Grid computing eller distribuerad dator syftar till att optimera hela systemet genom att flytta uppgifter från datorer med hög användning till datorer med inaktiv tid.

Det tog tid för optimering

Ibland kan den tid det tar att genomföra optimering däri i sig vara ett problem.

Att optimera befintlig kod lägger vanligtvis inte till nya funktioner, och värre, det kan lägga till nya buggar i tidigare arbetskod (som alla ändringar kan göra). Eftersom manuellt optimerad kod ibland kan ha mindre "läsbarhet" än ooptimerad kod, kan optimering också påverka underhållbarheten av den. Optimering har ett pris och det är viktigt att vara säker på att investeringen är värd.

En automatisk optimering (eller optimering av kompilatorn , ett program som utför kodoptimering) kan själv behöva optimeras, antingen för att ytterligare förbättra effektiviteten i sina målprogram eller för att påskynda sin egen drift. En sammanställning som utförs med optimering "påslagen" tar vanligtvis längre tid, även om detta vanligtvis bara är ett problem när program är ganska stora.

I synnerhet för just-in-time-kompilatorer prestanda körning sammanställa komponent, utföra tillsammans med sitt mål kod, är nyckeln till att förbättra den totala exekveringshastighet.

Referenser

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

externa länkar