optimalisatie is een programma transformatie techniek, die probeert om de code te verbeteren door het maken van het verbruiken minder middelen (dwz CPU, geheugen) en leveren hoge snelheid.
Bij optimalisatie worden algemene programmeerconstructies op hoog niveau vervangen door zeer efficiënte programmeercodes op laag niveau. Een code optimalisatie proces moet de drie onderstaande regels volgen:
-
de uitvoercode mag op geen enkele manier de Betekenis van het programma veranderen.
-
optimalisatie moet de snelheid van het programma te verhogen en indien mogelijk, het programma moet minder aantal middelen te eisen.
-
optimalisatie moet zelf snel zijn en mag het totale compilatieproces niet vertragen.
inspanningen voor een geoptimaliseerde code kunnen worden gedaan op verschillende niveaus van het compileren van het proces.
-
in het begin kunnen gebruikers de code wijzigen/herschikken of betere algoritmen gebruiken om de code te schrijven.
-
na het genereren van tussenliggende code, kan de compiler de tussenliggende code wijzigen door adresberekeningen en het verbeteren van loops.
-
tijdens het produceren van de doel machine code, kan de compiler gebruik maken van geheugen hiërarchie en CPU registers.
optimalisatie kan in grote lijnen worden onderverdeeld in twee types : machine-onafhankelijk en machine-afhankelijk.
Machine-onafhankelijke optimalisatie
bij deze optimalisatie neemt de compiler de tussenliggende code in en transformeert een deel van de code dat geen CPU-registers en/of absolute geheugenlocaties omvat. Bijvoorbeeld::
do{ item = 10; value = value + item; } while(valueThis code involves repeated assignment of the identifier item, which if we put this way:
Item = 10;do{ value = value + item; } while(valueshould not only save the CPU cycles, but can be used on any processor.
Machine-dependent Optimization
Machine-dependent optimization is done after the target code has been generated and when the code is transformed according to the target machine architecture. It involves CPU registers and may have absolute memory references rather than relative references. Machine-dependent optimizers put efforts to take maximum advantage of memory hierarchy.
Basic Blocks
Source codes generally have a number of instructions, which are always executed in sequence and are considered as the basic blocks of the code. These basic blocks do not have any jump statements among them, i.e., when the first instruction is executed, all the instructions in the same basic block will be executed in their sequence of appearance without losing the flow control of the program.
A program can have various constructs as basic blocks, like IF-THEN-ELSE, SWITCH-CASE conditional statements and loops such as DO-WHILE, FOR, and REPEAT-UNTIL, etc.
Basic block identification
We may use the following algorithm to find the basic blocks in a program:
-
zoek header statements van alle basisblokken vanaf waar een basisblok begint:
- eerste verklaring van een programma.
- Statements die het doelwit zijn van elke branch (voorwaardelijk/onvoorwaardelijk).
- Statements die volgen op een branch statement.
-
Header statements en de daarop volgende statements vormen een basisblok.
-
een basic block bevat geen header statement van een ander basic block.
basisblokken zijn belangrijke concepten vanuit het oogpunt van codegeneratie en optimalisatie.
basisblokken spelen een belangrijke rol bij het identificeren van variabelen die meer dan eens in één basisblok worden gebruikt. Als een variabele meer dan eens wordt gebruikt, hoeft het aan die variabele toegewezen register-geheugen niet te worden geleegd tenzij het blok de uitvoering beëindigt.
Regelstroomgrafiek
basisblokken in een programma kunnen worden weergegeven door middel van regelstroomgrafieken. Een grafiek van de controlestroom toont hoe de programmacontrole tussen de blokken wordt doorgegeven. Het is een handig hulpmiddel dat helpt bij optimalisatie door hulp bij het lokaliseren van ongewenste loops in het programma.
Lusoptimalisatie
de meeste programma ‘ s draaien als een lus in het systeem. Het wordt noodzakelijk om de loops te optimaliseren om CPU cycli en geheugen op te slaan. Lussen kunnen worden geoptimaliseerd door de volgende technieken:
-
invariante code: een fragment van code dat zich in de lus bevindt en bij elke iteratie dezelfde waarde berekent, wordt een lus-invariante code genoemd. Deze code kan uit de lus worden verplaatst door het op te slaan om slechts eenmaal te worden berekend, in plaats van bij elke iteratie.
-
Inductieanalyse: een variabele wordt een inductievariabele genoemd als zijn waarde binnen de lus wordt veranderd door een lus-invariante waarde.
-
sterkte reductie: er zijn uitdrukkingen die verbruiken meer CPU cycli, tijd, en geheugen. Deze uitdrukkingen moeten worden vervangen door goedkopere uitdrukkingen zonder afbreuk te doen aan de output van expressie. Bijvoorbeeld, vermenigvuldiging (x * 2) is duur in termen van CPU cycli dan (x
Dead-code eliminatie
Dead code is een of meer codeterminaties, die zijn:
- ofwel nooit uitgevoerd of onbereikbaar,
- of indien uitgevoerd, wordt hun uitvoer nooit gebruikt.
dode code speelt dus geen rol in een programmabewerking en kan daarom eenvoudig worden geëlimineerd.
gedeeltelijk dode code
er zijn enkele codeterminaties waarvan de berekende waarden alleen onder bepaalde omstandigheden worden gebruikt, d.w.z. soms worden de waarden gebruikt en soms niet. Dergelijke codes staan bekend als gedeeltelijk dode code.
de bovenstaande regelstroomgrafiek toont een deel van het programma waarin variabele ” a “wordt gebruikt om de uitvoer van uitdrukking” x * y ” toe te wijzen. Laten we aannemen dat de waarde toegewezen aan ‘a’ nooit binnen de lus wordt gebruikt.Direct nadat het besturingselement de lus verlaat, krijgt’ a ‘de waarde van variabele’ z ‘ toegewezen, die later in het programma zou worden gebruikt. We concluderen hier dat de assignment code van ‘ a ‘ nooit ergens wordt gebruikt, daarom komt het in aanmerking om te worden geëlimineerd.
op dezelfde manier toont de afbeelding hierboven dat de voorwaardelijke verklaring altijd onwaar is, wat impliceert dat de code, geschreven in true case, nooit zal worden uitgevoerd, dus het kan worden verwijderd.
partiële redundantie
redundante expressies worden meer dan eens berekend in parallel pad, zonder enige verandering in operanden.terwijl partieel-redundante expressies meer dan eens in een pad worden berekend, zonder enige verandering in operanden. Bijvoorbeeld:,
|
|
lus-invariante code is gedeeltelijk overbodig en kan worden geëlimineerd met behulp van een code-motion techniek.
een ander voorbeeld van een gedeeltelijk redundante code kan zijn::
If (condition){ a = y OP z;}else{ ...}c = y OP z;
we gaan ervan uit dat de waarden van operanden (y en z) niet worden gewijzigd van toewijzing van variabele a naar variabele c. Hier, als de condition statement waar is, dan wordt y Op z twee keer berekend, anders één keer. Code motion kan worden gebruikt om deze redundantie te elimineren, zoals hieronder getoond:
If (condition){ ... tmp = y OP z; a = tmp; ...}else{ ... tmp = y OP z;}c = tmp;
hier, of de voorwaarde WAAR of onwaar is; y Op z moet slechts één keer worden berekend.