Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,6 @@
## 2024-06-15 - Optimize Dictionary allocations in C# hot loops
**Learning:** In C#, LINQ `.ToDictionary` allocates a new dictionary, delegates, and enumerators. Using `.Any()` afterwards also introduces another O(N) pass.
**Action:** Replace `.ToDictionary` and subsequent `.Any()` combinations with a manual `Dictionary` pre-allocated by count, populated via a `foreach` loop, and track boolean flags (e.g., `hasFiniteEdges`) inside the same loop to avoid multiple iterations and delegate allocations.
## $(date +%Y-%m-%d) - [Optimize GroupBy Redundant Enumerations]
**Learning:** In C#, applying multiple LINQ `Where()`, `ToList()`, and `Sum()` aggregations inside a `Select` projection on grouped data triggers redundant iterations over the group's elements and creates unnecessary temporary lists and delegate closures.
**Action:** Replace multiple LINQ aggregations on `IEnumerable` groupings with a single `foreach` loop that accumulates all required metrics at once. This shifts the time complexity per group from O(k*N) to strictly O(N) and drastically cuts heap allocations.
25 changes: 8 additions & 17 deletions plan.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,10 @@
1. **Optimize `TemporalNodeTrafficState.Clone`:**
- Replace LINQ `.Select().Clone()` combined with `AddRange()` which causes multiple delegate/enumerator allocations.
- Use pre-sized explicit `foreach` loops to allocate clones directly into the target lists.
1. **Optimize GroupBy Redundant Enumerations in `WorkspacePresentation.cs` (Lines 8651-8678)**
- The method processes `trafficNames` utilizing multiple LINQ `Sum()` calls on `nodeStates` and `allocations` within the projection.
- Refactor this to use a standard `foreach` loop that accumulates all required metrics at once to shift time complexity from O(k*N) to O(N) and reduce heap allocations.

2. **Optimize `GetWeightedUnitCost` Signature:**
- Change the parameter `IEnumerable<TemporalQuantityBatch> batches` to `List<TemporalQuantityBatch> batches`.
- This avoids boxing the `List<T>` enumerator when used in a `foreach` loop, which allocates an `IEnumerator` on the heap when passed via the interface.
2. **Complete pre-commit steps to ensure proper testing, verification, review, and reflection are done.**
- Run tests using `dotnet test`.
- Use `dotnet format` on `MedWNetworkSim.Presentation.csproj` and `MedWNetworkSim.Tests.csproj`.

3. **Optimize `ConsumeFromBatches` Sorting:**
- Replace LINQ `.OrderBy().ThenBy().ToList()` with a pre-sized `List<T>` and `List.Sort()`.
- Because `Sequence` is globally unique via `Interlocked.Increment`, `List.Sort()` behaves deterministically as a stable sort would, avoiding O(N log N) sorting overhead generated by LINQ's multiple enumerators and allocations.

4. **Run pre-commit steps:**
- Complete pre-commit steps to ensure proper testing, verification, review, and reflection are done.

5. **Commit and Submit PR:**
- Run the full test suite (`dotnet test`).
- Run formatting checking (`dotnet format`).
- Create PR with title `⚑ Bolt: [performance improvement]` and the required Bolt description.
3. **Submit the changes.**
- Create a commit for the optimization with the title `⚑ Bolt: [performance improvement]` and the appropriate PR description format.
46 changes: 36 additions & 10 deletions src/MedWNetworkSim.Presentation/WorkspacePresentation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8658,21 +8658,47 @@ private static IReadOnlyList<TrafficSimulationOutcome> BuildTimelineOutcomes(
.Select(pair => pair.Value)
.ToList();

// Bolt: Replaced multiple LINQ .Sum() calls with a single pass to eliminate redundant iterations and delegate allocations.
var stateAvailableSupply = 0d;
var stateDemandBacklog = 0d;
foreach (var state in nodeStates)
{
stateAvailableSupply += state.AvailableSupply;
stateDemandBacklog += state.DemandBacklog;
}

var allocQuantity = 0d;
var allocSaleRevenue = 0d;
var allocTransportCost = 0d;
var allocProductionCost = 0d;
var allocTax = 0d;
var allocProfit = 0d;

foreach (var allocation in allocations)
{
allocQuantity += allocation.Quantity;
allocSaleRevenue += allocation.SaleRevenue;
allocTransportCost += allocation.TotalTransportCost;
allocProductionCost += allocation.TotalProductionCost;
allocTax += allocation.TotalTax;
allocProfit += allocation.Profit;
}

return new TrafficSimulationOutcome
{
TrafficType = trafficType,
RoutingPreference = definition?.RoutingPreference ?? allocations.FirstOrDefault()?.RoutingPreference ?? RoutingPreference.TotalCost,
AllocationMode = definition?.AllocationMode ?? allocations.FirstOrDefault()?.AllocationMode ?? AllocationMode.GreedyBestRoute,
TotalProduction = nodeStates.Sum(state => state.AvailableSupply) + allocations.Sum(allocation => allocation.Quantity),
TotalConsumption = nodeStates.Sum(state => state.DemandBacklog) + allocations.Sum(allocation => allocation.Quantity),
TotalDelivered = allocations.Sum(allocation => allocation.Quantity),
UnusedSupply = nodeStates.Sum(state => state.AvailableSupply),
UnmetDemand = nodeStates.Sum(state => state.DemandBacklog),
TotalSalesRevenue = allocations.Sum(allocation => allocation.SaleRevenue),
TotalTransportCost = allocations.Sum(allocation => allocation.TotalTransportCost),
TotalProductionCost = allocations.Sum(allocation => allocation.TotalProductionCost),
TotalTax = allocations.Sum(allocation => allocation.TotalTax),
TotalProfit = allocations.Sum(allocation => allocation.Profit),
TotalProduction = stateAvailableSupply + allocQuantity,
TotalConsumption = stateDemandBacklog + allocQuantity,
TotalDelivered = allocQuantity,
UnusedSupply = stateAvailableSupply,
UnmetDemand = stateDemandBacklog,
TotalSalesRevenue = allocSaleRevenue,
TotalTransportCost = allocTransportCost,
TotalProductionCost = allocProductionCost,
TotalTax = allocTax,
TotalProfit = allocProfit,
Allocations = allocations
};
}).ToList();
Expand Down