Optimizes EqualizePressureInZone to use ArrayPool
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using Content.Server.Atmos.Reactions;
|
using Content.Server.Atmos.Reactions;
|
||||||
@@ -28,6 +29,9 @@ namespace Content.Server.Atmos
|
|||||||
[Robust.Shared.IoC.Dependency] private IEntityManager _entityManager = default!;
|
[Robust.Shared.IoC.Dependency] private IEntityManager _entityManager = default!;
|
||||||
[Robust.Shared.IoC.Dependency] private IMapManager _mapManager = default!;
|
[Robust.Shared.IoC.Dependency] private IMapManager _mapManager = default!;
|
||||||
|
|
||||||
|
|
||||||
|
private static readonly TileAtmosphereComparer _comparer = new TileAtmosphereComparer();
|
||||||
|
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
private int _archivedCycle = 0;
|
private int _archivedCycle = 0;
|
||||||
|
|
||||||
@@ -193,6 +197,23 @@ namespace Content.Server.Atmos
|
|||||||
_soundCooldown = 0;
|
_soundCooldown = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class TileAtmosphereComparer : IComparer<TileAtmosphere>
|
||||||
|
{
|
||||||
|
public int Compare(TileAtmosphere a, TileAtmosphere b)
|
||||||
|
{
|
||||||
|
if (a == null && b == null)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (a == null)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (b == null)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return a._tileAtmosInfo.MoleDelta.CompareTo(b._tileAtmosInfo.MoleDelta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void EqualizePressureInZone(int cycleNum)
|
public void EqualizePressureInZone(int cycleNum)
|
||||||
{
|
{
|
||||||
@@ -221,7 +242,7 @@ namespace Content.Server.Atmos
|
|||||||
|
|
||||||
var queueCycle = ++_gridAtmosphereComponent.EqualizationQueueCycleControl;
|
var queueCycle = ++_gridAtmosphereComponent.EqualizationQueueCycleControl;
|
||||||
var totalMoles = 0f;
|
var totalMoles = 0f;
|
||||||
var tiles = new TileAtmosphere[Atmospherics.ZumosHardTileLimit];
|
var tiles = ArrayPool<TileAtmosphere>.Shared.Rent(Atmospherics.ZumosHardTileLimit);
|
||||||
tiles[0] = this;
|
tiles[0] = this;
|
||||||
_tileAtmosInfo.LastQueueCycle = queueCycle;
|
_tileAtmosInfo.LastQueueCycle = queueCycle;
|
||||||
var tileCount = 1;
|
var tileCount = 1;
|
||||||
@@ -269,11 +290,13 @@ namespace Content.Server.Atmos
|
|||||||
}
|
}
|
||||||
|
|
||||||
//tiles = tiles.AsSpan().Slice(0, tileCount).ToArray(); // According to my benchmarks, this is much slower.
|
//tiles = tiles.AsSpan().Slice(0, tileCount).ToArray(); // According to my benchmarks, this is much slower.
|
||||||
Array.Resize(ref tiles, tileCount);
|
//Array.Resize(ref tiles, tileCount);
|
||||||
|
|
||||||
var averageMoles = totalMoles / (tiles.Length);
|
var averageMoles = totalMoles / (tileCount);
|
||||||
var giverTiles = new List<TileAtmosphere>();
|
var giverTiles = ArrayPool<TileAtmosphere>.Shared.Rent(tileCount);
|
||||||
var takerTiles = new List<TileAtmosphere>();
|
var takerTiles = ArrayPool<TileAtmosphere>.Shared.Rent(tileCount);
|
||||||
|
var giverTilesLength = 0;
|
||||||
|
var takerTilesLength = 0;
|
||||||
|
|
||||||
for (var i = 0; i < tileCount; i++)
|
for (var i = 0; i < tileCount; i++)
|
||||||
{
|
{
|
||||||
@@ -282,25 +305,25 @@ namespace Content.Server.Atmos
|
|||||||
tile._tileAtmosInfo.MoleDelta -= averageMoles;
|
tile._tileAtmosInfo.MoleDelta -= averageMoles;
|
||||||
if (tile._tileAtmosInfo.MoleDelta > 0)
|
if (tile._tileAtmosInfo.MoleDelta > 0)
|
||||||
{
|
{
|
||||||
giverTiles.Add(tile);
|
giverTiles[giverTilesLength++] = tile;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
takerTiles.Add(tile);
|
takerTiles[takerTilesLength++] = tile;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var logN = MathF.Log2(tiles.Length);
|
var logN = MathF.Log2(tileCount);
|
||||||
|
|
||||||
// Optimization - try to spread gases using an O(nlogn) algorithm that has a chance of not working first to avoid O(n^2)
|
// Optimization - try to spread gases using an O(nlogn) algorithm that has a chance of not working first to avoid O(n^2)
|
||||||
if (giverTiles.Count > logN && takerTiles.Count > logN)
|
if (giverTilesLength > logN && takerTilesLength > logN)
|
||||||
{
|
{
|
||||||
// Even if it fails, it will speed up the next part.
|
// Even if it fails, it will speed up the next part.
|
||||||
Array.Sort(tiles, (a, b)
|
Array.Sort(tiles, 0, tileCount, _comparer);
|
||||||
=> a._tileAtmosInfo.MoleDelta.CompareTo(b._tileAtmosInfo.MoleDelta));
|
|
||||||
|
|
||||||
foreach (var tile in tiles)
|
for (var i = 0; i < tileCount; i++)
|
||||||
{
|
{
|
||||||
|
var tile = tiles[i];
|
||||||
tile._tileAtmosInfo.FastDone = true;
|
tile._tileAtmosInfo.FastDone = true;
|
||||||
if (!(tile._tileAtmosInfo.MoleDelta > 0)) continue;
|
if (!(tile._tileAtmosInfo.MoleDelta > 0)) continue;
|
||||||
Direction eligibleAdjBits = 0;
|
Direction eligibleAdjBits = 0;
|
||||||
@@ -317,47 +340,50 @@ namespace Content.Server.Atmos
|
|||||||
amtEligibleAdj++;
|
amtEligibleAdj++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (amtEligibleAdj <= 0) continue; // Oof we've painted ourselves into a corner. Bad luck. Next part will handle this.
|
if (amtEligibleAdj <= 0)
|
||||||
|
continue; // Oof we've painted ourselves into a corner. Bad luck. Next part will handle this.
|
||||||
var molesToMove = tile._tileAtmosInfo.MoleDelta / amtEligibleAdj;
|
var molesToMove = tile._tileAtmosInfo.MoleDelta / amtEligibleAdj;
|
||||||
foreach (var direction in Cardinal)
|
foreach (var direction in Cardinal)
|
||||||
{
|
{
|
||||||
if((eligibleAdjBits & direction) == 0 || !tile._adjacentTiles.TryGetValue(direction, out var tile2)) continue;
|
if ((eligibleAdjBits & direction) == 0 ||
|
||||||
|
!tile._adjacentTiles.TryGetValue(direction, out var tile2)) continue;
|
||||||
tile.AdjustEqMovement(direction, molesToMove);
|
tile.AdjustEqMovement(direction, molesToMove);
|
||||||
tile._tileAtmosInfo.MoleDelta -= molesToMove;
|
tile._tileAtmosInfo.MoleDelta -= molesToMove;
|
||||||
tile2._tileAtmosInfo.MoleDelta += molesToMove;
|
tile2._tileAtmosInfo.MoleDelta += molesToMove;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
giverTiles.Clear();
|
giverTilesLength = 0;
|
||||||
takerTiles.Clear();
|
takerTilesLength = 0;
|
||||||
|
|
||||||
foreach (var tile in tiles)
|
for (var i = 0; i < tileCount; i++)
|
||||||
{
|
{
|
||||||
|
var tile = tiles[i];
|
||||||
if (tile._tileAtmosInfo.MoleDelta > 0)
|
if (tile._tileAtmosInfo.MoleDelta > 0)
|
||||||
{
|
{
|
||||||
giverTiles.Add(tile);
|
giverTiles[giverTilesLength++] = tile;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
takerTiles.Add(tile);
|
takerTiles[takerTilesLength++] = tile;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is the part that can become O(n^2).
|
// This is the part that can become O(n^2).
|
||||||
if (giverTiles.Count < takerTiles.Count)
|
if (giverTilesLength < takerTilesLength)
|
||||||
{
|
{
|
||||||
// as an optimization, we choose one of two methods based on which list is smaller. We really want to avoid O(n^2) if we can.
|
// as an optimization, we choose one of two methods based on which list is smaller. We really want to avoid O(n^2) if we can.
|
||||||
var queue = new List<TileAtmosphere>(takerTiles.Count);
|
var queue = ArrayPool<TileAtmosphere>.Shared.Rent(tileCount);
|
||||||
foreach (var giver in giverTiles)
|
for (var j = 0; j < giverTilesLength; j++)
|
||||||
{
|
{
|
||||||
giver._tileAtmosInfo.CurrentTransferDirection = (Direction)(-1);
|
var giver = giverTiles[j];
|
||||||
|
giver._tileAtmosInfo.CurrentTransferDirection = (Direction) (-1);
|
||||||
giver._tileAtmosInfo.CurrentTransferAmount = 0;
|
giver._tileAtmosInfo.CurrentTransferAmount = 0;
|
||||||
var queueCycleSlow = ++_gridAtmosphereComponent.EqualizationQueueCycleControl;
|
var queueCycleSlow = ++_gridAtmosphereComponent.EqualizationQueueCycleControl;
|
||||||
queue.Clear();
|
var queueLength = 0;
|
||||||
queue.Add(giver);
|
queue[queueLength++] = giver;
|
||||||
giver._tileAtmosInfo.LastSlowQueueCycle = queueCycleSlow;
|
giver._tileAtmosInfo.LastSlowQueueCycle = queueCycleSlow;
|
||||||
var queueCount = queue.Count;
|
for (var i = 0; i < queueLength; i++)
|
||||||
for (var i = 0; i < queueCount; i++)
|
|
||||||
{
|
{
|
||||||
if (giver._tileAtmosInfo.MoleDelta <= 0)
|
if (giver._tileAtmosInfo.MoleDelta <= 0)
|
||||||
break; // We're done here now. Let's not do more work than needed.
|
break; // We're done here now. Let's not do more work than needed.
|
||||||
@@ -365,7 +391,7 @@ namespace Content.Server.Atmos
|
|||||||
var tile = queue[i];
|
var tile = queue[i];
|
||||||
foreach (var direction in Cardinal)
|
foreach (var direction in Cardinal)
|
||||||
{
|
{
|
||||||
if(!tile._adjacentTiles.TryGetValue(direction, out var tile2)) continue;
|
if (!tile._adjacentTiles.TryGetValue(direction, out var tile2)) continue;
|
||||||
if (giver._tileAtmosInfo.MoleDelta <= 0)
|
if (giver._tileAtmosInfo.MoleDelta <= 0)
|
||||||
break; // We're done here now. Let's not do more work than needed.
|
break; // We're done here now. Let's not do more work than needed.
|
||||||
|
|
||||||
@@ -373,8 +399,7 @@ namespace Content.Server.Atmos
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (tile2._tileAtmosInfo.LastSlowQueueCycle == queueCycleSlow) continue;
|
if (tile2._tileAtmosInfo.LastSlowQueueCycle == queueCycleSlow) continue;
|
||||||
queue.Add(tile2);
|
queue[queueLength++] = tile2;
|
||||||
queueCount++;
|
|
||||||
tile2._tileAtmosInfo.LastSlowQueueCycle = queueCycleSlow;
|
tile2._tileAtmosInfo.LastSlowQueueCycle = queueCycleSlow;
|
||||||
tile2._tileAtmosInfo.CurrentTransferDirection = direction.GetOpposite();
|
tile2._tileAtmosInfo.CurrentTransferDirection = direction.GetOpposite();
|
||||||
tile2._tileAtmosInfo.CurrentTransferAmount = 0;
|
tile2._tileAtmosInfo.CurrentTransferAmount = 0;
|
||||||
@@ -400,33 +425,38 @@ namespace Content.Server.Atmos
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Putting this loop here helps make it O(n^2) over O(n^3)
|
// Putting this loop here helps make it O(n^2) over O(n^3)
|
||||||
for (var i = queue.Count - 1; i >= 0; i--)
|
for (var i = queueLength - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
var tile = queue[i];
|
var tile = queue[i];
|
||||||
if (tile._tileAtmosInfo.CurrentTransferAmount != 0 &&
|
if (tile._tileAtmosInfo.CurrentTransferAmount != 0 &&
|
||||||
tile._tileAtmosInfo.CurrentTransferDirection != (Direction)(-1))
|
tile._tileAtmosInfo.CurrentTransferDirection != (Direction) (-1))
|
||||||
{
|
{
|
||||||
tile.AdjustEqMovement(tile._tileAtmosInfo.CurrentTransferDirection, tile._tileAtmosInfo.CurrentTransferAmount);
|
tile.AdjustEqMovement(tile._tileAtmosInfo.CurrentTransferDirection,
|
||||||
if(tile._adjacentTiles.TryGetValue(tile._tileAtmosInfo.CurrentTransferDirection, out var adjacent))
|
tile._tileAtmosInfo.CurrentTransferAmount);
|
||||||
adjacent._tileAtmosInfo.CurrentTransferAmount += tile._tileAtmosInfo.CurrentTransferAmount;
|
if (tile._adjacentTiles.TryGetValue(tile._tileAtmosInfo.CurrentTransferDirection,
|
||||||
|
out var adjacent))
|
||||||
|
adjacent._tileAtmosInfo.CurrentTransferAmount +=
|
||||||
|
tile._tileAtmosInfo.CurrentTransferAmount;
|
||||||
tile._tileAtmosInfo.CurrentTransferAmount = 0;
|
tile._tileAtmosInfo.CurrentTransferAmount = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ArrayPool<TileAtmosphere>.Shared.Return(queue, true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var queue = new List<TileAtmosphere>(giverTiles.Count);
|
var queue = ArrayPool<TileAtmosphere>.Shared.Rent(tileCount);
|
||||||
foreach (var taker in takerTiles)
|
for (var j = 0; j < takerTilesLength; j++)
|
||||||
{
|
{
|
||||||
|
var taker = takerTiles[j];
|
||||||
taker._tileAtmosInfo.CurrentTransferDirection = Direction.Invalid;
|
taker._tileAtmosInfo.CurrentTransferDirection = Direction.Invalid;
|
||||||
taker._tileAtmosInfo.CurrentTransferAmount = 0;
|
taker._tileAtmosInfo.CurrentTransferAmount = 0;
|
||||||
var queueCycleSlow = ++_gridAtmosphereComponent.EqualizationQueueCycleControl;
|
var queueCycleSlow = ++_gridAtmosphereComponent.EqualizationQueueCycleControl;
|
||||||
queue.Clear();
|
var queueLength = 0;
|
||||||
queue.Add(taker);
|
queue[queueLength++] = taker;
|
||||||
taker._tileAtmosInfo.LastSlowQueueCycle = queueCycleSlow;
|
taker._tileAtmosInfo.LastSlowQueueCycle = queueCycleSlow;
|
||||||
var queueCount = queue.Count;
|
for (int i = 0; i < queueLength; i++)
|
||||||
for (int i = 0; i < queueCount; i++)
|
|
||||||
{
|
{
|
||||||
if (taker._tileAtmosInfo.MoleDelta >= 0)
|
if (taker._tileAtmosInfo.MoleDelta >= 0)
|
||||||
break; // We're done here now. Let's not do more work than needed.
|
break; // We're done here now. Let's not do more work than needed.
|
||||||
@@ -434,16 +464,16 @@ namespace Content.Server.Atmos
|
|||||||
var tile = queue[i];
|
var tile = queue[i];
|
||||||
foreach (var direction in Cardinal)
|
foreach (var direction in Cardinal)
|
||||||
{
|
{
|
||||||
if(!tile._adjacentTiles.ContainsKey(direction)) continue;
|
if (!tile._adjacentTiles.ContainsKey(direction)) continue;
|
||||||
var tile2 = tile._adjacentTiles[direction];
|
var tile2 = tile._adjacentTiles[direction];
|
||||||
|
|
||||||
if (taker._tileAtmosInfo.MoleDelta >= 0)
|
if (taker._tileAtmosInfo.MoleDelta >= 0)
|
||||||
break; // We're done here now. Let's not do more work than needed.
|
break; // We're done here now. Let's not do more work than needed.
|
||||||
|
|
||||||
if (tile2?._tileAtmosInfo == null || tile2._tileAtmosInfo.LastQueueCycle != queueCycle) continue;
|
if (tile2?._tileAtmosInfo == null || tile2._tileAtmosInfo.LastQueueCycle != queueCycle)
|
||||||
|
continue;
|
||||||
if (tile2._tileAtmosInfo.LastSlowQueueCycle == queueCycleSlow) continue;
|
if (tile2._tileAtmosInfo.LastSlowQueueCycle == queueCycleSlow) continue;
|
||||||
queue.Add(tile2);
|
queue[queueLength++] = tile2;
|
||||||
queueCount++;
|
|
||||||
tile2._tileAtmosInfo.LastSlowQueueCycle = queueCycleSlow;
|
tile2._tileAtmosInfo.LastSlowQueueCycle = queueCycleSlow;
|
||||||
tile2._tileAtmosInfo.CurrentTransferDirection = direction.GetOpposite();
|
tile2._tileAtmosInfo.CurrentTransferDirection = direction.GetOpposite();
|
||||||
tile2._tileAtmosInfo.CurrentTransferAmount = 0;
|
tile2._tileAtmosInfo.CurrentTransferAmount = 0;
|
||||||
@@ -469,27 +499,34 @@ namespace Content.Server.Atmos
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = queue.Count - 1; i >= 0; i--)
|
for (var i = queueLength - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
var tile = queue[i];
|
var tile = queue[i];
|
||||||
if (tile._tileAtmosInfo.CurrentTransferAmount == 0 ||
|
if (tile._tileAtmosInfo.CurrentTransferAmount == 0 ||
|
||||||
tile._tileAtmosInfo.CurrentTransferDirection == Direction.Invalid) continue;
|
tile._tileAtmosInfo.CurrentTransferDirection == Direction.Invalid) continue;
|
||||||
tile.AdjustEqMovement(tile._tileAtmosInfo.CurrentTransferDirection, tile._tileAtmosInfo.CurrentTransferAmount);
|
tile.AdjustEqMovement(tile._tileAtmosInfo.CurrentTransferDirection,
|
||||||
|
tile._tileAtmosInfo.CurrentTransferAmount);
|
||||||
|
|
||||||
if(tile._adjacentTiles.TryGetValue(tile._tileAtmosInfo.CurrentTransferDirection, out var adjacent))
|
if (tile._adjacentTiles.TryGetValue(tile._tileAtmosInfo.CurrentTransferDirection,
|
||||||
adjacent._tileAtmosInfo.CurrentTransferAmount += tile._tileAtmosInfo.CurrentTransferAmount;
|
out var adjacent))
|
||||||
|
adjacent._tileAtmosInfo.CurrentTransferAmount +=
|
||||||
|
tile._tileAtmosInfo.CurrentTransferAmount;
|
||||||
tile._tileAtmosInfo.CurrentTransferAmount = 0;
|
tile._tileAtmosInfo.CurrentTransferAmount = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ArrayPool<TileAtmosphere>.Shared.Return(queue, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var tile in tiles)
|
for (var i = 0; i < tileCount; i++)
|
||||||
{
|
{
|
||||||
|
var tile = tiles[i];
|
||||||
tile.FinalizeEq();
|
tile.FinalizeEq();
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var tile in tiles)
|
for (var i = 0; i < tileCount; i++)
|
||||||
{
|
{
|
||||||
|
var tile = tiles[i];
|
||||||
foreach (var direction in Cardinal)
|
foreach (var direction in Cardinal)
|
||||||
{
|
{
|
||||||
if (!tile._adjacentTiles.TryGetValue(direction, out var tile2)) continue;
|
if (!tile._adjacentTiles.TryGetValue(direction, out var tile2)) continue;
|
||||||
@@ -498,6 +535,10 @@ namespace Content.Server.Atmos
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ArrayPool<TileAtmosphere>.Shared.Return(tiles, true);
|
||||||
|
ArrayPool<TileAtmosphere>.Shared.Return(giverTiles, true);
|
||||||
|
ArrayPool<TileAtmosphere>.Shared.Return(takerTiles, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user