Garbage Reduction

From Kerbal Space Program Wiki
Jump to: navigation, search

Some of this information may be incorrect, speculative or out of date. Unity provides an [1] with more up to date and accurate information. As of version 1.4, Kerbal Space Program runs on Unity 2017.1, which, for example, reduces the concern about foreach loops (problematic for Unity versions before 5.5). All advice is situational and the largest source of garbage collection issues in most user made plugins will be lack of/poor design.

What is garbage, and why is it bad?

C# is a garbage-collected language, which means that the programmer doesn't have to worry about deleting variables when done with them; the system will do it automatically.

However, the way it does this is by freezing the system and then walking through the list of variables and removing the unused ones ("garbage" variables). This can cause stutters, and the stutters will be more frequent and longer the more garbage there is.

Measuring garbage production

MemGraph can show you how much garbage KSP is creating. It is not broken down by mod, so to test a specific mod you'll need a copy of KSP with only that mod installed.

What does Squad say about garbage?

"removing old foreach() loops, Singletons and generally de-linqing the game. We recommend that modders to the same in anticipation of the next update to help increase performance for all KSP players. Linq and foreach() loops in particular create a lot of garbage, which is what causes the stutter that some people experience while running the game."

Best practices to avoid creating garbage

avoid temporary variables

The fewer variables you create and destroy, the fewer garbage is created. Keeping a variable around between uses will increase total memory usage, but reduce garbage.

Especially try to avoid declaring variables inside loops.

avoid foreach

Replace "foreach" with "for".

Less garbage, but goes through the list backwards:

for (int i = <something>.Length - 1; i >= 0; --i)

One extra variable, but goes through the list forwards:

int count = <something>.Length; // (or .Count or whatever works for the object concerned)
for (int i = 0; i < count; ++i)

If you're using Visual Studio instead of MonoDevelop, replacing foreach is not strictly necessary in some cases:

"foreach" can be ok in Visual Studio "if the GetEnumerator function of the object you are doing it on returns a struct type. If it returns a class type or an interface then it will cause garbage with any compiler. System classes like List<T> do return a struct but some of the KSP classes that are basically a wrapper around a List<T> return an interface (e.g. PartResourceList). These are on the list for fixing for KSP 1.2 though with that sort of nesting, you will probably get better performance with plain for loops (using foreach also requires the code to make two function calls to the enumerator object every time around the loop)."

"You don't need to convert all your foreach loops into plain for loops unless the thing being enumerated happens to return an interface or class from GetEnumerator (even Squad no longer *need* to avoid foreach because Unity have updated the included compiler to fix the issue)."

However if you want a hard and fast rule, "don't use foreach" is pretty simple.

avoid Singletons

No info on this.

remove "using System.Linq"

Linq creates a lot of garbage. Don't use it.

"the quickest way to check if you are using Linq is to delete that using line if it's there. Then everything with Linq will have errors. And there are many more things other than the From, Select that use Linq; lots of methods that are used to modify or specify certain members of a list use it, like Any, OfType, Where, First, FirstOrDefault, etc..."

don't loop through all Parts/Resources/whatever every frame

Looping through every part on the ship is expensive. Don't do this every frame, but instead get notified when the ship changes and then re-scan it. Same for resources, or anything else that you can cache and then get notified when you need to act.

List<Part> partsICareAbout = new List<Part>();
 
Start()
{
  rescanMethod(); // run this at startup
 
  GameEvents.onVesselChange.Add(rescanMethod);
  // onVesselChange - switching between vessels with [ or ] keys
 
  GameEvents.onVesselStandardModification.Add(rescanMethod);
  // onVesselStandardModification collects various vessel events and fires them off with a single one.
  // Specifically - onPartAttach,onPartRemove,onPartCouple,onPartDie,onPartUndock,onVesselWasModified,onVesselPartCountChanged
}
 
public void rescanMethod(Vessel gameEventVessel=null)
{
  // scan the ship for the parts you need, and save them in partsICareAbout
}