C++ Optimization week!

Started by
13 comments, last by Hodgman 10 years, 6 months ago

Hi guys! smile.png

So after a while of programming, I realised that my code is becomming more messy and with slower compilation time.

So I ask you, what things do you commonly optimize, tricks n. stuff. (Speed Wise and Compilation Time Wise)

What I know so far:

  • You should love constants
  • Use class prototyping a lot

Please share!

Thank You

-MIGI0027

FastCall22: "I want to make the distinction that my laptop is a whore-box that connects to different network"

Blog about... stuff (GDNet, WordPress): www.gamedev.net/blog/1882-the-cuboid-zone/, cuboidzone.wordpress.com/

Advertisement

Use class forward declaration where possible (Is this what you mean by prototyping?)

A few things I keep in mind:

- use const where possible, both in member functions as parameters

- pass objects as const reference when their size is 'relatively' big

- don't let 'outsiders' get to crucial class members (make things private, make const 'Get...' functions public)

- use VLD Always in debug mode, detect memory leaks a.s.a.p.

- use &auto when possible to make code more readable

- where possible always use std::vector instead of dynamic arrays (new/delete)

(helps in following 'the rule of 3')

Crealysm game & engine development: http://www.crealysm.com

Looking for a passionate, disciplined and structured producer? PM me

You can also use precompiled headers, or headers that include things that rarely change.

A few things I keep in mind:

- use const where possible, both in member functions as parameters

- pass objects as const reference when their size is 'relatively' big

- don't let 'outsiders' get to crucial class members (make things private, make const 'Get...' functions public)

- use VLD Always in debug mode, detect memory leaks a.s.a.p.

- use &auto when possible to make code more readable

- where possible always use std::vector instead of dynamic arrays (new/delete)

(helps in following 'the rule of 3')

You should reserve the space of vectors as well this will at least in the avarage case help the vector not resize as much.

Check your template usage if you have loads of this it will upset compile times and may even make a compiler cry once in a while.

Batch compiling will also help, look for unity builds for that subject but keep in mind that you should always have a single file compile path in the solution as well.

Worked on titles: CMR:DiRT2, DiRT 3, DiRT: Showdown, GRID 2, theHunter, theHunter: Primal, Mad Max, Watch Dogs: Legion


So after a while of programming, I realised that my code is becomming more messy and with slower compilation time.

Define "slower compilation time".

Two minutes? Five minutes? Twenty minutes? Ninety minutes?

As projects grow there is more to compile, which means it takes more time.

C++ uses a build model that hasn't changed much from the 1960's and 1970's. It generally requires every module to know details about other modules, and as you increase the number of modules the build time grows exponentially. Building a single .cpp file can cause a compiler to open hundreds or even thousands of header files.

Many solutions solve different problems. But you haven't said what your actual problems are other than 'slow build times'.

There are many books on build systems out there. Techniques depend on the actual problems you are having. The PImpl idiom and programming against interfaces reduce coupling between modules and forcing rebuilds based on dependency changes. The concept of a "unity build", essentially having only one extremely large compilation unit, helps keep the compiler from re-opening files. Lower optimization levels can reduce compiling time because some optimizations are incredibly time consuming. Template use and template meta-programming can cause build times to skyrocket because the programmer is willingly replacing runtime processing with build time processing. Distributed builds can share the work between machines. Each of those are solutions to different problems.

Without knowing what your actual problems are, we can blindly offer suggestions but they may not help.

So, while all the suggestions are fine, they don't usually solve the larger issues. Don't get me wrong, I've seen trivial missing references cause massive performance degradation, in fact, for fun:


class ThingGrid
{
public:
  typedef uint32_t                     Thing;
  typedef std::vector< Thing >  ThingArray_t;
 
  const ThingArray_t    operator[]( size_t y ) const    {return mThings[ y ];}
  const Thing               GetThing( size_t x, size_t y ) const   {return mThings[ y ][ x ];}
 
private:
  std::vector< ThingArray_t >  mThings;
};

It is a silly piece of code and obviously just an example, but it works as intended and if we were not talking about optimizations (and this were in say a JavaDoc commented header) it would not jump out as to why the above is really bad. If you didn't catch it immediately, yes returning the uint32_t "Thing" by value is intended but the return of the array is bad since it is also by value, so GetThing is massively slower than it should be thanks to a hidden temporary being made. Of course, fix the reference and things are all better, or are they? Take the following:


for( int x=0; x<things.Width(); ++x )
  for( int y=0; y<things.Height(); ++y )
    {ThingGrid::Thing thing = things.GetThing( x, y ); ... do something ...}

That piece of code is going to perform horribly on multiple levels even with the fix for the reference. Why? Think about it, there are 2 primary problems with that loop as applied to the given class.

[spoiler]

1. First problem is the inner loop iterates on Y while the data is laid out in an sub arrays of x indexed data. So, you are touching new chunks of memory each loop. Fix:


for( int y=0; y<things.Height(); ++y )
  for( int x=0; x<things.Width(); ++x )
    {ThingGrid::Thing thing = things.GetThing( x, y ); ... do something ...}

2. Even with that fix, you still get bit by the fact that as individual std::vectors each one points to different chunks of memory, so each time you complete an x loop you are likely blowing the cache and looking at a completely new chunk of memory. Fixing this means removing the inner vectors and using a single vector with manual y offset to indexing. Of course at that point, you could just use "for( const Thing& : mThings ) {}" and not have two indexes being maintained.

[/spoiler]

So, all said and done, while the simple rules of thumb items are important, I believe knowing your libraries and memory access patterns are much more important to prevent simple things like the above from being constantly adding up performance drains.

  1. Preprocessing macros will significantly slow your compilation.
  2. Do not include unnecessary header files.
  3. Create a build target for speed emphasized compiler settings and work with that as you develop instead of what you usually use (probably a debug build). You can always make use of a debug build when you need to debug.

Compile time tip:

In a header file that doesn't use pointer as members, don't include the header file of the member object, instead do this:


B.h

#ifndef B_H
#define B_H

class A;

class B
{

public:

B();

private:

A* m_a;

}; 

#endif 

B.cpp

#include "A.h"

B::B
{
a = new A();
a->Initialize();
}

Make sure you're using all cores, ie. in VS check the project settings. C/C++ |General | Multi-processor compilation should be enabled. When using make, be sure to use -j <x> to run up to x jobs in parallel.

I noticed that Clang is a lot faster when using precompiled headers compared to GCC (almost as fast as VC++).

And as mentioned before, always be aware of your includes, avoid includes in header files as much as possible, consider using the forward declaration headers for some standard libraries, forward declare your own classes if possible, etc. etc. Once that doesn't help anymore, look into precompiled headers (especially if you use the standard library and/boost almost everywhere).

With a few tricks I could get a verification build from over 4min down to a little over 10s (with VC++). Disable compiler optimization, disable debug info, use precompiled headers and forward declarations, etc. Interestingly the difference with GCC was about a zero and that already took twice as long before. Clang on the other hand would go down to around 20s.

f@dzhttp://festini.device-zero.de

This topic is closed to new replies.

Advertisement