Sunday, October 24, 2010

Creating a Super-Fast Math Library, Part 2: Basics

Before we get to digging into the fun information, I thought it'd be helpful if I described what types I use and a little about my coding style.

A good portion of what I write is in C, but I use C++ namespaces, templates, and classes where appropriate. I always prefer clarity over brevity. Saving space by putting a lot of data on one line makes the code unreadable by everyone but the author (actually, it's unreadable to them as well, they just don't admit it.) Also, there is no such thing as "self documenting code." If I have to mentally trace through the code to understand what it's doing, it needs documentation.

In my engine I have various typedefs set up for standard types. This is one thing that allows me to easily switch platforms and not have to worry about, for instance, the size of an int versus the size of a long.

It's a pretty standard practice, but here's the basic types that I use:

typedef unsigned char      uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef unsigned long long uint64_t;

typedef signed char sint8_t;
typedef signed short sint16_t;
typedef signed int sint32_t;
typedef signed long long sint64_t;

typedef float f32_t;
typedef double f64_t;

There are actually more than these and they are different for different platforms, but this gives you an idea of what they are when I use them later.

After that, I have a couple of macros that align data. Because the Playstation3 uses alignment attributes that follow the data declaration, we have to either double up with pre and post macros or declare a special DECLARE_MY_ALIGNED_VARIABLE(int thing) which I'm not overly fond of. Who knows, maybe one day I'll change my mind.

# define PRE_ALIGN(x) __declspec(align(x))
# define POST_ALIGN(x)

Another thing that you may (or may not...) see is branch prediction hints. MSVC++ optimizes code under the assumption that it will always enter the first case of a conditional. For example:

if (a) // this is predicted as always being true
do_a_stuff(); // which (hopefully...) loads this code in the cache
do_not_a_stuff(); // but doesn't load this code.

When a branch prediction is wrong, the CPU has to grab the code and load it into memory, which takes time. Data and code cache misses kill a program's speed like an army of ants descending on a picnic.

My branch prediction hints are simply defined as this for Windows:

# define LIKELY(x) x
# define UNLIKELY(x) x

In other words... they don't do anything. But, if you see them, you know what they're for.

On a final note about coding conventions, style, and so forth... I tend to adapt to what I need, not pick a particular book style and strictly adhere to it. If I see something I like that suits my needs I'll probably end up using it. I'm always open to new ideas.

No comments:

Post a Comment