Ytelse og optimalisering Frode Eika Sandnes Premature optimization is the root of all evil Donald Knuth 1
Begrep og tolkning I databehandling betyr optimalisering og forbedre et system slik at det går raskere eller bruker mindre minne. I matematikk, økonomi etc betyr optimalisering å finne laveste eller største verdi av en funksjon. For eksempel, optimalisere en kostnadavkastningsfunksjon. Læringsmål Få en forståelse for begrepet ytelse og hvilke faktorer som påvirker ytelse Få en forståelse for forholdet mellom tidsresurs og plassresurs Få innsikt i noen av teknikkene som brukes for å forbedre ytelse Lære om verktøy som brukes for å forbedre ytelse Lære at enkelte problemer ikke kan løses. 2
Ytelse Hvor mange multiplikasjoner og addisjoner skal til for å regne ut følgende utrykk? (en datamaskin kan egentlig bare multiplisere og addere) Ax 5 +Bx 4 +Cx 3 +Dx 2 +Ex+D Teknikk 1 (rett frem) 5 addisjoner 5+4+3+2+1=15 multiplikasjoner Kan vi gjøre dette med mindre innsats? Axxxxx+Bxxxx+Cxxx+Dxx+Ex+D 3
Teknikk 2 (Horner s metode) 5 addisjoner 5 multiplikasjoner Kan vi gjøre dette med mindre innsats? ((((Ax+B)x+C)x+D)x+E)x+D Omformulering Ofte kan vi omformulere et problem Omformuleringer kan føre til at problemet kan løses med mindre innsats Hvis en problem kan løses med mindre innsats, så får vi tid til å løse flere problemer innenfor samme tid. Vi har dermed forbedret ytelsen 4
Problem En organisasjon vurderer hvordan de skal restrukturere sorteringen av den interne posten til de forskjellige avdelingene og kutte kostnadene. De får en stor mengde post. Hva er den mest effektive løsningen? Man kjøper en postkasse for hver avdeling og henger de opp utenfor bygget med egen adresse. Det er nå postverket sin oppgave å sortere posten. Løsning 5
En liten oppgave Vi har et program som ser slik ut int a = verdi fra bruker int b = verdi fra bruker int temp = a; a = b; b = temp;? Vi skal lage dette programmet på en datamaskin som kun har plass til to variabler, nemlig a og b, og ikke temp. Hvordan gjør vi dette? Løsning int a = verdi fra bruker int b = verdi fra bruker b = a - b; a = a - b; b = a + b; Vi bruker differansen som en slags temp variabel 6
Utregning av heltallspotenser Vi så tidligere hvordan vi lett kunne regne ut heltallspotenser i polynomer med Horner s metode Hva gjør vi med enkle heltallpotenser med høy verdi. Hvordan regne ut for eksempel: 3 53 Løsning 1 Vi multipliserer tallet 53 ganger (53 multiplikasjoner) 3x3x3x..x3 Dette er lite effektivt 7
hvis vi analyserer 3 53 kan omskrives som 3 53 = 3 1+4+16+32 = 3 1 x3 4 x3 16 x3 32 Vi lager en tabell Factor potens Delprodukt 3 1 1 3 3 1 x 3 1 = 3 2 0 0 3 2 x 3 2 = 3 4 1 81 3 4 x 3 4 = 3 8 0 0 3 8 x 3 8 = 3 16 1 43046721 3 16 x 3 16 = 3 32 1 1853020188851841 Totalprodukt Et veldig stort tall 8
Innsats 5 multiplikasjoner 4 addisjoner 5 shift operasjoner 6 sammenlikninger Multiplikasjon tar mer tid enn addisjon som tar mer tid enn shift og sammenlikninger. Balansen mellom tid og plass Ofte er det en balansegang mellom tid og plass. Enten så kan vi løse et problem med mye innsats, hvor vi trenger liten lagringsplass Eller så kan vi løse et problem hurtig med lite innsats på bekostning av mer lagringsplass. 9
Praktisk eksempel Uregning av matematiske funksjoner som sin, cos Kan gjøres matematisk etter modell, eg. En Taylor rekke etc. Det kreves ofte en god del innsats Kan bruke en tabell med ferdige verdier Liknende matematisk tabell på skolen Eksempel Finne median (den midterste verdien) i en rekke med usorterte heltall. Plassoptimalisering Vi sorterer tallene (tar lang tid) Vi velger det midterste tallet (dette er median) Trenger ikke plass, trenger å kikke på verdiene opptil N*N ganger Ytelsesoptimalisering Vi lager et histogram over tallene (ved å telle) Vi kan lett telle oss frem til midtpunktet i histogrammet Vi trenger lagringsplass til histogrammet (opptil N elementer) Vi trenger kun å skanne igjennom tallene en gang (N). 10
Finne median med histogram 9 8 7 6 5 4 Dette er det midterste elementet 3 2 1 0 1 2 3 4 5 6 Dette er median Uløselige problemer Det finnes problemer som ikke kan løses (eksakt) av datamaskiner fordi det rett og slett tar for lang tid eller krever for mye minne. Disse problemene kaller vi uløselige problemer Eksempler er laging av timeplaner, sette opp arbeidsplaner for bussjåfører eller kabinpersonale i flyselskaper Slike problemer løser man ved å finne tilnærminger til løsningen 11
Eksempel: den reisende selger Tenk deg at du er salgsmann og du skal reise til alle byene hvor du har butikk, for eksempel Oslo, Bergen, Trondheim, Stavanger og Vardø. Du ønsker å reise på en slik måte at du besøker hver by kun en gang Samtidig ønsker du å reise korteste vei (spare drivstoff eller kostnad). 12
Hvorfor? Si at du har N byer. Hvilken by skal du velge først? Du kan velge mellom N byer. Hvilken by skal du velge neste gang? Du kan velge mellom N-1 byer (det er de byene du har igjen). Hvilden by skal du velge deretter? Du kan velge mellom N-2 byer (det er de byene du har igjen) Slik forsetter det til vi har en by igjen For å finne korteste vei må vi prøve alle mulighetene. Da må vi prøve N(N-1)(N-2)(N-3) (N-(N-1)) = N! muligheter Hvis du har mange byer, dvs en stor N, så blir dette veldig mange muligheter. Prøv for eksempel å finne hvor mange muligheter det er for 10 byer, 15 byer, 100 byer? Hvordan Det finnes mange måter å finne gode løsninger på den reisende selgers problem Ingen av disse garanterer den beste løsningen. En måte er å velge byene helt tilfeldig (random), og deretter kikke på resultatet. Hvis du for eksempel repeterer dette, si 1000 ganger vil du sannsynligvis finne en ganske god løsning (samme prinsipp som å kjøpe mange lodd i lotto for å øke vinnersjansene). Du velger da den ruten som gir kortest vei. 13
Hvordan optimalisere i praksis Hvordan kan vi måle ytelse i Java long start = System.currentTimeMillis();.. gjøre det som skal måles her long tid = System.currentTimeMillis() - start; 14
En annen vanlig teknikk Import java.util.date;.. Date start = new Date(); gjøre det som skal måles her Date slutt = new Date); Long antallmillisekunder = slutt.gettime() start.gettime(); Verktøy for å forbedre ytelsen Profiler Lar utvikleren se hva datamaskinen bruker tiden på Man kompilerer programmet med ekstra bokføringskode (et valg i kompilatoren) Man kjører programmet og bokføringskoden aktiveres Man får statistikk om Hvor mange ganger hver modul ble brukt, metode ble kalt, og hvor ofte hver kodelinje ble eksekvert. Hvor mange prosent av tiden som ble brukt av de respektive deler av koden. Dette kalles en eksekveringsprofil. 15
Hva gjør man med en profil Profilen viser oss hvor flaskehalsen er i programmet Hvis vi ønsker å forbedre ytelsen så gjør vi dette ved å starte der hvor skoen trykker. Dvs. vi kan lett se hvor i koden vi bør gjøre inngrep. Java profil Du kan kjøre profileren med prof flagget java prof klassenavnet Eller for applets java -prof sun.applet.appletviewer myapplet.html Det genereres en fil med navn java.prof som inneholder profilen 16
Profilering i praksis Gjør målinger både før og etter en endring for å dokumentere at endringen faktisk førte til en forbedring i ytelse (iallfall under testoppsettet) Prøv å utfør hver måling under de samme forutsetningene og forholdene Unngå målinger der brukere leverer input til systemet, da dette kan føre til store variasjoner i målingene Prinsipper for optimalisering 90-10 regelen 90% av tiden brukes vanligvis i 10% av koden. Vi bør bruke 90 % av optimaliseringsinnsatsen på å forbedre disse 10% ene av koden 17
Unngå optimalisering Man bør unngå optimalisering av koden Kan resultere i nye feil i koden Gjør koden vanskeligere å forstå Gjør vedlikehold av koden vanskelig Fjør koden mindre generell og portabel Er kostbart og tidkrevende Noe som er optimalisert på en plattform kjører kanskje enda saktere på en annen plattform. Mye tid investert i optimalisering gir ikke nødvendigvis avkasting i ytelsesforbedringer Man bør utsette optimalisering av koden Man bør bruke automatiske optimaliseringsverktøy (optimaliseringsflagg) Bruk bedre og kjente algoritmer Det kan være billigere å kjøpe bedre maskinvare Det er ofte et motsetningsforhold mellom vedlikeholdbar, generisk kode og optimalisert effektiv kode Teknikker for optimalisering 18
Styrkereduksjon Erstatte en operasjon med en annen ekvivalent operasjon som er mindre tidkrevende For eksempel erstatte multiplikasjon av tall med base 2 (2, 4, 8, 16 etc) med venstre shift operasjoner, og divisjon av tall med base2 med høyre shift operasjoner. x >> 2 er det samme som x / 4 x << 1 er det samme som x * 2 Begge gir samme resultat Shift operasjoner tar mye mindre tid enn multiplikasjoner og divisjoner. Eliminering av vanlige subutrykk Eliminere utrykk som går igjen hvor samme ting regnes ut gang på gang. double x = d * (lim / max) * sx; double y = d * (lim / max) * sy; Bør erstattes med double dybde = d * (lim / max); double x = dybde * sx; double y = dybde * sy; 19
Flytting av kode Kode som eksekveres flere ganger selv om verdiene ikke endres seg bør flyttes slik at koden kun eksekverer når det har skjedd en endring. for (int i = 0; i < x.length; i++) x[i] *= Math.PI * Math.cos(y); Bør endres til double picosy = Math.PI * Math.cos(y); for (int i = 0; i < x.length; i++) x[i] *= picosy; Rulle ut løkker Ved og ekspandere løkker så unngår vi å eksekvere kode som styrer selve løkka. Hvis vi i forrige eksempel vet at lengden på tabellen alltid er et partall (delelig med to), så kan vi halvere antall løkkeiterasjoner double picosy = Math.PI * Math.cos(y); for (int i = 0; i < x.length; i += 2) x[i] *= picosy; x[i+1] *= picosy; 20
Sammenlikninger Å sammenlikne to vilkårlige tall er vanligvis mer tidkrevende enn å sammenlikne et tall med null (fordi begge tall må lagres i minnet). for (i = 0; i < N; i++) // gjør et eller annet Vi endrer sammenlikningen: for (i = N; --i >= 0; ) // gjør et eller annet Ikke sammenlikn Vi kan også fjerne sammenlikningen try for (i = 0; ; i++) array[i] = i; catch (ArrayIndexOutOfBoundsException e) Ulempene er Avvik er kostbare Fungerer kun hvis N er veldig stor Ytelsen vil variere mye fra en plattform til en annen 21
Variabler Lokale variabler er hurtigere enn globale variabler I java er int den hurtigste datatypen Mindre datatyper som for eksempel, boolean, short, char og boolean er representert som en integer Hvis vi skal eksplisitt arbeide med kortere datatyper så kreves det ekstra instruksjoner for konvertering Kortere datatyper er derfor tregere enn int Ikke kopier mer enn nødvendig Kopiering av data tar lang tid, spesielt lange variabler etc. Java hjelper oss ved all alle ikke atomiske datatyper videreformidles med referanser. Dette er ikke tilfelle i alle språk som for eksempel C/C++/Pascal hvor en lett kan kaste bort tid på unødvendig kopiering (spesielt parametere til metoder osv). 22
Arrayer og static Hvor du bruker en lokal array i en metode som kalles ofte, så må denne arrayen skapes hver gang. Dette er ueffektivt. Du kan deklarere arrayen som static. Da kan du bruke den samme arrayen gang på gang uten at den skapes på nytt for hvert metodekall. Unngå casting Casting tar lang tid boolean equals (Object obj) if (obj instanceof Point) return (((Point)obj).x == this.x && ((Point)obj).y == this.y); return false; Erstatt med boolean equals (Object obj) if (obj instanceof Point) Point p = (Point)obj; return (p.x == this.x && p.y == this.y); return false; Casting er spesielt dyrt hvis det dreier seg om et interface 23
Generelle tips for ytelsesøkning Native-metoder (kall til typisk C-funksjoner) Konverter til C (finnes verktøy for dette) Bruk buffere ved input og output (BufferedInputStream og BufferedOutputStream) Ikke bruk synchronize (synkronisering) Unngå avvik (exceptions) Unngå sammenslåingsoperatoren + for strenger Bruk API funksjonalitet Bruk erstatninger for API funksjonalitet Gjenbruk av objekter Oppgave 2 Vi kan finne det største av to tall som følger if (a>b) max = a; else max = b; Og vi kan finne det minste tallet ved å endre > til <. Kan du gjøre det samme uten å bruke en sammenlikning (if)? (Ja, det er faktisk mulig) 24
Kompilatoroptimalisert kode Kompilatorer gjør en del optimaliseringer C og C++ kompilatorer er meget gode. Java kompilatorer er fortsatt unge Java compilatoren: Folding av konstanter: i=10*10 blir til i=100 Branch folding: unødvendige goto s i bytekoden fjernes Eliminering av død kode: if(false) i = 1 Du kan også bruke O (optimaliserings) muligheten fra kommandolinjen (javac O fil.java) Kode ekspansjon (inline): alle metoder som er static, private eller final ekspanderes slik at utgiftene i forbindelse med selve metodekallet elimineres Optimalisering for plass Javakode som tar mindre plass vil lastes hurtigere inn i minnet Eksempel: Det tar kortere tid å laste ned en liten java applet enn en stor java applet fra en nettside Vi derfor ønsker å minske størrelsen på den kompilerte javakoden 25
Plassbesparingsteknikk er Bruke arkiv (jar-fil) Ikke finne opp hjulet på nytt (bruk eksisterende API) Utnytt arv Separer ut felles kode Ikke initialiser store arrayer Datoer er store (bruk long) Bruk korte navn (kompilert kode inneholder navnene) Plasser static final konstanter i interfaces Unngå string-sammenslåing (ekstra kall og objektskapelse) Lykke til! 26