The Simplest Change to Improve the Performance of Your C++ Code
How a single character can make your games crash or thrive.
Hello there!
Welcome to Algorithmically Speaking, where we discuss topics on the intersection of Computer Science, Software Engineering, and life.
Todayâs topic is a guest post by
, a former competitive programmer renowned for his extensive experience in C++, computer graphics, and game development.This is the first post in a series that delves into the intricacies of C++. This series is a unique opportunity to learn about this beautiful programming language from a savvy computer scientist with hands-on experience.
Buckle up!
Great things are not done by impulse but by a series of small things brought together.
â Vincent van Gogh
Let's kick things off with a look at some code.
Imagine youâre working on a game engine and have a function that checks collisions between objects. Naturally, you pass in a list of game objectsâletâs say, 1000 of themâto this function like this:
Seems fine, right?
After all, std::vector<GameObject> is the exact type we need, and the function just does its job. But hereâs the twist: this code is silently hogging memory and processing power, all because of how weâre passing in that list.
Since weâre passing objects by value, C++ creates a full copy of all 1000 elements. Thatâs not just 1,000 game objects in memoryâitâs 2000 once the copy is made! In game programming, this duplication can mean youâre doubling hefty assets like textures, models, and animations.
Imagine each game object with its own textures, animations, and modelsâduplicating all that can quickly drain memory and drag down performance. Suddenly, your game might slow down, drop frames, or even lagâall because of a hidden cost in how the objects are passed.
The good news?
We can fix this by adding one character. But conceptually, this small change is actually a big one, so letâs explore why C++ works this way and how to avoid these hidden costs.
But first, let me quickly introduce you to C++!
What Is C++?
C++ is often misunderstood, especially by those just getting started.
Some people think it's a program you can download to start coding immediately. But C++ is a language specificationâitâs like a set of rules describing how the language should work.
To write and run C++ programs, we use a compiler, software that turns your C++ code into machine code that your computer can understand.
Creating a C++ compiler isnât exactly simple, which is why only a few have become industry standards. You might have heard of GCC (GNU Compiler Collection), MSVC (Microsoft Visual C++), and Clangâthese are some of the most widely used. While they all adhere to the same C++ rules, they have their own quirks.
For example, they might display different error messages, compile code at different speeds, or optimize your program in unique ways.
To see what I mean, letâs take a look at this code snippet with a deliberate typo:
I intentionally made a mistake: the variable should be myVariable, but I wrote mVariable instead. If we try to compile this code, hereâs what the error messages look like with different compilers:
(GCC) error: âmyVariableâ was not declared in this scope; did you mean âmVariableâ?
(Clang) error: use of undeclared identifier 'myVariable'; did you mean 'mVariable'?
See the difference? Both compilers are flagging the same issue, but the wording varies.
So why are there multiple compilers if they all do basically the same thing?1
Well, each one brings something a little different to the table. Some prioritize speed, others offer better debugging features, and some are just better suited to certain platforms.
This variety gives developers the flexibility to pick the best tool for the job, whether maximizing speed or monitoring memory usage.
Getting to Grips with C++ Parameters: Value vs. Reference
Before jumping into a solution to the initial problem, letâs get comfortable with how C++ handles function parameters. Understanding this is crucial, and it can often be the key to writing efficient and high-performing code.
So, let's explore why pass-by-reference can often be the better choiceâbut remember, it's not always the answer!
The Basics of Parameter Passing in C++
When we talk about âpassing parametersâ to a function, weâre describing how data moves from the caller to the function itself. C++ offers a few methods to do this, each with unique trade-offs:
Pass-by-Value
This is the default in C++.
When you pass a variable by value, C++ creates a copy of that variable specifically for the function to use. While this approach is safe and prevents the original variable from being modified, it can be inefficient for large data types.
Imagine passing a structure or class containing numerous fieldsâeach function call would require C++ to create a duplicate, taking up extra memory and time.
Pass-by-Reference
When you pass a parameter by reference, the function works directly with the original variable rather than a copy. This allows C++ to save memory and time since no duplication occurs.
References are indicated in the function declaration by the & symbol.2
Whatâs a Reference, Anyway?
In C++, a reference essentially refers to the memory address of a variable.
While the term âreferenceâ might sound confusing, it just boils down to a number representing where the data is located in memory. When you pass a parameter by reference, youâre just passing a pointer (an address) to the variable.
So why not just use references everywhere?
Passing by reference isnât always worth it, especially with smaller data types like int or float, which donât benefit as much from reference-passing due to their minimal memory footprint.3
A Practical Look: Pass-by-Reference in Game Development
To illustrate, letâs examine a common function in game development: lerp, or linear interpolation.
Lerp helps create smooth transitions in animations, like the position of a character moving between two points. We will explore its applications in a future post.
Hereâs an example of a lerp function where all parameters are passed by reference:
This approach technically avoids copying the parameters, but thereâs a hidden cost here.
References in C++ are implemented as pointers under the hood, which means each reference stores a memory address. On a 64-bit system, a pointer takes up 8 bytes, so the three references here occupy 24 bytes in total!
By contrast, if we pass these variables by value instead each float takes up only 4 bytes.
In this case, passing by value saves memory since weâre only using 12 bytes instead of 24. For small data types like float, int, or char, copying values can sometimes be cheaper and faster than managing memory addresses.
You might think that 24 bytes isnât a big deal, but every single byte counts in game development!
Itâs like trying to squeeze the last drop of juice from an orangeâevery bit matters when you're optimizing performance. Those seemingly small amounts can add up quickly, impacting load times and overall gameplay experience.
So, let's not underestimate the power of those bytes.
How Much Memory Does a Pointer Use?
Earlier, I mentioned that a pointer takes up 8 bytes on 64-bit machines, while 32-bit machines use 4 bytes.
It's essential to be aware of this difference, especially when considering the hardware your program might run on.
To determine the exact size of a pointer in your environment, you can use C++âs sizeof operator:
A Simple Change, Big Impact
Now that we better understand the mechanisms of passing parameters to functions in C++, the fix to our initial problem is simple: use a reference.
By passing our vector of game objects as a reference, we avoid duplicating data in memory. Hereâs the optimized version:
With const std::vector<GameObject>&, weâre now working directly with the original vectorâno extra copies are made, and const ensures we donât accidentally modify it.
With this single tweak, weâve transformed our function to be both faster and more memory-efficient.
So, next time you write a function, think about how you pass your data.
Passing by reference can help keep your game running smoothly for extensive collections or complex objects.
Just a simple & can save the dayâand your gameâs performance!
Conclusions
I hope these insights have sparked your curiosity and left you eager to explore the intricacies of C++ further.
Thereâs so much more to explore, and in the upcoming episodes, Iâll cover topics like static variables, smart pointers, the pitfalls of using the std namespace, and the right way to work with vectors.
We'll also delve into the power of inline functions and variables, practical design patterns for graphics programming and game engines, tools to streamline C++ compilation, and plenty of other tips and tricks.
Stay tunedâthereâs a lot more to uncover!
Until next time,
Jesus
Did you like this article? Make sure to đ click the like button.
Is there something you want to add? Make sure to đŹ comment.
Do you know someone who would find this helpful? Make sure to đ share this post.
The latest from Algorithmically Speaking
- Practical Software Development Skills from Competitive Programming 
- The importance of data when making decisions in the engineering industry 
Here is how I can help you further
- Are you interested in my book on graph theory? Check it out. 
- Are you interested in sponsoring this newsletter? Please reach out. 
Get in touch
You can find me on LinkedIn and GitHub.
This newsletter is funded by paid subscriptions from readers like yourself.
If you arenât already, consider becoming a paid subscriber to receive the whole experience!
Please let me know if you want me to cover any specific topics of interest to you. Iâm more than happy to do so.
Have a great day đ,
Alberto
You may also wonder why anyone would bother creating a C++ compiler in the first place. Besides the serious reasons, there's always the option of doing it for fun. After all, some people just enjoy a good challenge!
There is also the Pass-By-Pointer mechanism, with the difference that instead of passing a reference, we pass a raw pointer itself.
When you think about it, C++ only allows passing by value. References, essentially pointers, are just valuesâthey represent memory addresses (basically numbers).












