On average, how often is assembly used in modern game code?
Specifically on platforms that already have good C++ compilers, like x86, PPC, or ARM--because I assume games on embedded systems make extensive use of assembly.
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It only takes a minute to sign up.
Sign up to join this communityOn average, how often is assembly used in modern game code?
Specifically on platforms that already have good C++ compilers, like x86, PPC, or ARM--because I assume games on embedded systems make extensive use of assembly.
The answer depends a bit on what you mean by "game", and on "used". I'll assume "used" means "written during the course of the specific game project".
In my experience and anecdotal data from people I've talked to:
Knowledge of assembly language is not expected for work in the games industry, but depending on the type of games you make, it might come in advantageous.
There used to be an argument that the compiler does a better job at optimising C code than a human would with hand-written assembly. Usually that's true, sometimes it's false. But these days a combination of ever-growing CPU complexity and the need to scale 'out' (ie. to separate processors) means that optimisation efforts are usually spent elsewhere.
In recent years the only time I've seen assembly in game code was __asm int 3
statements to force breakpoints, and my only personal use of assembly was in looking at the disassembly of a function to diagnose unusual crash bugs.
The bulk of high performance code in modern console games is written using a sort of middle ground between assembly and C++: compiler intrinsics. These constructs look and parse like C++ functions, but are actually translated into single machine instructions. So, for example, my "clamp each value of vector V to be >= a and <= b" function looks like
// for each v.x, ensure v.x >= a.x && v.x <= b.x
inline __m128 ClampSIMD( const __m128 &v, const __m128 & a, const __m128 & b )
{
return _mm_max_ps( a, _mm_min_ps( v, b ) );
}
In functions like these I'm still thinking in terms of the specific machine instructions, but I have the convenience of writing them in C so that I don't need to worry about register coloring and scheduling and load ops and other boring details.
You still need to be aware of what instructions the CPU supports, especially because modern compilers are terrible at vectorizing code, compared to how well a smart human can do the job. Also sometimes subtle details of how you arrange your code can have huge implications for performance that aren't obvious without understanding what the machine is doing.
Although we may not code in assembly, we still debug in assembly a lot. Optimizing compilers aggressively reorganize code in ways that debuggers can't keep up with, so often when debugging a "release mode" build the best thing to do is pop open the disassembler and trace through the code that way. This GDC talk on "Forensic Debugging" of crashes illustrates many of the whys and hows of debugging at that level.
The problems that used to require hand rolled assembly are getting fewer in number. What you "might" gain in speed you lose in readability and the ability to debug. It should also be done only as one of the very last optimisation steps on sections of code as in most cases speed problems aren't something that can't be made better with assembly. These days CPU's have gotten much faster while memory speeds have not, often it's more important to control how data flows through the CPU than anything else.
With modern compilers they also find it hard to optimise around assembly code as they have to deal with whatever registers you've touched and they can't usually re-order instructions in your hand crafted code. To reduce the need for assembly, there is also now intrinsics which help to get access to low level concepts, but in a way that is compiler friendly and allow them to work with you rather than against.
With that said, the SPU on the PS3 is one area where people are still having to use assembly to get the most out of the processor, with manual instruction pipelining for example as explained here.
The fact is that we live in a multi platform world, and parts of our game code have to be tailored to the local platform - well not have to, but there are benefits if we take advantage of local hardware stuff!
This is not about games or game logic, it's about hardware locality for optimal performance of the code upon which game logic hinges, we can indeed wrap code sections, for example using macros, such that there is one source code, which executes well on the platform it was built against.