Damageable/Destructible Benchmarks (#41064)
* the fard * oomba * The woke swarm... * Review * review * Apply suggestions from code review --------- Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
7dbf084940
commit
3a9bcf1a83
159
Content.Benchmarks/DestructibleBenchmark.cs
Normal file
159
Content.Benchmarks/DestructibleBenchmark.cs
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using BenchmarkDotNet.Attributes;
|
||||||
|
using Content.IntegrationTests;
|
||||||
|
using Content.IntegrationTests.Pair;
|
||||||
|
using Content.Server.Destructible;
|
||||||
|
using Content.Shared.Damage;
|
||||||
|
using Content.Shared.Damage.Prototypes;
|
||||||
|
using Content.Shared.FixedPoint;
|
||||||
|
using Content.Shared.Maps;
|
||||||
|
using Robust.Shared;
|
||||||
|
using Robust.Shared.Analyzers;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Maths;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Random;
|
||||||
|
|
||||||
|
namespace Content.Benchmarks;
|
||||||
|
|
||||||
|
[Virtual]
|
||||||
|
[GcServer(true)]
|
||||||
|
[MemoryDiagnoser]
|
||||||
|
public class DestructibleBenchmark
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Number of destructible entities per prototype to spawn with a <see cref="DestructibleComponent"/>.
|
||||||
|
/// </summary>
|
||||||
|
[Params(1, 10, 100, 1000, 5000)]
|
||||||
|
public int EntityCount;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Amount of blunt damage we do to each entity.
|
||||||
|
/// </summary>
|
||||||
|
[Params(10000)]
|
||||||
|
public FixedPoint2 DamageAmount;
|
||||||
|
|
||||||
|
[Params("Blunt")]
|
||||||
|
public ProtoId<DamageTypePrototype> DamageType;
|
||||||
|
|
||||||
|
private static readonly EntProtoId WindowProtoId = "Window";
|
||||||
|
private static readonly EntProtoId WallProtoId = "WallReinforced";
|
||||||
|
private static readonly EntProtoId HumanProtoId = "MobHuman";
|
||||||
|
|
||||||
|
private static readonly ProtoId<ContentTileDefinition> TileRef = "Plating";
|
||||||
|
|
||||||
|
private readonly EntProtoId[] _prototypes = [WindowProtoId, WallProtoId, HumanProtoId];
|
||||||
|
|
||||||
|
private readonly List<Entity<DamageableComponent>> _damageables = new();
|
||||||
|
private readonly List<Entity<DamageableComponent, DestructibleComponent>> _destructbiles = new();
|
||||||
|
|
||||||
|
private DamageSpecifier _damage;
|
||||||
|
|
||||||
|
private TestPair _pair = default!;
|
||||||
|
private IEntityManager _entMan = default!;
|
||||||
|
private IPrototypeManager _protoMan = default!;
|
||||||
|
private IRobustRandom _random = default!;
|
||||||
|
private ITileDefinitionManager _tileDefMan = default!;
|
||||||
|
private DamageableSystem _damageable = default!;
|
||||||
|
private DestructibleSystem _destructible = default!;
|
||||||
|
private SharedMapSystem _map = default!;
|
||||||
|
|
||||||
|
[GlobalSetup]
|
||||||
|
public async Task SetupAsync()
|
||||||
|
{
|
||||||
|
ProgramShared.PathOffset = "../../../../";
|
||||||
|
PoolManager.Startup();
|
||||||
|
_pair = await PoolManager.GetServerClient();
|
||||||
|
var server = _pair.Server;
|
||||||
|
|
||||||
|
var mapdata = await _pair.CreateTestMap();
|
||||||
|
|
||||||
|
_entMan = server.ResolveDependency<IEntityManager>();
|
||||||
|
_protoMan = server.ResolveDependency<IPrototypeManager>();
|
||||||
|
_random = server.ResolveDependency<IRobustRandom>();
|
||||||
|
_tileDefMan = server.ResolveDependency<ITileDefinitionManager>();
|
||||||
|
_damageable = _entMan.System<DamageableSystem>();
|
||||||
|
_destructible = _entMan.System<DestructibleSystem>();
|
||||||
|
_map = _entMan.System<SharedMapSystem>();
|
||||||
|
|
||||||
|
if (!_protoMan.Resolve(DamageType, out var type))
|
||||||
|
return;
|
||||||
|
|
||||||
|
_damage = new DamageSpecifier(type, DamageAmount);
|
||||||
|
|
||||||
|
_random.SetSeed(69420); // Randomness needs to be deterministic for benchmarking.
|
||||||
|
|
||||||
|
var plating = _tileDefMan[TileRef].TileId;
|
||||||
|
|
||||||
|
// We make a rectangular grid of destructible entities, and then damage them all simultaneously to stress test the system.
|
||||||
|
// Needed for managing the performance of destructive effects and damage application.
|
||||||
|
await server.WaitPost(() =>
|
||||||
|
{
|
||||||
|
// Set up a thin line of tiles to place our objects on. They should be anchored for a "realistic" scenario...
|
||||||
|
for (var x = 0; x < EntityCount; x++)
|
||||||
|
{
|
||||||
|
for (var y = 0; y < _prototypes.Length; y++)
|
||||||
|
{
|
||||||
|
_map.SetTile(mapdata.Grid, mapdata.Grid, new Vector2i(x, y), new Tile(plating));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var x = 0; x < EntityCount; x++)
|
||||||
|
{
|
||||||
|
var y = 0;
|
||||||
|
foreach (var protoId in _prototypes)
|
||||||
|
{
|
||||||
|
var coords = new EntityCoordinates(mapdata.Grid, x + 0.5f, y + 0.5f);
|
||||||
|
_entMan.SpawnEntity(protoId, coords);
|
||||||
|
y++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var query = _entMan.EntityQueryEnumerator<DamageableComponent, DestructibleComponent>();
|
||||||
|
|
||||||
|
while (query.MoveNext(out var uid, out var damageable, out var destructible))
|
||||||
|
{
|
||||||
|
_damageables.Add((uid, damageable));
|
||||||
|
_destructbiles.Add((uid, damageable, destructible));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public async Task PerformDealDamage()
|
||||||
|
{
|
||||||
|
await _pair.Server.WaitPost(() =>
|
||||||
|
{
|
||||||
|
_damageable.ApplyDamageToAllEntities(_damageables, _damage);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public async Task PerformTestTriggers()
|
||||||
|
{
|
||||||
|
await _pair.Server.WaitPost(() =>
|
||||||
|
{
|
||||||
|
_destructible.TestAllTriggers(_destructbiles);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public async Task PerformTestBehaviors()
|
||||||
|
{
|
||||||
|
await _pair.Server.WaitPost(() =>
|
||||||
|
{
|
||||||
|
_destructible.TestAllBehaviors(_destructbiles);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[GlobalCleanup]
|
||||||
|
public async Task CleanupAsync()
|
||||||
|
{
|
||||||
|
await _pair.DisposeAsync();
|
||||||
|
PoolManager.Shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
using Content.Shared.Damage;
|
||||||
|
|
||||||
|
namespace Content.Server.Destructible;
|
||||||
|
|
||||||
|
public sealed partial class DestructibleSystem
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Tests all triggers in a DestructibleComponent to see how expensive it is to query them.
|
||||||
|
/// </summary>
|
||||||
|
public void TestAllTriggers(List<Entity<DamageableComponent, DestructibleComponent>> destructibles)
|
||||||
|
{
|
||||||
|
foreach (var (uid, damageable, destructible) in destructibles)
|
||||||
|
{
|
||||||
|
foreach (var threshold in destructible.Thresholds)
|
||||||
|
{
|
||||||
|
// Chances are, none of these triggers will pass!
|
||||||
|
Triggered(threshold, (uid, damageable));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests all behaviours in a DestructibleComponent to see how expensive it is to query them.
|
||||||
|
/// </summary>
|
||||||
|
public void TestAllBehaviors(List<Entity<DamageableComponent, DestructibleComponent>> destructibles)
|
||||||
|
{
|
||||||
|
foreach (var (uid, damageable, destructible) in destructibles)
|
||||||
|
{
|
||||||
|
foreach (var threshold in destructible.Thresholds)
|
||||||
|
{
|
||||||
|
Execute(threshold, uid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,7 +26,7 @@ using Robust.Shared.Random;
|
|||||||
namespace Content.Server.Destructible
|
namespace Content.Server.Destructible
|
||||||
{
|
{
|
||||||
[UsedImplicitly]
|
[UsedImplicitly]
|
||||||
public sealed class DestructibleSystem : SharedDestructibleSystem
|
public sealed partial class DestructibleSystem : SharedDestructibleSystem
|
||||||
{
|
{
|
||||||
[Dependency] public readonly IRobustRandom Random = default!;
|
[Dependency] public readonly IRobustRandom Random = default!;
|
||||||
public new IEntityManager EntityManager => base.EntityManager;
|
public new IEntityManager EntityManager => base.EntityManager;
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
namespace Content.Shared.Damage;
|
||||||
|
|
||||||
|
public sealed partial class DamageableSystem
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Applies damage to all entities to see how expensive it is to deal damage.
|
||||||
|
/// </summary>
|
||||||
|
public void ApplyDamageToAllEntities(List<Entity<DamageableComponent>> damageables, DamageSpecifier damage)
|
||||||
|
{
|
||||||
|
foreach (var (uid, damageable) in damageables)
|
||||||
|
{
|
||||||
|
TryChangeDamage(uid, damage, damageable: damageable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,7 +17,7 @@ using Robust.Shared.Utility;
|
|||||||
|
|
||||||
namespace Content.Shared.Damage
|
namespace Content.Shared.Damage
|
||||||
{
|
{
|
||||||
public sealed class DamageableSystem : EntitySystem
|
public sealed partial class DamageableSystem : EntitySystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||||
|
|||||||
Reference in New Issue
Block a user