Toon posts:

[C++] functie of define?

Pagina: 1
Acties:
  • 128 views sinds 30-01-2008
  • Reageer

Verwijderd

Topicstarter
Soms vraag ik me echt af of bepaalde implementaties beter in een functie worden gezet of in een define. Zoals deze bijvoorbeeld:

C++:
1
2
3
4
5
6
7
8
// return the absolute value of a floating point value
const float Absolute(const float num)
{
    return (num < 0)?(-num):(num);
}

// of:
#define Absolute(x) (x<0)?(-x):(x)


Ik dénk dat de laatste het best geoptimaliseerde is... maar 'k ben niet zeker.
Het voordeel van het laatste is dat het voor meer types dan enkel floats werkt...

  • MisterData
  • Registratie: September 2001
  • Laatst online: 27-05 21:51
misschien inline erbij:

C++:
1
2
3
const inline float Absolute(const float num) { 
    return (num < 0)?(-num):(num); 
} 

Verwijderd

Met de define wordt overal, waar deze 'functie' wordt 'aangeroepen', deze inline in de source opgenomen. Voordeel is snelheid, nadeel is langere code.

Het nadeel dat de functie alleen voor floats werkt, zou in OOP eenvoudig op te lossen moeten zijn (maar ik ben geen OO programmeur).

  • Eelis
  • Registratie: Januari 2003
  • Laatst online: 21-02-2015
.

[ Voor 122% gewijzigd door Eelis op 18-02-2015 20:03 ]


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 04:03
Dat ding bestaat allang en heet std::abs. Dat was iets te snel geredeneerd :).

In C++ zou ik zeggen, maak er een (inline) template functie van. Een nadeel van de macro is namelijk dat het argument meerdere keren geëvalueerd kan worden wat irritant is als het argument side effects heeft (denk aan dingen als ++i).

[ Voor 78% gewijzigd door Soultaker op 07-12-2003 17:16 ]


Verwijderd

Topicstarter
'inline' heb ik zelf nog niet gebruikt, omdat ik dacht dat de compiler dit meestal automatisch deed.
't Is trouwens een goed idee om het te 'templatizen' :) *will do*
Bedankt voor de info.

Die std library moet ik toch ook es van dichtbij gaan bestuderen. 'k Heb steeds de neiging om zelf dingen te coden ipv ze uit de library te halen :s

Allen bedankt voor de informatie :)

dit heb ik nu:
C++:
1
2
3
4
5
template <typename type>
const inline type Absolute(type x)
{
    return (x < 0)?(-x):(x);
}

[ Voor 20% gewijzigd door Verwijderd op 07-12-2003 17:22 ]


  • Macros
  • Registratie: Februari 2000
  • Laatst online: 30-04 09:28

Macros

I'm watching...

Als je snelheid wil zou ik geen if else gebruiken. Branches zijn voor een cpu net zo evil als defines voor een programmeur. Er zijn een aantal super snelle bitflip methodes die je wel kan vinden op het net, welke je makkelijk in defines kan zetten:
http://aggregate.org/MAGIC/

Zo heb je ze ook voor min en max. Nadeel is dat ze alleen op 1 datatype werken. Voordeel is dat ze je cpu pipeline niet laten stallen als je cpu toevallig de verkeerde branch gokt.

"Beauty is the ultimate defence against complexity." David Gelernter


Verwijderd

Topicstarter
Macros schreef op 07 december 2003 @ 17:48:
Als je snelheid wil zou ik geen if else gebruiken. Branches zijn voor een cpu net zo evil als defines voor een programmeur. Er zijn een aantal super snelle bitflip methodes die je wel kan vinden op het net, welke je makkelijk in defines kan zetten:
http://aggregate.org/MAGIC/

Zo heb je ze ook voor min en max. Nadeel is dat ze alleen op 1 datatype werken. Voordeel is dat ze je cpu pipeline niet laten stallen als je cpu toevallig de verkeerde branch gokt.
Das een heel erg interessant artikel!! Thanks :) Ik ga het zo nalezen :)

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 09-04 22:08
Soultaker schreef op 07 december 2003 @ 17:14:
Dat ding bestaat allang en heet std::abs. Dat was iets te snel geredeneerd :).
Want? Wat is er mis met std::abs? Is zelfs overloaded, dus werkt ook al op [long] doubles. Te vinden in <cmath>

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 15:32

.oisyn

Moderator Devschuur®

Demotivational Speaker

Macros schreef op 07 december 2003 @ 17:48:
Als je snelheid wil zou ik geen if else gebruiken. Branches zijn voor een cpu net zo evil als defines voor een programmeur. Er zijn een aantal super snelle bitflip methodes die je wel kan vinden op het net, welke je makkelijk in defines kan zetten:
http://aggregate.org/MAGIC/

Zo heb je ze ook voor min en max. Nadeel is dat ze alleen op 1 datatype werken. Voordeel is dat ze je cpu pipeline niet laten stallen als je cpu toevallig de verkeerde branch gokt.
gelukkig gebruiken veel compilers daar gewoon de cmov instructie voor

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 04:03
MSalters schreef op 07 december 2003 @ 19:18:
Want? Wat is er mis met std::abs? Is zelfs overloaded, dus werkt ook al op [long] doubles. Te vinden in <cmath>
In principe niets mis met std::abs, maar het probleem was dat kenvh de abs-functie aanvoerde als voorbeeld en niet als specifiek probleem. De algemene discussie ging over macro's versus inline functies en in het algemeen bestaat zo'n standaardfunctie natuurlijk niet. ;)

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 09-04 22:08
Oh, in dat geval is het simpel. Inline functie; de compiler kan dan namelijk beslissen wat het beste is; debuggen werkt makkelijker; je kunt local variabelen hebben in inline functies en de semantiek is voorspelbaarder.

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


  • Olaf van der Spek
  • Registratie: September 2000
  • Niet online
Soultaker schreef op 07 december 2003 @ 17:14:
Dat ding bestaat allang en heet std::abs. Dat was iets te snel geredeneerd :).

In C++ zou ik zeggen, maak er een (inline) template functie van. Een nadeel van de macro is namelijk dat het argument meerdere keren geëvalueerd kan worden wat irritant is als het argument side effects heeft (denk aan dingen als ++i).
Zelfs als het geen side-effects heeft kan het erg vervelend zijn, want het is (veel) trager, zeker als het binnen een recursieve functie gebeurd.

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 15:32

.oisyn

Moderator Devschuur®

Demotivational Speaker

Waarom zou een macro trager zijn dan een (inline) functie :?

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Verwijderd

.oisyn schreef op 09 december 2003 @ 13:38:
Waarom zou een macro trager zijn dan een (inline) functie :?
Heb het net even met Borland C++Builder 6 geprobeerd maar gek genoeg is een inline-functie sneller.
Hieronder de C++ code met assembler-code erbij:
C++:
1
2
3
4
inline int InlineAbsolute(const int right)
{
  return (right<0 ? -right : right);
}

En dit wordt naar de volgende asm uitgevoerd:
GAS:
1
2
3
4
5
6
7
8
9
10
push ebp;
mov ebp,esp
cmp dword ptr [ebp+0x08],0x00
jnl 0x07
mov eax,[ebp+0x08]
neg eax
jmp 0x03
mov eax,[ebp+0x08]
pop ebp
ret

Nu de macro:
C++:
1
#define DefineAbsolute(x) (x<0 ? -x : x);

Dit wordt vertaald naar:
GAS:
1
2
3
4
5
test eax, eax
jnl 0x0f

neg eax
jmp 0x0b

Snelheidsverschil bij 10000000 van deze acties: +/- 2 seconden in het voordeel van de eerste oplossing (inline functie). Dit is gemeten op een Intel Pentium 4 2,8 GHz.

  • madwizard
  • Registratie: Juli 2002
  • Laatst online: 26-10-2024

madwizard

Missionary to the word of ska

hvdberg: Die eerste ziet er niet echt uit als een inline functie, er wordt een compleet stack frame opgebouwd. Je moet altijd opletten als je dit soort dingen test, met name de assembler output kan nogal verschillen als je extra code toevoegt of bepaalde condities gelden (bijvoorbeeld als de input een constante is wordt er niet eens code gegenereerd). VC 7.1 maakt er iig in beide gevallen dit van:
code:
1
2
3
4
  test eax, eax
  jge  .pos
  neg  eax
.pos:


edit:
Je krijgt trouwens wel verschil als je de functies nest (wat zowieso nutteloos is en weggeoptimaliseerd zou kunnen worden):

C++:
1
2
3
4
5
6
7
8
9
int entry(int val)
{
    return DefineAbsolute(DefineAbsolute(val));
}
//--- of ---
int entry(int val)
{
    return InlineAbsolute(InlineAbsolute(val));
}


De eerste genereert:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    mov       eax,[esp][00004]
    test      eax,eax
    mov       ecx,eax
    jge      .a 
    neg       ecx
.a:
    test      ecx,ecx
    jge      .b  
    test      eax,eax
    jge      .c   
    neg       eax
    neg       eax
    retn
.b:
    test      eax,eax
    jge      .d  
.c: 
    neg       eax
.d: 
    retn

Een hoop nutteloze code dus (zelfs 2x neg eax achter elkaar 8)7), terwijl de inline variant er dit van maakt:

code:
1
2
3
4
5
6
7
8
9
    mov       eax,[esp][00004]
    test      eax,eax
    jge      .a  
    neg       eax
    test      eax,eax
    jge      .a  
    neg       eax
a:
    retn

Een stuk kleiner dus, maar wel valt op dat ook deze niet optimaal is, er staat eigenlijk gewoon 2 keer dezelfde code achter elkaar, terwijl de 2e weg zou kunnen. Maar iig al een stuk beter dan die define.

Omdat bij die define de functie binnen de haakjes gewoon 3 keer als 'x' gebruikt wordt in dezelfde functie, is het voor de compiler waarschijnlijk lastiger de verbanden te zien dan bij een inline functie die 1 keer genest is. De inline variant is hierbij dus ook duidelijk in het voordeel.

Conclusie: geen defines gebruiken voor iets dat je ook als inline functie kunt maken :).

[ Voor 63% gewijzigd door madwizard op 09-12-2003 15:44 ]

www.madwizard.org


  • Olaf van der Spek
  • Registratie: September 2000
  • Niet online
.oisyn schreef op 09 december 2003 @ 13:38:
Waarom zou een macro trager zijn dan een (inline) functie :?
Omdat de argumenten meerdere keren geevalueerd (kunnen) worden.

  • curry684
  • Registratie: Juni 2000
  • Laatst online: 12-05 22:23

curry684

left part of the evil twins

OlafvdSpek schreef op 09 december 2003 @ 16:12:
[...]

Omdat de argumenten meerdere keren geevalueerd (kunnen) worden.
Da's natuurlijk een onzinargument, dat gebeurt bij inlines net zo hard als je het toestaat. De compiler expandeert gewoon je code en compileert 'm als onderdeel van de omliggende code, en voor een inline exact hetzelfde. Dat er in het geval van hvdberg toevallig verschillende code uitkomt is stom toeval en lijkt me toe te schrijven aan een compilerfout. Het zou gewoon bij een goede compiler identiek en even snel moeten zijn.

Professionele website nodig?


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 15:32

.oisyn

Moderator Devschuur®

Demotivational Speaker

OlafvdSpek schreef op 09 december 2003 @ 16:12:
[...]

Omdat de argumenten meerdere keren geevalueerd (kunnen) worden.
dan moet je je macro's goed schrijven zodat dat niet gebeurt

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 09-04 22:08
Inline functies kunnen ook sneller zijn dan macros als de compiler uitvindt dat inlining in een bepaalde non-inline functie die functie over een kritische grens duwt. Bijv een 60 bytes grote functie, die een 16 bytes inlined functie aanroept. Is het een winst om die 16 bytes te inlinen, als je daardoor meer dan een cacheline gaat gebruiken?
De grap is dat de compiler voor een inline functie voor elke situatie apart die afweging kan maken, maar een macro moet (bijna) altijd lokaal geexpandeerd worden.

PS. Ander voordeel van inine functies: ze kunnen recursief zijn, en goede compilers doen dan een loop unroll gebaseerd op de grootte van de inline functie.

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


  • Olaf van der Spek
  • Registratie: September 2000
  • Niet online
curry684 schreef op 09 december 2003 @ 17:10:
Da's natuurlijk een onzinargument, dat gebeurt bij inlines net zo hard als je het toestaat. De compiler expandeert gewoon je code en compileert 'm als onderdeel van de omliggende code, en voor een inline exact hetzelfde. Dat er in het geval van hvdberg toevallig verschillende code uitkomt is stom toeval en lijkt me toe te schrijven aan een compilerfout. Het zou gewoon bij een goede compiler identiek en even snel moeten zijn.
Hoe kan dat bij een inline functie optreden dan? Dat zou namelijk betekenen dat de uitvoer van je code afhankelijk kan zijn van of inlineing wordt toegepast of niet en dat lijkt me toch niet de bedoeling.
.oisyn schreef op 09 december 2003 @ 17:12:
[...]


dan moet je je macro's goed schrijven zodat dat niet gebeurt
Dat is makkelijker gezegd dan gedaan (zie boven).

[ Voor 28% gewijzigd door Olaf van der Spek op 09-12-2003 19:32 ]


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 09-04 22:08
Om te beseffen waarom inlining anders werkt dan macro's moet je weten dat macro's de compiler nooit halen; ze worden voor het parsen al verwijderd. Inlining gebeurt typisch nadat de code al gecompileerd is; de code fragmenten moeten dan samen in een object file worden gezet. Op dat moment moeten de (relatieve) adressen van functies worden vastgelegd. Vlak daarvoor weet je dus in principe hoe groot elke functie is, behalve dat sommige functies andere inlined functies aanroepen. Voorafgaande stappen kunnen de grootte van functies nog aanpassen (je mag hopen dat de optimzier dat doet :) ) wat inlinen moeilijk maakt, en erna kan het natuurlijk niet meer zo simpel.

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


  • Count
  • Registratie: Augustus 2000
  • Laatst online: 10-08-2023
Een goed idee is absoluut niets te doen aan optimalisatie voordat je de snelheid nodig hebt.

Ook bij realtime applicaties geldt dat profiling altijd nog goed achteraf kan en dat de meeste optimalisatie in de eerste instantie alleen leid tot extra debug problemen.

Naja, inlining is IMO altijd wel aan te raden bij de meest primitieve functies omdat je daar nog gewoon typechecking e.d. hebt.

Great minds think in parallel gutters.


  • farlane
  • Registratie: Maart 2000
  • Laatst online: 22-05 16:53
Count schreef op 09 december 2003 @ 22:25:
Ook bij realtime applicaties geldt dat profiling altijd nog goed achteraf kan en dat de meeste optimalisatie in de eerste instantie alleen leid tot extra debug problemen.
Als je een applicatie schrijft voor een platform waarbij je van te voren weet dat performance een probeem kan gaan worden moet je vanaf het begin ontwerpen en implementeren met dit in het achterhoofd.

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 09-04 22:08
Bij realtime applicaties is optimalisatie vaak ongewenst. Het idee van realtime is niet dat het snel is, het idee is dat het op tijd is. Dat betekent dat je van elke functie moet weten hoe lang deze er maximaal over doet. Sommige optimalisaties hebben tot gevolg dat de worst case verslechterd, en die zijn dus negatief in real-time applicaties - ook al verbetert de average case.

Je hebt evengoed wel profiling nodig, om de worst-case van elke geschreven functie te bepalen. Dat is dus validatie(testen) en geen optimalisatie.

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


  • Count
  • Registratie: Augustus 2000
  • Laatst online: 10-08-2023
farlane schreef op 11 december 2003 @ 09:14:
[...]


Als je een applicatie schrijft voor een platform waarbij je van te voren weet dat performance een probeem kan gaan worden moet je vanaf het begin ontwerpen en implementeren met dit in het achterhoofd.
Dat is natuurlijk ook waar. Als je voor een mobieltje/GBA iets aan het schrijven bent dan moet je realistisch inschatten wat het apparaatje kan trekken.

Mijn punt is dat men de snelheid van de hedendaagse CPU's nog wel eens wil onderschatten en het onderste uit de kan haalt om een paar CPU cycles te besparen. Terwijl je die moeite veel beter kan stoppen in wat echt belangrijk is; het functioneren van je programma.

Zelfs voor een trage CPU zou ik altijd focussen op het werkend te krijgen en pas achteraf eventueel de snelheid opvoeren.

Great minds think in parallel gutters.


Verwijderd

Topicstarter
Het ging hier net echt om over te vroeg optimaliseren, maar gewoon om teweten meer te weten te komen over define vs. functies, opdat ik bepaalde ontwerpen in m'n programma (3D engine) al degelijk kan schrijven zodat ik in de toekomst niet alles moet gaan optimaliseren. Bij de eerste versie van de engine wist ik een aantal bottlenecks(bvb collision detection), dus dat is wat ik in orde wil krijgen.
Pagina: 1