Skip to content

Commit

Permalink
Merge pull request #1731 from CitiesSkylinesMods/bugfix/eliminate-mem…
Browse files Browse the repository at this point in the history
…ory-pressure-hot-path

Eliminate memory pressure/garbage generation in sim performance critical path
  • Loading branch information
krzychu124 authored Mar 12, 2023
2 parents 183d209 + 1e4b1c5 commit 8276bab
Show file tree
Hide file tree
Showing 12 changed files with 182 additions and 170 deletions.
21 changes: 1 addition & 20 deletions TLM/CSUtil.Commons/Log.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ namespace CSUtil.Commons {
/// Log.DebugFormat is optimized away, others are not, so is a good idea to wrap in if (boolValue)
/// ⚠ Expensive if not wrapped in a if () condition
///
/// Log.DebugIf, Log.WarningIf, ... - these first check a condition, and then call a lambda,
/// Log.DebugIf -- first check a condition, and then call a lambda,
/// which provides a formatted string.
/// ✔ Lambda building is just as cheap as format args building
/// 💲 The cost incurred: each captured value (pointer) is copied into lambda
Expand Down Expand Up @@ -154,19 +154,6 @@ public static void WarningFormat(string format, params object[] args) {
LogToFile(string.Format(format, args), LogLevel.Warning);
}

/// <summary>
/// Log a warning only if cond is true
/// NOTE: If a lambda contains values from `out` and `ref` scope args,
/// then you can not use a lambda, instead use `if (cond) { Log.Warning() }`
/// </summary>
/// <param name="cond">The condition</param>
/// <param name="formatFn">The function which returns text to log</param>
public static void WarningIf(bool cond, Func<string> formatFn) {
if (cond) {
LogToFile(formatFn(), LogLevel.Warning);
}
}

public static void Error(string s) {
LogToFile(s, LogLevel.Error);
}
Expand All @@ -175,12 +162,6 @@ public static void ErrorFormat(string format, params object[] args) {
LogToFile(string.Format(format, args), LogLevel.Error);
}

public static void ErrorIf(bool cond, Func<string> formatFn) {
if (cond) {
LogToFile(formatFn(), LogLevel.Error);
}
}

/// <summary>Log error only in debug mode.</summary>
/// <param name="s">The text</param>
[Conditional("DEBUG")]
Expand Down
30 changes: 25 additions & 5 deletions TLM/TLM/Manager/Impl/LaneConnection/LaneConnectionSubManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ namespace TrafficManager.Manager.Impl.LaneConnection {
using System;
using System.Collections.Generic;
using System.Linq;
using ColossalFramework;
using CSUtil.Commons;
using TrafficManager.API.Manager;
using TrafficManager.API.Traffic.Data;
Expand Down Expand Up @@ -223,10 +224,7 @@ internal uint[] GetLaneConnections(uint laneId, bool startNode) {

LaneEnd key = new(laneId, startNode);
if (connectionDataBase_.TryGetValue(key, out var targets)) {
return targets
.Where(item => item.Enabled)
.Select(item => item.LaneId)
.ToArray();
return FindEnabledLaneIds(targets);
}
return null;
}
Expand Down Expand Up @@ -460,7 +458,7 @@ private void AssertLane(uint laneId, bool startNode) {
try {
Assert(laneId.ToLane().IsValidWithSegment(), $"IsValidWithSegment() failed for laneId:{laneId}");
if (connectionDataBase_.TryGetValue(new LaneEnd(laneId, startNode), out var connections) &&
connections.Any(item => item.LaneId == laneId)) {
ContainsLaneId(connections, laneId)) {
ushort nodeId = laneId.ToLane().GetNodeId(startNode);
Assert(connections.Length == 1,
$"dead end should only have one connection to itself. " +
Expand Down Expand Up @@ -692,5 +690,27 @@ public bool LoadData(List<Configuration.LaneConnection> data) {

return ret;
}

private bool ContainsLaneId(LaneConnectionData[] laneData, uint laneId) {
foreach (LaneConnectionData connectionData in laneData) {
if (connectionData.LaneId == laneId) {
return true;
}
}
return false;
}

private uint[] FindEnabledLaneIds(LaneConnectionData[] laneData) {
using (PoolList<uint> temp = PoolList<uint>.Obtain()) {
temp.EnsureCapacity(laneData.Length);
foreach (LaneConnectionData connectionData in laneData) {
if (connectionData.Enabled) {
temp.Add(connectionData.LaneId);
}
}

return temp.ToArray();
}
}
}
}
3 changes: 2 additions & 1 deletion TLM/TLM/Manager/Impl/ParkingRestrictionsManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,9 @@ public bool SetParkingAllowed(ushort segmentId, NetInfo.Direction finalDir, bool

if (!flag || !parkingAllowed[segmentId][1 - dirIndex]) {
// force relocation of illegally parked vehicles
ushort tempSegmentId = segmentId;
Singleton<SimulationManager>.instance.AddAction(
() => segmentId.ToSegment().UpdateSegment(segmentId));
() => tempSegmentId.ToSegment().UpdateSegment(tempSegmentId));
}
Notifier.Instance.OnSegmentModified(segmentId, this);
return true;
Expand Down
27 changes: 14 additions & 13 deletions TLM/TLM/Manager/Impl/TrafficPriorityManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -425,9 +425,10 @@ public bool HasPriority(ushort vehicleId,
}

if ((vehicle.m_flags & Vehicle.Flags.Spawned) == 0) {
Log.WarningIf(
logPriority,
() => $"TrafficPriorityManager.HasPriority({vehicleId}): Vehicle is not spawned.");
if (logPriority) {
ushort vehicleIdCopy = vehicleId;
Log.Warning($"TrafficPriorityManager.HasPriority({vehicleIdCopy}): Vehicle is not spawned.");
}
return true;
}

Expand Down Expand Up @@ -636,10 +637,10 @@ private bool IsConflictingVehicle(bool logPriority,
}

if ((incomingState.flags & ExtVehicleFlags.Spawned) == ExtVehicleFlags.None) {
Log.WarningIf(
logPriority,
() => $"TrafficPriorityManager.IsConflictingVehicle({vehicleId}, " +
$"{incomingVehicleId}): Incoming vehicle is not spawned.");
if (logPriority) {
Log.Warning($"TrafficPriorityManager.IsConflictingVehicle({vehicleId}, " +
$"{incomingVehicleId}): Incoming vehicle is not spawned.");
}
return false;
}

Expand Down Expand Up @@ -1204,12 +1205,12 @@ public bool DetectCollision(bool logPriority,
default: // (should not happen)
{
wouldCollide = false;
Log.WarningIf(
logPriority,
() => $" TrafficPriorityManager.DetectCollision({vehicleId}, " +
$"{incomingVehicleId}): Target is going {targetToDir} and " +
$"incoming is coming from {incomingFromDir} (SHOULD NOT HAPPEN). " +
$"would collide? {wouldCollide}");
if (logPriority) {
Log.Warning($" TrafficPriorityManager.DetectCollision({vehicleId}, " +
$"{incomingVehicleId}): Target is going {targetToDir} and " +
$"incoming is coming from {incomingFromDir} (SHOULD NOT HAPPEN). " +
$"would collide? {wouldCollide}");
}
break;
}
}
Expand Down
40 changes: 25 additions & 15 deletions TLM/TLM/Notifier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,35 +16,45 @@ public class Notifier : INotifier

public void OnLevelLoaded() => EventLevelLoaded?.Invoke();

public void OnModified(OnModifiedEventArgs args)
private void OnModified(OnModifiedEventArgs args)
{
SimulationManager.instance.AddAction(() =>
EventModified?.Invoke(args));
}

public void OnSegmentModified(ushort segmentId, object sender = null, object data = null) {
OnModified(new OnModifiedEventArgs {
InstanceID = new InstanceID { NetSegment = segmentId },
Sender = sender,
Data = data,
});
// skip if no listeners
if (EventModified != null) {
OnModified(
new OnModifiedEventArgs {
InstanceID = new InstanceID { NetSegment = segmentId },
Sender = sender,
Data = data,
});
}
}

public void OnNodeModified(ushort nodeId, object sender = null, object data = null)
{
OnModified(new OnModifiedEventArgs
{
InstanceID = new InstanceID { NetNode = nodeId },
Sender = sender,
Data = data,
});
// skip if no listeners
if (EventModified != null) {
OnModified(
new OnModifiedEventArgs {
InstanceID = new InstanceID { NetNode = nodeId },
Sender = sender,
Data = data,
});
}
}

public void OnSegmentNodesModified(ushort segmentId, object sender = null, object data = null)
{
ref NetSegment netSegment = ref segmentId.ToSegment();
OnNodeModified(netSegment.m_startNode, sender, data);
OnNodeModified(netSegment.m_endNode, sender, data);
// skip if no listeners
if (EventModified != null) {
ref NetSegment netSegment = ref segmentId.ToSegment();
OnNodeModified(netSegment.m_startNode, sender, data);
OnNodeModified(netSegment.m_endNode, sender, data);
}
}
}
}
Loading

0 comments on commit 8276bab

Please sign in to comment.