• No results found

Effektivitet inline eller inte?

N/A
N/A
Protected

Academic year: 2021

Share "Effektivitet inline eller inte?"

Copied!
9
0
0

Loading.... (view fulltext now)

Full text

(1)

Kapitel 7 Kapitel 7

Effektivitet inline eller inte?

Temporärer Minnespooler

Kapitel 7 - Effektivitet

inline

• När bör man / bör man inte använda inline

Optimering med inline

• Temporära objekt

• Minneshantering med minnespool - specialiserad new och delete

Effektivitet

Innehållet i denna del är inspirerat av

Scott Meyers, ”Effective C++”

• Boken nämner 50 specifika tips om hur man bör skriva C++-program

samt

Mayhew och Bulka, ”Efficient C++:

performance programming techniques''

• Boken jämför hastigheten hos olika konstruktioner i C++

Kompileringsprocessen Kompileringsprocessen

Kompilering

Länkning

(2)

Kompilering till objektfiler

En källkodsfil (med suffix .cpp) som är körd genom preprocessorn kallas en

översättningsenhet (translation unit)

• I C++ kompileras källkodsfilerna separat till objektfiler (med suffix .o)

• Kompilatorn kan inte veta om anrop sker till funktion som inte är definierad.

Länkaren har översikt över alla definierade funktioner och varnar om anrop sker till funktion som ej är definierad.

Objektfilerna länkas sedan till ett körbart program.

Länkning av objektfiler

Inline Inline

När är inline olämpligt?

90-10-regeln Felhantering

Optimering

• Allmänt om optimering Regel nr 1: "Don't do it"

Regel nr 2: "Don't do it yet"

• Omöjligt att veta vilka delar som är tidskritiska

• Endast tidtagning avslöjar vilka delar som ska optimeras, om ens då…

Prestanda beror ju även på indata

(3)

inline

inline är ett förslag till kompilatorn att ersätta funktionsanrop med den kod som anropas

Man kan tänka sig inline som att kompilatorn klipp-och-klistrar kod

inline kan vara till hjälp när man vill skriva effektiva program

Utan inline kan man använda

preprocessorn för att uppnå samma effekt.

Man får då inte samma typkontroll

Lämplighet hos inline-definitioner:

• Funktioner med lång exekveringstid (många gånger tiden för anrop) är inte lämpliga för inline

• Funktioner som är rekursiva kan vara olämpliga, liksom funktioner med statiska variabler

• Funktionspekare till en funktion som deklarerats inline skapar en funktion i minnet som man kan peka till

inline eller inte?

inline eller inte?

När bör man undvika att använda inline?

Använd inline där du lokaliserat en flaskhals

• Överdrivet användande ger märkbart större exekverbara program pga expansion av koden (code bloat)

inline kan ge långsammare kod pga sidfel (page faults)

inline eller inte?

När bör man undvika att använda inline?

inline kan öka kompileringstiderna

eftersom programkoden ligger i headerfilerna

• Använda objekt måste finnas definierade

• Ändringar ger omkompilering pga beroenden

(4)

char foo(int i) {

return i % 7;

}

// utan inline och utan optimering int i = 9;

char c = "abcdefg"[foo(i)];

// med inline och optimering int i = 9;

char c = 'c';

Exempel på snabbare program med inline:

Optimering mellan funktionsanrop är svårt, med inline blir det lättare

inline inline och 90-10-regeln

90-10 regeln (även känd som 80-20-regeln):

• De flesta program spenderar 90% av körtiden i 10% av koden

• De funktioner som utför arbetet vill man snabba upp

• Felhanteringkod används sällan och bör inte göras inline

inline vid behov

void foo() { if(error) {

/* 500 rader felhantering */

}

/* 10 rader kod */

}

for(int i = 0; i < 100000; i++) foo(); // foo är tidskritisk /* foo används på flera andra ställen */

Betrakta följande exempel: Vi konstaterar följande:

Funktionen foo är tidskritisk

Gör vi foo() inline får vi en stor ökning av kodens storlek pga felhanteringsrutinen Vi väljer att dela upp foo:

• Den del av koden som ska exekvera snabbt gör vi inline

• Felhanteringen låter vi ske genom funktionsanrop

inline vid behov

(5)

inline // vi gör foo inline void foo() {

if(error) {

foo_handle_error(); // ej inlineanrop }

/* 10 rader kod */

}

void foo_handle_error() { // felhanteringen ej inline /* 500 rader felhantering */

}

for(int i = 0; i < 100000; i++)

foo(); // foo expanderar till < 15 rader kod

Vårt nya, effektiva program blir:

inline vid behov

Temporära objekt Temporära objekt

Onödiga instruktioner Kopiering kontra

initiering Referenser

Temporära objekt och kopiering

För att slippa onödiga instruktioner vill man undvika att skapa temporära objekt:

Vid anrop: skicka referenser istället för objekt där så är möjligt.

• Omformulera uttryck för att undvika temporära objekt.

Att slippa temporära objekt:

Använd initiering istället för tilldelning, där så är möjligt

Använd referenser för att undvika call- by-value

• Var medveten om operatoioner som skapar temporärer

Temporära objekt och

kopiering

(6)

std::string s;

std::string r("foo");

s = r + "bar"; // temp. objekt som innehåller "foobar"

s += r; // bättre: inget temp. objekt s += "bar"; // samma resultat

Exempel på hur man undviker temporära objekt:

Temporära objekt och kopiering

Annat, vanligt förekommande, exempel på onödiga instruktioner

c l a s s H u g e {

H u g e ( ) : e ( 0 ) { h e a v y ( ) ; } H u g e ( i n t i ) : e ( i ) { h e a v y ( ) ; } H u g e & o p e r a t o r = ( c o n s t H u g e & h ) { e = h . e ; h e a v y ( ) ; r e t u r n * t h i s ; }

h e a v y ( ) { f o r ( i n t i = 0 ; i < 1 0 0 0 0 0 0 0 ; i + + ) ; } i n t e ;

} ;

c l a s s A { H u g e e ;

A ( ) { e = 7 ; } / / k ö r h e a v y t r e g å n g e r A ( i n t ) : e ( 7 ) { } / / k ö r h e a v y e n g å n g } ;

Temporära objekt och kopiering

Specialiserad minneshantering Specialiserad minneshantering

De globala operatorerna ::new

och ::delete Minnespool för dynamisk allokering

Specialiserad minneshantering

De inbyggda new och delete är byggda för att klara samtliga krav på minnesallokering.

• Eftersom de är generella kan de vara för långsamma i vissa sammanhang.

Man kan då skapa specialbyggda new

och delete som sköter allokeringen.

(7)

• Om man ska allokera många objekt av samma typ kan man minska körtiderna genom att specialisera minnes-

allokeringen:

Vi använder den inbyggda new för att allokera stora mängder minne; denna mängd kallar vi vår minnespool.

• Minnet delas sedan ut i lagom stora portioner i vår egen new.

Minnespool

/* Fraction definierar inte new och delete */

class Fraction {...};

// ineffektivt att allokera 5M objekt ett i taget for(int i = 0; i < 5000000; i++)

{

Fraction *f = new Fraction;

foo(f);

delete f;

}

Vi har följande program som vi vill optimera:

Minnespool - huvudfil

Minnespool

Vi skapar en mallklass Pool:

• Hanterar minnespoolen

• Allokerar större stycken minne för prestanda

• Styckar minne och delar ut till objekt

• Allockerar mer minne vid behov

• Länkar lediga objekt genom att återanvända det minne som objektet ska uppta

/ / i f i l e n p o o l . h t e m p l a t e < c l a s s T >

c l a s s P o o l { p u b l i c : P o o l ( ) ; ~ P o o l ( ) ;

v o i d * a l l o c ( ) ; / / h ä m t a e n m i n n e s c e l l v o i d f r e e ( v o i d * ) ; / / l ä m n a t i l l b a k a m i n n e s c e l l p r o t e c t e d :

v o i d e x p a n d ( ) ; / / u t ö k a m i n n e t

e n u m { E X P A N D = 6 5 5 3 6 } ; / / a l l o k e r a d s t o r l e k i b y t e s . . .

s t d : : v e c t o r < T * > b l o c k s ; / / b l o c k m e d m i n n e

s t r u c t L i s t { / / l ä n k a d l i s t a a v c e l l e r L i s t * n e x t ; / / p e k a r e t i l l n ä s t a c e l l } f r e e l i s t ; / / l e d i g a c e l l e r } ;

# i n c l u d e " p o o l . i n l " / / t e m p l a t e k l a s s e n s i m p l e m .

Minnespool - headerfil

(8)

/ / i f i l e n p o o l . i n l t e m p l a t e < c l a s s T >

P o o l < T > : : P o o l ( ) : b l o c k s ( 1 0 0 ) {

a s s e r t ( s i z e o f ( T ) > = s i z e o f ( P o o l < T > * ) ) ; f r e e l i s t . n e x t = 0 ;

e x p a n d ( ) ; }

t e m p l a t e < c l a s s T >

P o o l < T > : : ~ P o o l ( ) {

s t d : : v e c t o r < T * > : : i t e r a t o r i t ;

f o r ( i t = b l o c k s . b e g i n ( ) ; i t ! = b l o c k s . e n d ( ) ; i t + + ) d e l e t e [ ] * i t ;

}

t e m p l a t e < c l a s s T >

i n l i n e

v o i d * P o o l < T > : : a l l o c ( ) {

i f ( ! f r e e l i s t . n e x t ) e x p a n d ( ) ;

L i s t * t m p = f r e e l i s t . n e x t ; f r e e l i s t . n e x t = t m p - > n e x t ; r e t u r n r e i n t e r p r e t _ c a s t < v o i d * > ( t m p ) ; }

Minnespool - implementation Minnespool - implementation

/ / i f i l e n p o o l . i n l t e m p l a t e < c l a s s T >

i n l i n e

v o i d P o o l < T > : : f r e e ( v o i d * p ) {

L i s t * t m p = r e i n t e r p r e t _ c a s t < L i s t * > ( p ) ; t m p - > n e x t = f r e e l i s t . n e x t ;

f r e e l i s t . n e x t = t m p ; }

t e m p l a t e < c l a s s T >

v o i d P o o l < T > : : e x p a n d ( ) {

i n t s i z e = E X P A N D / s i z e o f ( T ) ; T * m e m = n e w T [ s i z e ] ; b l o c k s . p u s h _ b a c k ( m e m ) ;

L i s t * o l d p = r e i n t e r p r e t _ c a s t < L i s t * > ( m e m ) ; o l d p - > n e x t = f r e e l i s t . n e x t ;

f o r ( i n t i = 0 ; i < s i z e - 1 ; i + + ) { L i s t * n e w p =

r e i n t e r p r e t _ c a s t < L i s t * > ( m e m + i + 1 ) ; n e w p - > n e x t = o l d p ;

o l d p = n e w p ; }

f r e e l i s t . n e x t = o l d p ; }

Minnespool - testklass

class Fraction {

public:

Fraction(int d = 0, int n = 1) : n_(n), d_(d) {}

void *operator new(size_t size) { return pool.alloc(); }

void operator delete(void *p, size_t) { pool.free(p); }

protected:

int n_; // nominator int d_; // denominator ...

protected:

static Pool<Fraction> pool; // deklaration };

Den optimerade huvudfilen blir:

clock_t start = clock();

for(int i = 0; i < 5000000; i++) {

Fraction *f = new Fraction;

foo(f);

delete f;

}

std::cout << "testen tog (s): "

<< ((double)clock()-start) / CLOCKS_PER_SEC << std::endl;

Minnespool - huvudfil

(9)

Mätningar ger:

Utan minnespool - testen tog (s): 2.37

Med minnespool - testen tog (s): 0.52

Hastighetsökning = 2.37/0.52 = 4.56 ggr

Minnespool - resultat

References

Related documents

Om jag vill framföra orgelmusik utifrån den tradition som gällde på Bachs tid borde jag uppenbarligen inspireras av Bachs tillvägagångssätt i citatet ovan och

När det kommer till beräkningsmetoder förklarar Björling och de Hollanda (2016) att de inte använder några specifika kalkyler just för miljöinvesteringar.. De använder sig

Helt central för skogsvårdslagens skogsskydd, eller till och med skogs- skyddet överhuvudtaget, är 30 § skogsvårdslagen som ger Skogsstyrelsen möjlighet att

The surface topography is the field of study, which deals with the out-of-plan (z-directional) surface structure properties. In a broad sense, it comprises the surface

The energy efficiency and daylight analysis of the earlier functionalist style buildings, Villa Savoye and Fall- ingwater, shows that the main temperature variation in these

I slutet av årskurs 4 framkommer det att eleverna inte innehar en lika hög grad av motivation eller känner lika mycket trivsel i skolan som de gjorde i slutet av årskurs 3

(a) Assuming a match score of 2, a mismatch score of -1 and a gap score of -2, derive the score matrix for a local alignment of &#34;GAAC&#34;..

Vi betraktar en funktion