Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Eliminate memory pressure/garbage generation in sim performance critical path #1731

Merged
merged 7 commits into from
Mar 12, 2023
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>
krzychu124 marked this conversation as resolved.
Show resolved Hide resolved
/// 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) {
krzychu124 marked this conversation as resolved.
Show resolved Hide resolved
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