1+ using System . Runtime . CompilerServices ;
2+ using Friflo . Engine . ECS ;
3+ using Hexecs . Benchmarks . Mocks . ActorComponents ;
4+ using Hexecs . Worlds ;
5+ using World = Hexecs . Worlds . World ;
6+
7+ namespace Hexecs . Benchmarks . Actors ;
8+
9+ // BenchmarkDotNet v0.15.8, Windows 11 (10.0.22621.4317/22H2/2022Update/SunValley2)
10+ // Intel Xeon CPU E5-2697 v3 2.60GHz, 2 CPU, 56 logical and 28 physical cores
11+ // .NET SDK 10.0.100
12+ // [Host] : .NET 10.0.0 (10.0.0, 10.0.25.52411), X64 RyuJIT x86-64-v3
13+ // .NET 10.0 : .NET 10.0.0 (10.0.0, 10.0.25.52411), X64 RyuJIT x86-64-v3
14+ //
15+ // Job=.NET 10.0 Runtime=.NET 10.0
16+ //
17+ // | Method | Count | Mean | Ratio | Allocated | Alloc Ratio |
18+ // |----------------- |------- |----------:|------:|----------:|------------:|
19+ // | Hexecs_Is | 10000 | 20.42 us | 0.96 | - | NA |
20+ // | Hexecs_Has | 10000 | 21.19 us | 1.00 | - | NA |
21+ // | Hexecs_Reference | 10000 | 24.30 us | 1.15 | - | NA |
22+ // | FriFlo_Has | 10000 | 40.28 us | 1.90 | - | NA |
23+ // | DefaultEcs_Has | 10000 | 73.24 us | 3.46 | - | NA |
24+ // | | | | | | |
25+ // | Hexecs_Is | 100000 | 204.98 us | 0.94 | - | NA |
26+ // | Hexecs_Has | 100000 | 219.12 us | 1.00 | - | NA |
27+ // | Hexecs_Reference | 100000 | 251.83 us | 1.15 | - | NA |
28+ // | FriFlo_Has | 100000 | 409.48 us | 1.87 | - | NA |
29+ // | DefaultEcs_Has | 100000 | 712.00 us | 3.25 | - | NA |
30+ //
31+ // ------------------------------------------------------------------------------------
32+ //
33+ // BenchmarkDotNet v0.15.8, macOS Tahoe 26.2 (25C56) [Darwin 25.2.0]
34+ // Apple M3 Max, 1 CPU, 16 logical and 16 physical cores
35+ // .NET SDK 10.0.101
36+ // [Host] : .NET 10.0.1 (10.0.1, 10.0.125.57005), Arm64 RyuJIT armv8.0-a
37+ // .NET 10.0 : .NET 10.0.1 (10.0.1, 10.0.125.57005), Arm64 RyuJIT armv8.0-a
38+ //
39+ // Job=.NET 10.0 Runtime=.NET 10.0
40+ //
41+ // | Method | Mean | Ratio | Allocated | Alloc Ratio |
42+ // |----------------- |----------:|------:|----------:|------------:|
43+ // | Hexecs_Is | 12.76 us | 0.93 | - | NA |
44+ // | Hexecs_Has | 13.79 us | 1.00 | - | NA |
45+ // | Hexecs_Reference | 15.44 us | 1.12 | - | NA |
46+ // | DefaultEcs_Has | 25.32 us | 1.84 | - | NA |
47+ // | | | | | |
48+ // | Hexecs_Is | 127.64 us | 0.92 | - | NA |
49+ // | Hexecs_Has | 139.17 us | 1.00 | - | NA |
50+ // | Hexecs_Reference | 155.12 us | 1.11 | - | NA |
51+ // | DefaultEcs_Has | 255.36 us | 1.83 | - | NA |
52+
53+ [ SimpleJob ( RuntimeMoniker . Net10_0 ) ]
54+ [ Orderer ( SummaryOrderPolicy . FastestToSlowest ) ]
55+ [ MeanColumn , MemoryDiagnoser ]
56+ [ HideColumns ( "Job" , "Error" , "StdDev" , "Median" , "RatioSD" ) ]
57+ [ JsonExporterAttribute . Full ]
58+ [ JsonExporterAttribute . FullCompressed ]
59+ [ BenchmarkCategory ( "Actors" ) ]
60+ public class ActorCheckComponentExistsBenchmark
61+ {
62+ [ Params ( 10_000 , 100_000 ) ] public int Count ;
63+
64+ private ActorContext _context = null ! ;
65+ private DefaultEcs . World _defaultWorld = null ! ;
66+ private EntityStore _frifloWorld = null ! ;
67+ private ArchetypeQuery _frifloAllEntitiesQuery = null ! ;
68+ private World _world = null ! ;
69+
70+ [ Benchmark ( Baseline = true ) ]
71+ public int Hexecs_Has ( )
72+ {
73+ var result = 0 ;
74+
75+ // ReSharper disable once ForeachCanBeConvertedToQueryUsingAnotherGetEnumerator
76+ foreach ( var actor in _context )
77+ {
78+ if ( actor . Has < Speed > ( ) ) result ++ ;
79+ }
80+
81+ return result ;
82+ }
83+
84+ [ Benchmark ]
85+ public int DefaultEcs_Has ( )
86+ {
87+ var result = 0 ;
88+
89+ // ReSharper disable once ForeachCanBeConvertedToQueryUsingAnotherGetEnumerator
90+ foreach ( var entity in _defaultWorld )
91+ {
92+ if ( entity . Has < Speed > ( ) )
93+ {
94+ result ++ ;
95+ }
96+ }
97+
98+ return result ;
99+ }
100+
101+ [ Benchmark ]
102+ public int FriFlo_Has ( )
103+ {
104+ var result = 0 ;
105+
106+ // ReSharper disable once ForeachCanBeConvertedToQueryUsingAnotherGetEnumerator
107+ foreach ( var entity in _frifloAllEntitiesQuery . Entities )
108+ {
109+ if ( entity . HasComponent < Speed > ( ) )
110+ {
111+ result ++ ;
112+ }
113+ }
114+
115+ return result ;
116+ }
117+
118+ [ Benchmark ]
119+ public int Hexecs_Is ( )
120+ {
121+ var result = 0 ;
122+
123+ // ReSharper disable once ForeachCanBeConvertedToQueryUsingAnotherGetEnumerator
124+ foreach ( var actor in _context )
125+ {
126+ if ( actor . Is < Speed > ( out _ ) )
127+ {
128+ result ++ ;
129+ }
130+ }
131+
132+ return result ;
133+ }
134+
135+ [ Benchmark ]
136+ public int Hexecs_Reference ( )
137+ {
138+ var result = 0 ;
139+
140+ // ReSharper disable once ForeachCanBeConvertedToQueryUsingAnotherGetEnumerator
141+ foreach ( var actor in _context )
142+ {
143+ ref var reference = ref actor . TryGetRef < Speed > ( ) ;
144+ if ( ! Unsafe . IsNullRef ( ref reference ) )
145+ {
146+ result ++ ;
147+ }
148+ }
149+
150+ return result ;
151+ }
152+
153+ [ GlobalCleanup ]
154+ public void Cleanup ( )
155+ {
156+ _defaultWorld . Dispose ( ) ;
157+ _defaultWorld = null ! ;
158+
159+ _frifloWorld = null ! ;
160+
161+ _world . Dispose ( ) ;
162+ _world = null ! ;
163+ }
164+
165+ [ GlobalSetup ]
166+ public void Setup ( )
167+ {
168+ _defaultWorld = new DefaultEcs . World ( ) ;
169+ _frifloWorld = new EntityStore ( ) ;
170+ _frifloAllEntitiesQuery = _frifloWorld . Query ( ) ;
171+ _world = new WorldBuilder ( ) . Build ( ) ;
172+ _context = _world . Actors ;
173+
174+ for ( var i = 0 ; i < Count ; i ++ )
175+ {
176+ var actor = _context . CreateActor ( ) ;
177+ actor . Add ( new Attack ( ) ) ;
178+ actor . Add ( new Defence ( ) ) ;
179+
180+ var defaultEntity = _defaultWorld . CreateEntity ( ) ;
181+ defaultEntity . Set < Attack > ( ) ;
182+ defaultEntity . Set < Defence > ( ) ;
183+
184+ var frifloEntity = _frifloWorld . CreateEntity ( new Attack ( ) , new Defence ( ) ) ;
185+
186+ if ( i % 10 != 0 ) continue ;
187+
188+ actor . Add ( new Speed ( ) ) ;
189+
190+ defaultEntity . Set < Speed > ( ) ;
191+ frifloEntity . Add ( new Speed ( ) ) ;
192+ }
193+ }
194+ }
0 commit comments