Skip to main content
gamedev.net
🔒 Locked

Good ways to optimise/optimize code?

Started by tom76 Nov 6, 2001 at 9:49 AM 31 replies 2.3k views
Original Post
tom76
tom76
I know about making functions inline if they''re to be accessed frequently, but are there some good tips for speeding up code? Mine currently has a lot of if statements which I suspect slows it down. "I envy you, who has seen it all" "And I, young sir, envy you, who have yet to see it for the first time..." - Daniel Glenfield 1st October 2001
if (witisism == !witty) return to_hole
TheFez
TheFez
Another thing that can help is assembly. It makes your code longer but it will speed it up (as long as you write your code well).
tom76
tom76
I don''t know assembly, just C and C++

"I envy you, who has seen it all"
"And I, young sir, envy you, who have yet to see it for the first time..."
- Daniel Glenfield
1st October 2001
if (witisism == !witty) return to_hole
gimp
gimp
I agree. Profile.

Create test data and take timings. Profile, optimise, re-time on same test data, now check your FPS (or whatever metric you use).

Chris Brodie
http:\\fourth.flipcode.com
Chris Brodie
nosfy
nosfy
I think the most obvious optimization is to use the right data structure for the right job.

If you have a bad algorithm or data structure your program will perform badly. You could put all the effort in the world to optimize it but you could''t not compete against a better algorithm (and it doesn''t necessarily mean more complicated).

So the morale is, keep it simple stupid (aka KISS) and straight to the point.

Patrick Lafleur

System Engineer
Creation Objet Inc.
Diodor
Diodor
Has anyone an opinion on AoS vs SoA? (array of structures and reverse)The AoS seems like the C++ way to do things, but the SoA can be considerably faster if the program needs to go through the whole data and perform operations only on some (or just one) of the fields in the structure because of the cache speed-up.

Edited by - Diodor on November 6, 2001 11:54:08 PM
Glandalf
Glandalf
Lots of tricks to do this, but one thing you will want to remember is the 80 - 20 rule. That comes up alot in life btw. Anyway, 80% of the time the user will run 20% of the code. So optimize that.

You can unroll loops, that takes some serious typing to get any significant increase in preformance though. You could also tell the compiler to keep certin variables in the CPU register. This helps some, but if you use an optimize compiler it might do this for you. I''m not sure.

Best thing to do has already been told though, profile.

Glandalf
Just a thought

NuffSaid
NuffSaid
quote:
Original post by TheFez
Another thing that can help is assembly. It makes your code longer but it will speed it up (as long as you write your code well).


Unless you really know what you''re doing, assembly isn''t going to speed things up, but it might actually slow your program down. An optimizing compiler today does a much better job than a poor assemblyly programmer.

==========================================In a team, you either lead, follow or GET OUT OF THE WAY.
Prosper/LOADED
Prosper/LOADED
MadKeithV wrote an article for gamedev about C++ optimisation technics. Check it in the "Articles" section.
Dean Harding
Dean Harding
quote:
Original post by Anonymous Poster
General code optimizing tips: Always use an optimizing compiler. (MSVC Professional, not Standard or Student.)



Just a note, the student version of VC++ is exactly the same as professional, it''s just a lot cheaper. It''s optimizing, it has a profiler, and everything else that''s in professional. Of course, you need to be a full-time student or work for a teaching institution to be able to get it.


codeka.com - Just click it.
NuffSaid
NuffSaid
quote:
Original post by Anonymous Poster
Oops, forgot this... inlining functions won''t save you very much at all (if anything), according to Optimize to the Max - Winning the Passing Game. Use macros instead, it suggests.

A quote: "Looking at the design of modern CPUs it is easy to see that inlining does not provide any performance improvement."

Alistair Keys

.Net defined


The article you quote doesn''t even say anything about macros. How on earth did you reach the conclusion that macros should be used instead of inline functions?

==========================================In a team, you either lead, follow or GET OUT OF THE WAY.
Stoffel
Stoffel
quote:
Original post by Anonymous Poster
A quote: "Looking at the design of modern CPUs it is easy to see that inlining does not provide any performance improvement."



Seeing as how compilers generate the exact same code for inlined functions that they would have generated for macros (given the function is inlinable), this is simply laughable. It''s a typical ploy, though: the quoted author, knowing he doesn''t know what he''s talking about, throws in gobbledegook about CPU design, figuring his audience will be ignorant of CPU design and take his word on faith.
Stoffel
Stoffel
quote:
Original post by Anonymous Poster
Stoffel: I must say, I'm more convinced by a columnist for a respected journal than someone saying "it's all rubbish". Explain why you think it's rubbish, and argue with his results. If you don't agree because of some fact, fine. Otherwise, hold your peace.


I did. When the compiler inlines a function, the assembly code generated by inline functions is exactly the same as the assembly code generated by macros. The same instructions go to the CPU, so the CPU has no way to tell the difference between what is inlined and what is not. Arguing that the CPU is more efficient at running macros than inlined code is nonsense--there is no difference from the CPU's point of view.

Is that enough of an explanation, or do I need to re-re-quote my last post?

Want proof? See "Effective STL", Meyers, Item 46: Consider function objects instead of functions as algorithm parameters. He clocked several platforms on calls to sort using an out-of-line function and a function object which can be inlined. At worst, the object version was 50% faster; at best 160%.

If the column was from a respected journalist and assuming he knows what he's talking about, then I highly suspect he was either misquoted or was trying to say something other than what he ended up saying. Just because a journal is respected doesn't mean that everything in it is true. Classic example: IEEE Signal Processing journal had an article called "Breaking the Nyquist Limit" (hint--it can't be broken, the author made many critical mistakes, and there was fallout for many issues to come).

PS: I believe I provided enough empiric proof (instructions look the same to the CPU regardless of inlining) in my first post, but nevertheless, I do not appreciate being asked to keep my peace.

Edited by - Stoffel on November 12, 2001 2:38:06 PM
Martee
Martee
quote:
Original post by Anonymous Poster
even if a method is inlined, the parameters are still pushed onto the stack or stack-simulating temporary variables. If the method is called or inlined, excessive parameter passing can degrade performance dramatically.

Really?
      

#include <iostream.h>

#include <time.h>

#include <stdlib.h>


#define maxDef(a, b) (a > b ? a : b)


inline int maxFunc(int a, int b)
{
return (a > b ? a : b);
}

int main()
{
srand((unsigned int)time(0));

int x = rand() % 100;
int y = rand() % 100;

int max = maxDef(x, y);
int max2 = maxFunc(x, y);

// Use the variables, or the above code will be optimised out

cout << max << endl;
cout << max2 << endl;

return 0;
}

Now let's have a look at the generated code:

; 18 : int x = rand() % 100;

call _rand
cdq
mov ecx, 100 ; 00000064H
idiv ecx
mov esi, edx

; 19 : int y = rand() % 100;

call _rand
cdq
mov ecx, 100 ; 00000064H
idiv ecx

; 20 :
; 21 : int max = maxDef(x, y);

mov eax, esi
cmp esi, edx
jg SHORT $L1712
mov eax, edx
$L1712:

; 22 : int max2 = maxFunc(x, y);

cmp esi, edx
jg SHORT $L1719
mov esi, edx
$L1719:

I don't see a lot of pushing and popping. Now maybe this simple example isn't representative of the average case, but it certainly looks like inline functions are, well, inlined.

~~~~~~~~~~
"Usewhitespacetoenhancereadability" - Steve McConnell, Code Complete

Edited by - Martee on November 12, 2001 4:10:56 PM
ReactOS - an Open-source operating system compatible with Windows NT apps and drivers
Stoffel
Stoffel
quote:
Original post by Anonymous Poster
P.S. I''ll give you a heart attack yet! Wait until I get onto "goto is okay for optimised code" (joking ).


In the absence of exceptions, that''s perfectly acceptable. No part of the C/C++ language is without purpose or utility (except perhaps keyword auto).

quote:

You might be making the assumption I''m talking about inlining no-parameter functions.


I wasn''t.

quote:

"As I''ve mentioned in my "Winning the Passing Game" column, even if a method is inlined, the parameters are still pushed onto the stack or stack-simulating temporary variables. If the method is called or inlined, excessive parameter passing can degrade performance dramatically. I witnessed a very good example of this unfortunate truth. My first action was to add the /Oy compiler option for frame pointer omission. Later, I tried an even better solution — I replaced all of the "convenience" methods with macros, thus totally eliminating calling/inlining and the parameter-passing hassle".


The author was clearly not working with a modern compiler. Here''s my first shot at code:
  
#include <stdlib.h>

inline int multiplyAccumulate (int mac_reg, int tap, int input)
{
return mac_reg + tap * input;
}

int main ()
{
return (int) multiplyAccumulate (3, 5, 2);
}
//dissassembly

PUBLIC _main
; COMDAT _main
_TEXT SEGMENT
_main PROC NEAR ; COMDAT

; 10 : return (int) multiplyAccumulate (3, 5, 2);

push 13 ; 0000000dH
pop eax

; 11 : }

ret 0
_main ENDP
_TEXT ENDS
END

The compiler evaluated the multiplyAccumulate command at compile time--the program simply returns 13, since that is 3 + 5 * 2.
Second try:
  
#include <stdlib.h>

inline int multiplyAccumulate (int mac_reg, int tap, int input)
{
return mac_reg + tap * input;
}

int main ()
{
return (int) multiplyAccumulate (rand (), rand (), rand ());
}
//disassembly

PUBLIC _main
EXTRN _rand:NEAR
; COMDAT _main
_TEXT SEGMENT
_main PROC NEAR ; COMDAT

; 9 : {

push esi
push edi

; 10 : return (int) multiplyAccumulate (rand (), rand (), rand ());

call _rand
mov edi, eax
call _rand
mov esi, eax
call _rand
imul esi, edi
add eax, esi
pop edi
pop esi

; 11 : }

ret 0
_main ENDP
_TEXT ENDS
END

The math is completely done in registers. The only pushes and pops used are those to straighten the stack (boilerplate for any function call, including main). There are no "stack-simulating temporary variables", unless one includes registers as overhead, in which case one has to put down the crack pipe.

quote:

He then goes on to give sample code, which you should investigate. I''m too lazy to do this, but if you do be sure to report the results.


Yeah, that seems fair. You raise the stink and expect me to disprove it. Well, I already have above to my satisfaction; if you need more proof, do it yourself.

quote:

When I hear your argument, it sounds like religious dogma, of the "I''ve been taught this and I believe it more than you!" school - just like my argument, but from a different perspective.


It may sound that way to you, but my opinions are backed by real-world experience and experimentation. And for future reference, I was taught precious little about programming: my degree and current study is in the area of signal processing, though my profession is software tool development.

quote:

Are you willing to change your opinions when presented with new evidence?


Well, you''ve already been disproven. I hate to get pedantic, but since I''m in a proof-related class currently, that''s where my mindset is:
- Your postulate is that there is no gain from inlining, reason being that inlined functions still use temporary variables that simulate the push/fetch/pop method used in regular function calls.
- Since your postulate is absolute ("there is no gain" instead of "there is sometimes no gain"), a single instance of counter-proof is all that is needed to disprove your postulate.
- Counter-proof provided, postulate disproved.

I wouldn''t normally be so nasty about this, but instead of arguing my statements with code and examples, you''ve tried to paint me as a hard-headded zealot. Optimization is my bread and butter, and I have no reason to ignore an innovation or quirk that allows my code to be run faster. In fact, that''s the very reason I visit these boards, and I do find about one thing a month I never knew before. However, it''s up to me to experiment on my own and decide whether each new bit of knowledge actually works. My job depends on my NOT taking things on faith. This whole "inlining sux0rs, macros rulez0rs" thread comes up about once every two months here. It''s rubbish. There are dependency consequences for inlining, and are therefore to be used sparingly and only when necessary, but that''s not what anybody here was talking about, was it?
Stoffel
Stoffel
And as far as his code goes, "race-horse" arguments are always subject to huge problems when you''re talking about very specific optimizations. Plus, he seems to be testing the age-old fallacy "are member functions slower than non-member functions?". This also pops up every 2 months or so, and it''s also untrue--they each have 6 instructions of overhead (+ overhead per argument). So not only was this author testing two things at once, he wasn''t doing it the right way. The only time you should resort to race-horse tests is when the two algorithms you''re trying to compare are different enough that there''s no point in comparing disassembly. Definitely not the case here.
Stoffel
Stoffel
This pointer is passed through one of the registers. The main difference is that in C, half of the stack push/pop is done in the calling function, the other half done inside the function. In C++, the stack push is mostly the same (except using a register for *this), but the stack pop is entirely in the member function. This is entirely the same number of instructions in all, but it has fooled some people who only look at the function code itself and not the code calling that function (could name names, but won''t).

If you broke down the example you have above for CFoo and disable all inlining & macros, e.g. make sum an out-of-line member function in the class case and a free function in the struct case, each different path has exactly the same number of instructions.
Kylotan
Kylotan
quote:
Original post by Stoffel
No part of the C/C++ language is without purpose or utility (except perhaps keyword auto).

It provides explicit scope info to the reader, much like a comment might

Additionally: "one use of AUTO is to distinguish a declaration-statement from an expression-statement explicitly" (The C++ Programming Language 2nd Ed, pg 516).

Stoffel
Stoffel
Kylotan: Touche''.

re: keyword register: it may not matter much when developing for a PC target. It matters a lot when developing for a 8-bit 10MHz 3-register controller (yeah, I did that, lots of fun, esp. 256 bytes of stack space, 128 of which are reserved).

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.