Sarbians changes to use object pooling and other techniques to reduce GC
[VesselSimulator.git] / KerbalEngineer / VesselSimulator / EngineSim.cs
blob:a/KerbalEngineer/VesselSimulator/EngineSim.cs -> blob:b/KerbalEngineer/VesselSimulator/EngineSim.cs
--- a/KerbalEngineer/VesselSimulator/EngineSim.cs
+++ b/KerbalEngineer/VesselSimulator/EngineSim.cs
@@ -23,7 +23,7 @@
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
-
+using Smooth.Pools;
 using UnityEngine;
 
 #endregion
@@ -32,20 +32,41 @@
 {
     public class EngineSim
     {
+        public static readonly Pool<EngineSim> pool = new Pool<EngineSim>(Create, Reset);
+
         private readonly ResourceContainer resourceConsumptions = new ResourceContainer();
 
         public double actualThrust = 0;
         public bool isActive = false;
         public double isp = 0;
         public PartSim partSim;
-        public List<AppliedForce> appliedForces;
+        public List<AppliedForce> appliedForces = new List<AppliedForce>();
 
         public double thrust = 0;
 
         // Add thrust vector to account for directional losses
         public Vector3 thrustVec;
 
-        public EngineSim(PartSim theEngine,
+        private static EngineSim Create()
+        {
+            return new EngineSim();
+        }
+
+        private static void Reset(EngineSim engineSim)
+        {
+            engineSim.resourceConsumptions.Reset();
+            engineSim.actualThrust = 0;
+            engineSim.isActive = false;
+            engineSim.isp = 0;
+            for (int i = 0; i < engineSim.appliedForces.Count; i++)
+            {
+                AppliedForce.pool.Release(engineSim.appliedForces[i]);
+            }
+            engineSim.appliedForces.Clear();
+            engineSim.thrust = 0;
+        }
+
+        public void Set(PartSim theEngine,
                          double atmosphere,
                          double velocity,
                          float maxThrust,
@@ -186,8 +207,9 @@
             }
 
             float flowMass = 0f;
-            foreach (Propellant propellant in propellants)
-            {
+            for (int i = 0; i < propellants.Count; i++)
+            {
+                Propellant propellant = propellants[i];
                 flowMass += propellant.ratio * ResourceContainer.GetResourceDensity(propellant.id);
             }
 
@@ -196,8 +218,9 @@
                 buffer.AppendFormat("flowMass = {0:g6}\n", flowMass);
             }
 
-            foreach (Propellant propellant in propellants)
-            {
+            for (int i = 0; i < propellants.Count; i++)
+            {
+                Propellant propellant = propellants[i];
                 if (propellant.name == "ElectricCharge" || propellant.name == "IntakeAir")
                 {
                     continue;
@@ -206,7 +229,12 @@
                 double consumptionRate = propellant.ratio * flowRate / flowMass;
                 if (SimManager.logOutput)
                 {
-                    buffer.AppendFormat("Add consumption({0}, {1}:{2:d}) = {3:g6}\n", ResourceContainer.GetResourceName(propellant.id), theEngine.name, theEngine.partId, consumptionRate);
+                    buffer.AppendFormat(
+                        "Add consumption({0}, {1}:{2:d}) = {3:g6}\n",
+                        ResourceContainer.GetResourceName(propellant.id),
+                        theEngine.name,
+                        theEngine.partId,
+                        consumptionRate);
                 }
                 this.resourceConsumptions.Add(propellant.id, consumptionRate);
             }
@@ -216,12 +244,16 @@
                 MonoBehaviour.print(buffer);
             }
 
-            appliedForces = new List<AppliedForce>();
             double thrustPerThrustTransform = thrust / thrustTransforms.Count;
-            foreach (Transform thrustTransform in thrustTransforms) {
+            for (int i = 0; i < thrustTransforms.Count; i++)
+            {
+                Transform thrustTransform = thrustTransforms[i];
                 Vector3d direction = thrustTransform.forward.normalized;
                 Vector3d position = thrustTransform.position;
-                appliedForces.Add(new AppliedForce(direction * thrustPerThrustTransform, position));
+
+                AppliedForce appliedForce = AppliedForce.pool.Borrow();
+                appliedForce.Set(direction * thrustPerThrustTransform, position);
+                appliedForces.Add(appliedForce);
             }
         }
 
@@ -230,50 +262,70 @@
             get { return this.resourceConsumptions; }
         }
 
+        // A dictionary to hold a set of parts for each resource
+        Dictionary<int, HashSet<PartSim>> sourcePartSets = new Dictionary<int, HashSet<PartSim>>();
+
+        Dictionary<int, HashSet<PartSim>> stagePartSets = new Dictionary<int, HashSet<PartSim>>();
+
+        HashSet<PartSim> visited = new HashSet<PartSim>();
+
         public bool SetResourceDrains(List<PartSim> allParts, List<PartSim> allFuelLines, HashSet<PartSim> drainingParts)
         {
             LogMsg log = null;
-
-            // A dictionary to hold a set of parts for each resource
-            Dictionary<int, HashSet<PartSim>> sourcePartSets = new Dictionary<int, HashSet<PartSim>>();
-
-            foreach (int type in this.resourceConsumptions.Types)
-            {
-                HashSet<PartSim> sourcePartSet = null;
+            
+            foreach (HashSet<PartSim> sourcePartSet in sourcePartSets.Values)
+            {
+                sourcePartSet.Clear();
+            }
+
+            for (int index = 0; index < this.resourceConsumptions.Types.Count; index++)
+            {
+                int type = this.resourceConsumptions.Types[index];
+
+                HashSet<PartSim> sourcePartSet;
+                if (!sourcePartSets.TryGetValue(type, out sourcePartSet))
+                {
+                    sourcePartSet = new HashSet<PartSim>();
+                    sourcePartSets.Add(type, sourcePartSet);
+                }
                 switch (ResourceContainer.GetResourceFlowMode(type))
                 {
                     case ResourceFlowMode.NO_FLOW:
                         if (this.partSim.resources[type] > SimManager.RESOURCE_MIN && this.partSim.resourceFlowStates[type] != 0)
                         {
-                            sourcePartSet = new HashSet<PartSim>();
+                            //sourcePartSet = new HashSet<PartSim>();
                             //MonoBehaviour.print("SetResourceDrains(" + name + ":" + partId + ") setting sources to just this");
                             sourcePartSet.Add(this.partSim);
                         }
                         break;
 
                     case ResourceFlowMode.ALL_VESSEL:
-                        foreach (PartSim aPartSim in allParts)
-                        {
+                        for (int i = 0; i < allParts.Count; i++)
+                        {
+                            PartSim aPartSim = allParts[i];
                             if (aPartSim.resources[type] > SimManager.RESOURCE_MIN && aPartSim.resourceFlowStates[type] != 0)
                             {
-                                if (sourcePartSet == null)
-                                {
-                                    sourcePartSet = new HashSet<PartSim>();
-                                }
-
                                 sourcePartSet.Add(aPartSim);
                             }
                         }
                         break;
 
                     case ResourceFlowMode.STAGE_PRIORITY_FLOW:
-                        var stagePartSets = new Dictionary<int, HashSet<PartSim>>();
+
+                        foreach (HashSet<PartSim> stagePartSet in stagePartSets.Values)
+                        {
+                            stagePartSet.Clear();
+                        }
                         var maxStage = -1;
 
                         //Logger.Log(type);
-                        foreach (var aPartSim in allParts)
-                        {
-                            if (aPartSim.resources[type] <= SimManager.RESOURCE_MIN || aPartSim.resourceFlowStates[type] == 0) continue;
+                        for (int i = 0; i < allParts.Count; i++)
+                        {
+                            var aPartSim = allParts[i];
+                            if (aPartSim.resources[type] <= SimManager.RESOURCE_MIN || aPartSim.resourceFlowStates[type] == 0)
+                            {
+                                continue;
+                            }
 
                             var stage = aPartSim.DecouplerCount();
                             if (stage > maxStage)
@@ -281,9 +333,11 @@
                                 maxStage = stage;
                             }
 
-                            if (!stagePartSets.TryGetValue(stage, out sourcePartSet))
-                            {
-                                sourcePartSet = new HashSet<PartSim>();
+                            if (!stagePartSets.TryGetValue(stage, out sourcePartSet) || sourcePartSet.Count == 0)
+                            {
+                                if (sourcePartSet == null)
+                                    sourcePartSet = new HashSet<PartSim>();
+
                                 stagePartSets.Add(stage, sourcePartSet);
                             }
                             sourcePartSet.Add(aPartSim);
@@ -300,14 +354,16 @@
                         break;
 
                     case ResourceFlowMode.STACK_PRIORITY_SEARCH:
-                        HashSet<PartSim> visited = new HashSet<PartSim>();
+                        visited.Clear();
 
                         if (SimManager.logOutput)
                         {
                             log = new LogMsg();
-                            log.buf.AppendLine("Find " + ResourceContainer.GetResourceName(type) + " sources for " + this.partSim.name + ":" + this.partSim.partId);
-                        }
-                        sourcePartSet = this.partSim.GetSourceSet(type, allParts, visited, log, "");
+                            log.buf.AppendLine(
+                                "Find " + ResourceContainer.GetResourceName(type) + " sources for " + this.partSim.name + ":" +
+                                this.partSim.partId);
+                        }
+                        this.partSim.GetSourceSet(type, allParts, visited, sourcePartSet, log, "");
                         if (SimManager.logOutput)
                         {
                             MonoBehaviour.print(log.buf);
@@ -315,11 +371,14 @@
                         break;
 
                     default:
-                        MonoBehaviour.print("SetResourceDrains(" + this.partSim.name + ":" + this.partSim.partId + ") Unexpected flow type for " + ResourceContainer.GetResourceName(type) + ")");
+                        MonoBehaviour.print(
+                            "SetResourceDrains(" + this.partSim.name + ":" + this.partSim.partId + ") Unexpected flow type for " +
+                            ResourceContainer.GetResourceName(type) + ")");
                         break;
                 }
 
-                if (sourcePartSet != null && sourcePartSet.Count > 0)
+
+                if (sourcePartSet.Count > 0)
                 {
                     sourcePartSets[type] = sourcePartSet;
                     if (SimManager.logOutput)
@@ -334,11 +393,13 @@
                     }
                 }
             }
-
+            
             // If we don't have sources for all the needed resources then return false without setting up any drains
-            foreach (int type in this.resourceConsumptions.Types)
-            {
-                if (!sourcePartSets.ContainsKey(type))
+            for (int i = 0; i < this.resourceConsumptions.Types.Count; i++)
+            {
+                int type = this.resourceConsumptions.Types[i];
+                HashSet<PartSim> sourcePartSet; 
+                if (!sourcePartSets.TryGetValue(type, out sourcePartSet) || sourcePartSet.Count() == 0)
                 {
                     if (SimManager.logOutput)
                     {
@@ -349,10 +410,10 @@
                     return false;
                 }
             }
-
             // Now we set the drains on the members of the sets and update the draining parts set
-            foreach (int type in this.resourceConsumptions.Types)
-            {
+            for (int i = 0; i < this.resourceConsumptions.Types.Count; i++)
+            {
+                int type = this.resourceConsumptions.Types[i];
                 HashSet<PartSim> sourcePartSet = sourcePartSets[type];
                 // Loop through the members of the set 
                 double amount = this.resourceConsumptions[type] / sourcePartSet.Count;
@@ -360,14 +421,15 @@
                 {
                     if (SimManager.logOutput)
                     {
-                        MonoBehaviour.print("Adding drain of " + amount + " " + ResourceContainer.GetResourceName(type) + " to " + partSim.name + ":" + partSim.partId);
+                        MonoBehaviour.print(
+                            "Adding drain of " + amount + " " + ResourceContainer.GetResourceName(type) + " to " + partSim.name + ":" +
+                            partSim.partId);
                     }
 
                     partSim.resourceDrains.Add(type, amount);
                     drainingParts.Add(partSim);
                 }
             }
-
             return true;
         }