2. Objectives
• To get an overview of techniques for preventing bugs beforehand
• To learn how to track down and properly remove bugs from your
code
• To understand possible performance bottlenecks
2 / 55
3. Why you should always start debugging immediately
• Code entropy says your code will get worse, all the time, unless you
actively invest in preventing that
• Broken windows theory says the worse your code is, the worse it will
become
• Tracking down bugs is harder in a larger code base
• Tracking down bugs is harder in a buggy code base
3 / 55
6. Code Quality Tools
6 / 55
/// <summary>
/// Attaches the passed component to the entity with the specified id.
/// Note that this manager does not check whether the specified id is valid.
/// </summary>
/// <exception cref="ArgumentNullException">
/// Passed component is null.
/// </exception>
/// <exception cref="InvalidOperationException">
/// There is already a component of the same type attached.
/// </exception>
public void AddComponent(int entityId, IEntityComponent component)
{
if (component == null)
{
throw new ArgumentNullException("component");
}
if (this.components.ContainsKey(entityId))
{
throw new InvalidOperationException(
"There is already a component of type " + component.GetType() + " attached to entity with id "
+ entityId + ".");
}
this.components.Add(entityId, component);
this.OnComponentAdded(entityId, component);
}
7. You need a repro. Period.
How can you be sure you’ve fixed it?
7 / 55
8. Stack Traces
Object reference not set to an instance of an object
at LifeApplication.Initializer.CreateManagers () [0x00488] in
Initializer.cs:481
at LifeApplication.Initializer.OnLoad () [0x00016] in
Initializer.cs:235
8 / 55
9. What’s null here?
// Initialize progress manager.
var progressConfig = new ProgressConfig(this.unityLoader.Version.Code);
if (this.config.Progress.Encrypt)
{
progressConfig.SetEncryption(
this.config.Progress.Encryption.EncryptKey,
this.config.Progress.Encryption.EncryptIv);
}
9 / 55
10. What’s null here?
// Initialize progress manager.
var progressConfig = new ProgressConfig(this.unityLoader.Version.Code);
if (this.config.Progress.Encrypt)
{
progressConfig.SetEncryption(
this.config.Progress.Encryption.EncryptKey,
this.config.Progress.Encryption.EncryptIv);
}
10 / 55
11. What’s null here?
// Initialize progress manager.
var progressConfig = new ProgressConfig(this.unityLoader.Version.Code);
if (this.config.Progress.Encrypt)
{
progressConfig.SetEncryption(
this.config.Progress.Encryption.EncryptKey,
this.config.Progress.Encryption.EncryptIv);
}
11 / 55
12. What’s null here?
// Initialize progress manager.
var progressConfig = new ProgressConfig(this.unityLoader.Version.Code);
if (this.config.Progress.Encrypt)
{
progressConfig.SetEncryption(
this.config.Progress.Encryption.EncryptKey,
this.config.Progress.Encryption.EncryptIv);
}
12 / 55
13. What’s null here?
// Initialize progress manager.
var progressConfig = new ProgressConfig(this.unityLoader.Version.Code);
if (this.config.Progress.Encrypt)
{
progressConfig.SetEncryption(
this.config.Progress.Encryption.EncryptKey,
this.config.Progress.Encryption.EncryptIv);
}
13 / 55
14. What’s null here?
// Initialize progress manager.
var progressConfig = new ProgressConfig(this.unityLoader.Version.Code);
if (this.config.Progress.Encrypt)
{
progressConfig.SetEncryption(
this.config.Progress.Encryption.EncryptKey,
this.config.Progress.Encryption.EncryptIv);
}
14 / 55
32. Some are really nasty …
• Remote systems
• Race conditions
• Mobile development, embedded systems, drivers
• “Release Mode only” bugs
32 / 55
33. Quoting My Tutor
“If the bug is not where you expect it to be,
you better start looking for it where you’re not expecting it to be.”
- Hagen Peters
33 / 55
34. Why you should never start optimizing immediately
• Your code base will change over time, a lot, most likely removing
some of the code you’ve spent time on optimizing
• Optimized code tends to be hard to read
▪ … and thus, hard to debug.
34 / 55
45. Fighting CPU Bottlenecks
Pooling
Trades memory for CPU performance.
Re-uses objects to prevent costly construction and destruction.
Requires proper (cheap) reset of pooled objects.
45 / 55
46. Fighting CPU Bottlenecks
Caching
Trades memory for CPU performance.
Stores computed values for later use.
Requires proper cache invalidation whenever the input changes.
46 / 55
47. Fighting CPU Bottlenecks
Bucketing
Trades accuracy for CPU performance.
Distributes computations across multiple frames by dividing operation
into multiple input sets.
Can only be applied if player doesn’t notice difference immediately (e.g.
updating AI just twice per second).
47 / 55
51. Gotcha!
Always turn off logging before
profiling!
Otherwise, disk I/O will lead to
false results.
51 / 55
52. Hint
If no native profiling tools are
available (or applicable), you can
always fall back to utility classes
such as
System.Diagnostics.Stopwatch.
52 / 55
53. Memory Leaks
• allocated memory that is never released
▪ in most cases, the reference or pointer is not even available any
more
▪ if occurring on a regular basis (e.g. every time a level is loaded),
will eventually fill up all available memory and crash the game
• in Ansi C: “no malloc without free”
• in C++: “no new without delete”
▪ in modern C++, usually achieved by the means of smart pointers
55. Memory Leaks
• make sure to always remove all registered event handlers in
languages like C#
• more complicated runtime environments can represent unique
challenges
▪ e.g. Mono heap in Unity
• it might be a good idea to return to an “empty scene” once in a while
and verify all memory has been properly cleaned up
60. 5 Minute Review Session
• Why should you always start debugging immediately?
• Why should you never start optimizing immediately?
• Name a few tools and approaches for tracking down broken code!
• How do you know whether you’ve got an GPU, CPU or memory
bottleneck?
• Name a few techniques for fighting CPU bottlenecks!