Skip to content
Merged
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
53 changes: 24 additions & 29 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
@@ -1,31 +1,26 @@
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<CentralPackageVersionOverrideEnabled>true</CentralPackageVersionOverrideEnabled>
</PropertyGroup>

<ItemGroup>
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0"/>
</ItemGroup>

<ItemGroup>
<PackageVersion Include="MonoGame.Framework.DesktopGL" Version="3.8.4.1"/>
</ItemGroup>

<ItemGroup>
<PackageVersion Include="DefaultEcs" Version="0.17.2"/>
</ItemGroup>

<ItemGroup>
<PackageVersion Include="AutoFixture.Xunit2" Version="4.18.1"/>
<PackageVersion Include="BenchmarkDotNet" Version="0.15.8"/>
<PackageVersion Include="FluentAssertions" Version="8.8.0"/>
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.0.1"/>
<PackageVersion Include="Moq" Version="4.20.72"/>
<PackageVersion Include="xunit" Version="2.9.3"/>
<PackageVersion Include="JunitXml.TestLogger" Version="7.1.0"/>
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5"/>
<PackageVersion Include="coverlet.msbuild" Version="6.0.4"/>
<PackageVersion Include="coverlet.collector" Version="6.0.4"/>
</ItemGroup>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<CentralPackageVersionOverrideEnabled>true</CentralPackageVersionOverrideEnabled>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Friflo.Engine.ECS" Version="3.4.2" />
<PackageVersion Include="DefaultEcs" Version="0.17.2" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
</ItemGroup>
<ItemGroup>
<PackageVersion Include="MonoGame.Framework.DesktopGL" Version="3.8.4.1" />
</ItemGroup>
<ItemGroup>
<PackageVersion Include="AutoFixture.Xunit2" Version="4.18.1" />
<PackageVersion Include="BenchmarkDotNet" Version="0.15.8" />
<PackageVersion Include="FluentAssertions" Version="8.8.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageVersion Include="Moq" Version="4.20.72" />
<PackageVersion Include="xunit" Version="2.9.3" />
<PackageVersion Include="JunitXml.TestLogger" Version="7.1.0" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
<PackageVersion Include="coverlet.msbuild" Version="6.0.4" />
<PackageVersion Include="coverlet.collector" Version="6.0.4" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using Hexecs.Benchmarks.Map.Common.Positions;
using Hexecs.Benchmarks.Map.Terrains;
using Hexecs.Benchmarks.Map.Utils;
using Hexecs.Benchmarks.Map.Utils.Sprites;
using Hexecs.Threading;
using Hexecs.Worlds;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using Hexecs.Actors.Pipelines;
using Hexecs.Benchmarks.Map.Common.Positions;
using Hexecs.Benchmarks.Map.Terrains.Assets;
using Hexecs.Benchmarks.Map.Terrains.ValueTypes;
using Hexecs.Benchmarks.Map.Utils;
using Hexecs.Pipelines;

namespace Hexecs.Benchmarks.Map.Terrains.Commands.Generate;
Expand All @@ -9,7 +11,9 @@ internal sealed class GenerateTerrainHandler : ActorCommandHandler<GenerateTerra
{
private readonly TerrainSettings _settings;

public GenerateTerrainHandler(ActorContext context, TerrainSettings settings) : base(context)
public GenerateTerrainHandler(
ActorContext context,
TerrainSettings settings) : base(context)
{
_settings = settings;
}
Expand All @@ -28,17 +32,20 @@ public override Result Handle(in GenerateTerrainCommand terrainCommand)
for (var x = 0; x < width; x++)
{
var args = Args.Rent(nameof(Point), new Point(x, y));
var actor = x switch
if (x is > 45 and < 55) // river
{
// river
> 45 and < 55 => Context.BuildActor<Terrain>(river, args
.Set(nameof(Terrain.Elevation), Elevation.FromValue(-10))
.Set(nameof(Terrain.Moisture), Moisture.FromValue(35))),
// urban concrete
< 10 when y < 10 => Context.BuildActor<Terrain>(urbanConcrete, args),
// just ground
_ => Context.BuildActor<Terrain>(ground, args)
};
Context.BuildActor<Terrain>(river,
args.Set(nameof(Terrain.Elevation), Elevation.FromValue(-10))
.Set(nameof(Terrain.Moisture), Moisture.FromValue(35)));
}
else if (x < 10 && y < 10) // urban concrete
{
Context.BuildActor<Terrain>(urbanConcrete, args);
}
else // just ground
{
Context.BuildActor<Terrain>(ground, args);
}
}
}

Expand Down
1 change: 0 additions & 1 deletion src/Hexecs.Benchmarks.City/Terrains/TerrainDrawSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using Hexecs.Benchmarks.Map.Common.Positions;
using Hexecs.Benchmarks.Map.Common.Visibles;
using Hexecs.Benchmarks.Map.Utils;
using Hexecs.Benchmarks.Map.Utils.Sprites;
using Hexecs.Worlds;
using Microsoft.Xna.Framework.Graphics;

Expand Down
9 changes: 7 additions & 2 deletions src/Hexecs.Benchmarks.City/Terrains/TerrainInstaller.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
using Hexecs.Benchmarks.Map.Common.Positions;
using Hexecs.Benchmarks.Map.Terrains.Assets;
using Hexecs.Benchmarks.Map.Terrains.Commands.Generate;
using Hexecs.Configurations;
using Hexecs.Dependencies;
using Hexecs.Worlds;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;

namespace Hexecs.Benchmarks.Map.Terrains;

Expand All @@ -23,7 +23,7 @@ public static ActorContextBuilder AddTerrain(this ActorContextBuilder builder)
builder.CreateCommandHandler<GenerateTerrainHandler>();

builder.CreateDrawSystem<TerrainDrawSystem>();

return builder;
}

Expand All @@ -32,6 +32,11 @@ public static WorldBuilder UseTerrain(this WorldBuilder builder)
builder
.UseAddAssetSource(new TerrainAssetSource());

builder
.UseScoped(ctx => new ActorDictionary<Point, Position>(
context: ctx.GetRequiredService<ActorContext>(),
keyExtractor: terrain => terrain.Grid));

builder
.UseSingleton(ctx => new TerrainSpriteAtlas(
contentManager: ctx.GetRequiredService<ContentManager>(),
Expand Down
1 change: 0 additions & 1 deletion src/Hexecs.Benchmarks.City/Terrains/TerrainSpriteAtlas.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using Hexecs.Benchmarks.Map.Terrains.ValueTypes;
using Hexecs.Benchmarks.Map.Utils.Sprites;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;

namespace Hexecs.Benchmarks.Map.Terrains;

Expand Down
5 changes: 4 additions & 1 deletion src/Hexecs.Benchmarks.City/Utils/PointExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ namespace Hexecs.Benchmarks.Map.Utils;

public static class PointExtensions
{
public static void GetNeighborPoints(int x, int y, ref Span<Point> neighbors)
public static void GetNeighborPoints(this in Point point, ref Span<Point> neighbors)
{
var x = point.X;
var y = point.Y;

neighbors[0] = new Point(x - 1, y);
neighbors[1] = new Point(x + 1, y);
neighbors[2] = new Point(x, y - 1);
Expand Down
194 changes: 194 additions & 0 deletions src/Hexecs.Benchmarks/Actors/ActorCheckComponentExistsBenchmark.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
using System.Runtime.CompilerServices;
using Friflo.Engine.ECS;
using Hexecs.Benchmarks.Mocks.ActorComponents;
using Hexecs.Worlds;
using World = Hexecs.Worlds.World;

namespace Hexecs.Benchmarks.Actors;

// BenchmarkDotNet v0.15.8, Windows 11 (10.0.22621.4317/22H2/2022Update/SunValley2)
// Intel Xeon CPU E5-2697 v3 2.60GHz, 2 CPU, 56 logical and 28 physical cores
// .NET SDK 10.0.100
// [Host] : .NET 10.0.0 (10.0.0, 10.0.25.52411), X64 RyuJIT x86-64-v3
// .NET 10.0 : .NET 10.0.0 (10.0.0, 10.0.25.52411), X64 RyuJIT x86-64-v3
//
// Job=.NET 10.0 Runtime=.NET 10.0
//
// | Method | Count | Mean | Ratio | Allocated | Alloc Ratio |
// |----------------- |------- |----------:|------:|----------:|------------:|
// | Hexecs_Is | 10000 | 20.42 us | 0.96 | - | NA |
// | Hexecs_Has | 10000 | 21.19 us | 1.00 | - | NA |
// | Hexecs_Reference | 10000 | 24.30 us | 1.15 | - | NA |
// | FriFlo_Has | 10000 | 40.28 us | 1.90 | - | NA |
// | DefaultEcs_Has | 10000 | 73.24 us | 3.46 | - | NA |
// | | | | | | |
// | Hexecs_Is | 100000 | 204.98 us | 0.94 | - | NA |
// | Hexecs_Has | 100000 | 219.12 us | 1.00 | - | NA |
// | Hexecs_Reference | 100000 | 251.83 us | 1.15 | - | NA |
// | FriFlo_Has | 100000 | 409.48 us | 1.87 | - | NA |
// | DefaultEcs_Has | 100000 | 712.00 us | 3.25 | - | NA |
//
// ------------------------------------------------------------------------------------
//
// BenchmarkDotNet v0.15.8, macOS Tahoe 26.2 (25C56) [Darwin 25.2.0]
// Apple M3 Max, 1 CPU, 16 logical and 16 physical cores
// .NET SDK 10.0.101
// [Host] : .NET 10.0.1 (10.0.1, 10.0.125.57005), Arm64 RyuJIT armv8.0-a
// .NET 10.0 : .NET 10.0.1 (10.0.1, 10.0.125.57005), Arm64 RyuJIT armv8.0-a
//
// Job=.NET 10.0 Runtime=.NET 10.0
//
// | Method | Mean | Ratio | Allocated | Alloc Ratio |
// |----------------- |----------:|------:|----------:|------------:|
// | Hexecs_Is | 12.76 us | 0.93 | - | NA |
// | Hexecs_Has | 13.79 us | 1.00 | - | NA |
// | Hexecs_Reference | 15.44 us | 1.12 | - | NA |
// | DefaultEcs_Has | 25.32 us | 1.84 | - | NA |
// | | | | | |
// | Hexecs_Is | 127.64 us | 0.92 | - | NA |
// | Hexecs_Has | 139.17 us | 1.00 | - | NA |
// | Hexecs_Reference | 155.12 us | 1.11 | - | NA |
// | DefaultEcs_Has | 255.36 us | 1.83 | - | NA |

[SimpleJob(RuntimeMoniker.Net10_0)]
[Orderer(SummaryOrderPolicy.FastestToSlowest)]
[MeanColumn, MemoryDiagnoser]
[HideColumns("Job", "Error", "StdDev", "Median", "RatioSD")]
[JsonExporterAttribute.Full]
[JsonExporterAttribute.FullCompressed]
[BenchmarkCategory("Actors")]
public class ActorCheckComponentExistsBenchmark
{
[Params(10_000, 100_000)] public int Count;

private ActorContext _context = null!;
private DefaultEcs.World _defaultWorld = null!;
private EntityStore _frifloWorld = null!;
private ArchetypeQuery _frifloAllEntitiesQuery = null!;
private World _world = null!;

[Benchmark(Baseline = true)]
public int Hexecs_Has()
{
var result = 0;

// ReSharper disable once ForeachCanBeConvertedToQueryUsingAnotherGetEnumerator
foreach (var actor in _context)
{
if (actor.Has<Speed>()) result++;
}

return result;
}

[Benchmark]
public int DefaultEcs_Has()
{
var result = 0;

// ReSharper disable once ForeachCanBeConvertedToQueryUsingAnotherGetEnumerator
foreach (var entity in _defaultWorld)
{
if (entity.Has<Speed>())
{
result++;
}
}

return result;
}

[Benchmark]
public int FriFlo_Has()
{
var result = 0;

// ReSharper disable once ForeachCanBeConvertedToQueryUsingAnotherGetEnumerator
foreach (var entity in _frifloAllEntitiesQuery.Entities)
{
if (entity.HasComponent<Speed>())
{
result++;
}
}

return result;
}

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code should not contain multiple blank lines in a row.

Suggested change

[Benchmark]
public int Hexecs_Is()
{
var result = 0;

// ReSharper disable once ForeachCanBeConvertedToQueryUsingAnotherGetEnumerator
foreach (var actor in _context)
{
if (actor.Is<Speed>(out _))
{
result++;
}
}

return result;
}

[Benchmark]
public int Hexecs_Reference()
{
var result = 0;

// ReSharper disable once ForeachCanBeConvertedToQueryUsingAnotherGetEnumerator
foreach (var actor in _context)
{
ref var reference = ref actor.TryGetRef<Speed>();
if (!Unsafe.IsNullRef(ref reference))
{
result++;
}
}

return result;
}

[GlobalCleanup]
public void Cleanup()
{
_defaultWorld.Dispose();
_defaultWorld = null!;

_frifloWorld = null!;

_world.Dispose();
_world = null!;
}

[GlobalSetup]
public void Setup()
{
_defaultWorld = new DefaultEcs.World();
_frifloWorld = new EntityStore();
_frifloAllEntitiesQuery = _frifloWorld.Query();
_world = new WorldBuilder().Build();
_context = _world.Actors;

for (var i = 0; i < Count; i++)
{
var actor = _context.CreateActor();
actor.Add(new Attack());
actor.Add(new Defence());

var defaultEntity = _defaultWorld.CreateEntity();
defaultEntity.Set<Attack>();
defaultEntity.Set<Defence>();

var frifloEntity = _frifloWorld.CreateEntity(new Attack(), new Defence());

if (i % 10 != 0) continue;

actor.Add(new Speed());

defaultEntity.Set<Speed>();
frifloEntity.Add(new Speed());
}
}
}
Loading