├── .github
└── workflows
│ ├── benchmark.yml
│ └── test.yml
├── .gitignore
├── .gitmodules
├── .idea
└── .idea.Benchmark
│ └── .idea
│ ├── .gitignore
│ ├── .name
│ ├── encodings.xml
│ ├── indexLayout.xml
│ └── vcs.xml
├── ArrayECS
├── ArrayECS.csproj
└── World.cs
├── Benchmark.Arch
├── ArchContext.cs
├── Benchmark.Arch.csproj
└── System.cs
├── Benchmark.ArrayECS
├── ArrayECSContext.cs
└── Benchmark.ArrayECS.csproj
├── Benchmark.DefaultECS
├── Benchmark.DefaultECS.csproj
├── DefaultECSContext.cs
└── System.cs
├── Benchmark.DragonECS
├── Benchmark.DragonECS.csproj
├── DragonECSContext.cs
├── GenericAspects.cs
└── PointerInvocationSystem.cs
├── Benchmark.Fennecs
├── Benchmark.Fennecs.csproj
├── FennecsContext.cs
└── System.cs
├── Benchmark.FlecsNET
├── Benchmark.FlecsNET.csproj
└── FlecsNETContext.cs
├── Benchmark.FriFlo
├── Benchmark.FriFlo.csproj
├── FriFloContext.cs
└── FriFloSystem.cs
├── Benchmark.LeoEcs
├── Benchmark.LeoEcs.csproj
├── LeoEcsContext.cs
└── System.cs
├── Benchmark.LeoEcsLite
├── Benchmark.LeoEcsLite.csproj
├── LeoEcsLiteContext.cs
└── System.cs
├── Benchmark.MassiveECS
├── Benchmark.MassiveECS.csproj
└── MassiveEcsContext.cs
├── Benchmark.Morpeh
├── Benchmark.Morpeh.csproj
├── MorpehContext.cs
└── PointerInvocationStashSystem.cs
├── Benchmark.StaticEcs
├── Benchmark.StaticEcs.csproj
└── StaticEcsContext.cs
├── Benchmark.TinyECS
├── Benchmark.TinyECS.csproj
└── TinyEcsContext.cs
├── Benchmark.Xeno
├── Benchmark.Xeno.csproj
└── XenoContext.cs
├── Benchmark._Context
├── ArrayExtensions.cs
├── Benchmark._Context.csproj
├── Context.cs
├── IBenchmarkContext.cs
└── Ignore.cs
├── Benchmark._Generator
├── Benchmark._Generator.csproj
├── BenchmarkGenerator2.cs
├── MethodInliner.cs
├── Properties
│ └── launchSettings.json
└── SymbolExtensions.cs
├── Benchmark._Tests
├── Benchmark._Tests.csproj
├── Benchmark._Tests.csproj.DotSettings
├── Helper.cs
├── TestBenchmarks.cs
├── TestContexts.cs
└── TestOverhead.cs
├── Benchmark.sh
├── Benchmark.sln
├── Benchmark.sln.DotSettings
├── Benchmark.sln.DotSettings.user
├── Benchmark
├── Benchmark.csproj
├── Benchmarks
│ ├── Entities
│ │ ├── AddComponent
│ │ │ ├── Add1Component.cs
│ │ │ ├── Add1ComponentRandomOrder.cs
│ │ │ ├── Add1RandomComponent.cs
│ │ │ ├── Add1RandomComponentRandomOrder.cs
│ │ │ ├── Add2Components.cs
│ │ │ ├── Add2ComponentsRandomOrder.cs
│ │ │ ├── Add2RandomComponents.cs
│ │ │ ├── Add2RandomComponentsRandomOrder.cs
│ │ │ ├── Add3Components.cs
│ │ │ ├── Add3ComponentsRandomOrder.cs
│ │ │ ├── Add3RandomComponents.cs
│ │ │ ├── Add3RandomComponentsRandomOrder.cs
│ │ │ ├── Add4Components.cs
│ │ │ └── Add4ComponentsRandomOrder.cs
│ │ ├── CreateEntity
│ │ │ ├── CreateEmptyEntity.cs
│ │ │ ├── CreateEntityWith1Component.cs
│ │ │ ├── CreateEntityWith1RandomComponent.cs
│ │ │ ├── CreateEntityWith2Components.cs
│ │ │ ├── CreateEntityWith2RandomComponents.cs
│ │ │ ├── CreateEntityWith3Components.cs
│ │ │ ├── CreateEntityWith3RandomComponents.cs
│ │ │ └── CreateEntityWith4Components.cs
│ │ ├── DeleteEntity
│ │ │ └── DeleteEntity.cs
│ │ ├── RemoveComponent
│ │ │ ├── Remove1Component.cs
│ │ │ ├── Remove1ComponentRandomOrder.cs
│ │ │ ├── Remove2Components.cs
│ │ │ ├── Remove2ComponentsRandomOrder.cs
│ │ │ ├── Remove3Components.cs
│ │ │ ├── Remove3ComponentsRandomOrder.cs
│ │ │ ├── Remove4Components.cs
│ │ │ └── Remove4ComponentsRandomOrder.cs
│ │ └── StructuralChanges
│ │ │ ├── FourRemoveOneComponent.cs
│ │ │ ├── FourRemoveThreeComponents.cs
│ │ │ ├── FourRemoveTwoComponents.cs
│ │ │ ├── OneAddOneComponent.cs
│ │ │ ├── OneAddThreeComponents.cs
│ │ │ ├── OneAddTwoComponents.cs
│ │ │ ├── ThreeAddOneComponent.cs
│ │ │ ├── ThreeRemoveOneComponent.cs
│ │ │ ├── ThreeRemoveTwoComponents.cs
│ │ │ ├── TwoAddOneComponent.cs
│ │ │ ├── TwoAddTwoComponents.cs
│ │ │ └── TwoRemoveOneComponent.cs
│ ├── IBenchmark.cs
│ └── Systems
│ │ ├── SystemWith1Component.cs
│ │ ├── SystemWith2Components.cs
│ │ └── SystemWith3Components.cs
├── Constants.cs
├── Options.cs
├── Program.cs
└── Utils
│ ├── ContextColumn.cs
│ └── HashColumn.cs
├── CONTRIBUTING.md
├── FFS.StaticEcs.Workaround
└── FFS.StaticEcs.Workaround.csproj
├── LICENSE
├── LeoECS
└── LeoECS.csproj
├── LeoECSLite
└── LeoECSLite.csproj
├── README.md
├── Raw_Runner
├── Program.cs
└── Raw_Runner.csproj
└── Scellecs.Morpeh.Workaround
├── Scellecs.Morpeh.Workaround.csproj
└── WorldExtensions.cs
/.github/workflows/benchmark.yml:
--------------------------------------------------------------------------------
1 | name: Run parallel benchmarks
2 |
3 | on:
4 | push:
5 | branches: [ "main" ]
6 | paths:
7 | - './github/workflows/benchmark.yml'
8 | - './github/workflows/test.yml'
9 | - '**.cs'
10 | - '**.csproj'
11 | - '**.sln'
12 | workflow_dispatch:
13 |
14 | jobs:
15 | build:
16 | runs-on: ubuntu-latest
17 | outputs:
18 | matrix: ${{ steps.out.outputs.matrix }}
19 | steps:
20 | - uses: actions/checkout@v4
21 | with:
22 | fetch-depth: 1
23 | submodules: true
24 | - name: Setup .NET
25 | uses: actions/setup-dotnet@v4
26 | with:
27 | dotnet-version: 9.0.x
28 | - name: Restore Benchmark
29 | run: dotnet restore
30 | - name: Build Solution
31 | run: dotnet build -c Release /p:CheckCacheMisses=false
32 | - name: Build Benchmark
33 | run: dotnet publish -c Release /p:CheckCacheMisses=false Benchmark/Benchmark.csproj -o .benchmark
34 | - name: Prepare benchmarks list
35 | run: ./.benchmark/Benchmark --list > benchmarks.txt
36 | - name: Prepare Matrix
37 | id: out
38 | run: |
39 | echo "{\"benchmark\": $(jq --raw-input --slurp --compact-output 'split("\n") | map(select(. != ""))' benchmarks.txt)}" > matrix.json
40 | cat matrix.json
41 | echo "matrix=$(cat matrix.json)" >> $GITHUB_OUTPUT
42 | - name: Archive benchmark build
43 | uses: actions/upload-artifact@v4
44 | with:
45 | name: benchmark
46 | path: .benchmark/
47 | retention-days: 1
48 | overwrite: 'true'
49 | include-hidden-files: 'true'
50 |
51 | run:
52 | runs-on: ubuntu-latest
53 | needs: build
54 | strategy:
55 | max-parallel: 16
56 | matrix: ${{ fromJson(needs.build.outputs.matrix) }}
57 | steps:
58 | - uses: actions/checkout@v4
59 | with:
60 | fetch-depth: 1
61 | submodules: true
62 | - name: Setup .NET
63 | uses: actions/setup-dotnet@v4
64 | with:
65 | dotnet-version: 9.0.x
66 | - name: Download Benchmark
67 | uses: actions/download-artifact@v4
68 | with:
69 | name: benchmark
70 | path: .benchmark/
71 | - name: Run Benchmark
72 | run: |
73 | chmod +x ./.benchmark/Benchmark
74 | ./.benchmark/Benchmark benchmark=${{ matrix.benchmark }}
75 | - name: Upload Benchmark Report
76 | uses: actions/upload-artifact@v4
77 | with:
78 | name: benchmark-report-${{ matrix.benchmark }}
79 | path: |
80 | .benchmark_results/${{ matrix.benchmark }}/results/*.md
81 | .benchmark_results/hwinfo
82 | retention-days: 1
83 |
84 | merge-reports:
85 | runs-on: ubuntu-latest
86 | needs: run
87 | steps:
88 | - name: Download Benchmark Reports
89 | uses: actions/download-artifact@v4
90 | with:
91 | merge-multiple: true
92 | path: .benchmark_results/
93 | - name: Merge Benchmark Reports
94 | run: |
95 | find .benchmark_results/ | sed -e "s/[^-][^\/]*\// |/g" -e "s/|\([^ ]\)/|-\1/"
96 | echo -e "# Build from:\n" >> report.md
97 | echo -e "https://github.com/blackbone/other-ecs-benchmarks/commit/$GITHUB_SHA\n" >> report.md
98 | echo -e "HW Info:\n" >> report.md
99 | cat .benchmark_results/hwinfo >> report.md
100 | echo -e "\n" >> report.md
101 | find .benchmark_results -name '*.md' -print0 | while IFS= read -r -d '' file; do
102 | first_line=$(head -n 1 "$file")
103 | echo "$first_line|$file"
104 | done | sort | while IFS='|' read -r first_line file; do
105 | cat "$file" >> report.md
106 | echo -e "\n" >> report.md
107 | done
108 | # upload main report
109 | - name: Update Gist
110 | uses: exuanbo/actions-deploy-gist@v1
111 | with:
112 | token: ${{ secrets.TOKEN }}
113 | gist_id: 6d254a684cf580441bf58690ad9485c3
114 | file_path: report.md
115 | file_type: text
116 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: Test
2 |
3 | on:
4 | push:
5 | branches: [ "main" ]
6 | paths:
7 | - './github/workflows/benchmark.yml'
8 | - './github/workflows/test.yml'
9 | - '**.cs'
10 | - '**.csproj'
11 | - '**.sln'
12 | workflow_dispatch:
13 |
14 | jobs:
15 | build:
16 | runs-on: ubuntu-latest
17 | outputs:
18 | matrix: ${{ steps.out.outputs.matrix }}
19 | steps:
20 | - uses: actions/checkout@v4
21 | with:
22 | fetch-depth: 1
23 | submodules: true
24 | - name: Setup .NET
25 | uses: actions/setup-dotnet@v4
26 | with:
27 | dotnet-version: 9.0.x
28 | - name: Restore Benchmark
29 | run: dotnet restore
30 | - name: Build Benchmark
31 | run: dotnet build -c Release /p:CheckCacheMisses=false
32 | - name: Run Tests
33 | run: dotnet test -c Release -v quiet --nologo -l:"console;verbosity=normal"
34 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .benchmark_results/
2 | [Bb]in/
3 | [Oo]bj/
4 | .DS_Store
5 | .benchmark_bin
6 | .benchmark
7 | report.md
8 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "submodules/DragonECS"]
2 | path = submodules/DragonECS
3 | url = git@github.com:DCFApixels/DragonECS.git
4 | [submodule "submodules/morpeh"]
5 | path = submodules/morpeh
6 | url = git@github.com:scellecs/morpeh.git
7 | [submodule "submodules/leopotam-ecs"]
8 | path = submodules/leopotam-ecs
9 | url = git@github.com:Leopotam/ecs.git
10 | [submodule "submodules/leopotam-ecslite"]
11 | path = submodules/leopotam-ecslite
12 | url = git@github.com:Leopotam/ecslite.git
13 | [submodule "submodules/xeno"]
14 | path = submodules/xeno
15 | url = git@github.com:blackbone/xeno.git
16 | [submodule "submodules/StaticEcs"]
17 | path = submodules/StaticEcs
18 | url = git@github.com:Felid-Force-Studios/StaticEcs.git
19 | [submodule "submodules/massive-ecs"]
20 | path = submodules/massive-ecs
21 | url = git@github.com:nilpunch/massive-ecs.git
22 |
--------------------------------------------------------------------------------
/.idea/.idea.Benchmark/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Rider ignored files
5 | /projectSettingsUpdater.xml
6 | /.idea.Benchmark.iml
7 | /modules.xml
8 | /contentModel.xml
9 | # Editor-based HTTP Client requests
10 | /httpRequests/
11 | # Datasource local storage ignored files
12 | /dataSources/
13 | /dataSources.local.xml
14 |
--------------------------------------------------------------------------------
/.idea/.idea.Benchmark/.idea/.name:
--------------------------------------------------------------------------------
1 | Benchmark
--------------------------------------------------------------------------------
/.idea/.idea.Benchmark/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/.idea.Benchmark/.idea/indexLayout.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/.idea.Benchmark/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/ArrayECS/ArrayECS.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 | Array.ECS
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ArrayECS/World.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.Runtime.CompilerServices;
5 |
6 | namespace AECS {
7 | public abstract class ArraySystem
8 | {
9 | private ArrayWorld _world;
10 |
11 | protected float delta;
12 |
13 | internal ArrayWorld world {
14 | get => _world;
15 | set {
16 | _world = value;
17 | OnWorldInjected();
18 | }
19 | }
20 |
21 | protected abstract void OnWorldInjected();
22 |
23 | protected internal abstract void Update(float delta);
24 | }
25 |
26 | public abstract class ArraySystem : ArraySystem {
27 | private T[] data;
28 | private BitArray mask;
29 |
30 | protected sealed override void OnWorldInjected() {
31 | world.GetData(out data, out mask);
32 | }
33 |
34 | protected internal sealed override void Update(float delta) {
35 | this.delta = delta;
36 | for (int i = 0; i < base.world.EntityCount; i++) {
37 | if (mask.Get(i))
38 | OnUpdate(ref data[i]);
39 | }
40 | }
41 |
42 | protected abstract void OnUpdate(ref T c);
43 | }
44 |
45 | public abstract class ArraySystem : ArraySystem {
46 | private T1[] data1;
47 | private T2[] data2;
48 | private BitArray mask1;
49 | private BitArray mask2;
50 |
51 | private BitArray crossMask;
52 |
53 | protected sealed override void OnWorldInjected() {
54 | world.GetData(out data1, out mask1);
55 | world.GetData(out data2, out mask2);
56 |
57 | crossMask = new BitArray(mask1.Length);
58 | }
59 |
60 | protected internal sealed override void Update(float delta) {
61 | this.delta = delta;
62 |
63 | crossMask.Or(mask1).And(mask2);
64 | for (int i = 0; i < base.world.EntityCount; i++) {
65 | if (crossMask.Get(i))
66 | OnUpdate(ref data1[i], ref data2[i]);
67 | }
68 | }
69 |
70 | protected abstract void OnUpdate(ref T1 c1, ref T2 c2);
71 | }
72 |
73 | public abstract class ArraySystem : ArraySystem {
74 | private T1[] data1;
75 | private T2[] data2;
76 | private T3[] data3;
77 | private BitArray mask1;
78 | private BitArray mask2;
79 | private BitArray mask3;
80 |
81 | private BitArray crossMask;
82 |
83 | protected sealed override void OnWorldInjected() {
84 | world.GetData(out data1, out mask1);
85 | world.GetData(out data2, out mask2);
86 | world.GetData(out data3, out mask3);
87 |
88 | crossMask = new BitArray(mask1.Length);
89 | }
90 |
91 | protected internal sealed override void Update(float delta) {
92 | this.delta = delta;
93 |
94 | crossMask.Or(mask1).And(mask2).And(mask3);
95 | for (int i = 0; i < base.world.EntityCount; i++) {
96 | if (crossMask.Get(i))
97 | OnUpdate(ref data1[i], ref data2[i], ref data3[i]);
98 | }
99 | }
100 |
101 | protected abstract void OnUpdate(ref T1 c1, ref T2 c2, ref T3 c3);
102 | }
103 |
104 | public abstract class ArraySystem : ArraySystem {
105 | private T1[] data1;
106 | private T2[] data2;
107 | private T3[] data3;
108 | private T4[] data4;
109 | private BitArray mask1;
110 | private BitArray mask2;
111 | private BitArray mask3;
112 | private BitArray mask4;
113 |
114 | private BitArray crossMask;
115 |
116 | protected sealed override void OnWorldInjected() {
117 | world.GetData(out data1, out mask1);
118 | world.GetData(out data2, out mask2);
119 | world.GetData(out data3, out mask3);
120 | world.GetData(out data4, out mask4);
121 |
122 | crossMask = new BitArray(mask1.Length);
123 | }
124 |
125 | protected internal sealed override void Update(float delta) {
126 | this.delta = delta;
127 |
128 | crossMask.Or(mask1).And(mask2).And(mask3).And(mask4);
129 | for (int i = 0; i < base.world.EntityCount; i++) {
130 | if (crossMask.Get(i))
131 | OnUpdate(ref data1[i], ref data2[i], ref data3[i], ref data4[i]);
132 | }
133 | }
134 |
135 | protected abstract void OnUpdate(ref T1 c1, ref T2 c2, ref T3 c3, ref T4 c4);
136 | }
137 |
138 | public class ArrayWorld : IDisposable {
139 | private const ulong IS_ALIVE_MASK = 0b1000000000000000000000000000000000000000000000000000000000000000L;
140 | private const int ID_SHIFT = 31;
141 |
142 | private ulong[] entities;
143 | private readonly Dictionary components = new Dictionary();
144 | private readonly List systems = new List();
145 |
146 | public int EntityCount { get; private set; }
147 |
148 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
149 | public ArrayWorld(int entityCount) {
150 | entities = new ulong[entityCount];
151 | InitEntities(0, entityCount);
152 | EntityCount = 0;
153 | }
154 |
155 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
156 | private void InitEntities(int min, int max) {
157 | while (min < max) {
158 | entities[min] = ~IS_ALIVE_MASK & ((ulong)min << ID_SHIFT);
159 | min++;
160 | }
161 | }
162 |
163 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
164 | public ulong CreateEntity() {
165 | if (EntityCount == entities.Length) {
166 | throw new IndexOutOfRangeException();
167 | }
168 |
169 | entities[EntityCount] |= IS_ALIVE_MASK;
170 | return entities[EntityCount++];
171 | }
172 |
173 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
174 | public void DestroyEntity(ulong e) {
175 | var idx = getIdx(e);
176 | if (e != entities[idx]) return;
177 | entities[idx] = ++e & ~IS_ALIVE_MASK;
178 | EntityCount--;
179 | }
180 |
181 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
182 | public void AddComponent(in ulong e, in T c1) {
183 | var idx = getIdx(e);
184 | if (e != entities[idx]) return;
185 | GetData(out T[] data, out var mask);
186 | data[idx] = c1;
187 | mask.Set((int)idx, true);
188 | }
189 |
190 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
191 | public void RemoveComponent(in ulong e) {
192 | var idx = getIdx(e);
193 | if (e != entities[idx]) return;
194 | GetData(out T[] data, out var mask);
195 | data[idx] = default;
196 | mask.Set((int)idx, false);
197 | }
198 |
199 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
200 | public int Count() {
201 | if (!components.TryGetValue(typeof(T1), out var data1)) return 0;
202 | return data1.mask.Count;
203 | }
204 |
205 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
206 | public int Count() {
207 | if (!components.TryGetValue(typeof(T1), out var data1)) return 0;
208 | if (!components.TryGetValue(typeof(T2), out var data2)) return 0;
209 |
210 | return new BitArray(data1.mask).And(data2.mask).Count;
211 | }
212 |
213 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
214 | public int Count() {
215 | if (!components.TryGetValue(typeof(T1), out var data1)) return 0;
216 | if (!components.TryGetValue(typeof(T2), out var data2)) return 0;
217 | if (!components.TryGetValue(typeof(T3), out var data3)) return 0;
218 |
219 | return new BitArray(data1.mask).And(data2.mask).And(data3.mask).Count;
220 | }
221 |
222 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
223 | public int Count() {
224 | if (!components.TryGetValue(typeof(T1), out var data1)) return 0;
225 | if (!components.TryGetValue(typeof(T2), out var data2)) return 0;
226 | if (!components.TryGetValue(typeof(T3), out var data3)) return 0;
227 | if (!components.TryGetValue(typeof(T4), out var data4)) return 0;
228 |
229 | return new BitArray(data1.mask).And(data2.mask).And(data3.mask).And(data4.mask).Count;
230 | }
231 |
232 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
233 | public T Get(in ulong e) {
234 | var idx = getIdx(e);
235 | if (e != entities[idx]) return default;
236 |
237 | return !components.TryGetValue(typeof(T), out var data) ? default : ((Data)data).Get(idx);
238 | }
239 |
240 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
241 | public bool Ref(in ulong e, ref T c) {
242 | var idx = getIdx(e);
243 | if (e != entities[idx]) return false;
244 |
245 | return components.TryGetValue(typeof(T), out var data) && ((Data)data).Ref(idx, ref c);
246 | }
247 |
248 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
249 | public void Dispose() {
250 | entities = null;
251 | }
252 |
253 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
254 | public void Preallocate(int entityCount) {
255 | components[typeof(T)] = new Data(entityCount);
256 | }
257 |
258 |
259 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
260 | private static uint getIdx(ulong entityId) => (uint)((entityId & ~IS_ALIVE_MASK) >> ID_SHIFT);
261 |
262 | private class Data {
263 | public readonly BitArray mask;
264 | protected Data(int count) => mask = new BitArray(count);
265 | }
266 |
267 | private class Data : Data {
268 | internal readonly T[] data;
269 |
270 | public Data(int count):base(count) => data = new T[count];
271 |
272 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
273 | public void Add(in uint idx, in T c) {
274 | if (mask.Get((int)idx)) return;
275 | if (idx >= data.Length) throw new IndexOutOfRangeException();
276 | data[idx] = c;
277 | }
278 |
279 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
280 | public void Remove(in uint idx) {
281 | if (idx >= data.Length) return;
282 | if (!mask.Get((int)idx)) return;
283 | data[idx] = default;
284 | }
285 |
286 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
287 | public T Get(in uint idx) {
288 | if (idx >= data.Length) return default;
289 | if (!mask.Get((int)idx)) return default;
290 | return data[idx];
291 | }
292 |
293 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
294 | public bool Ref(in uint idx, ref T c) {
295 | if (idx >= data.Length) return false;
296 | if (!mask.Get((int)idx)) return false;
297 | c = ref data[idx];
298 | return true;
299 | }
300 | }
301 |
302 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
303 | public void Update(float delta) {
304 | foreach (var system in systems)
305 | system.Update(delta);
306 | }
307 |
308 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
309 | public void AddSystem(ArraySystem system) {
310 | system.world = this;
311 | systems.Add(system);
312 | }
313 |
314 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
315 | internal void GetData(out T[] data, out BitArray mask) {
316 | Data dataWrapper;
317 | if (!components.TryGetValue(typeof(T), out var d))
318 | components[typeof(T)] = dataWrapper = new Data(entities.Length);
319 | else
320 | dataWrapper = (Data)d;
321 |
322 | data = dataWrapper.data;
323 | mask = dataWrapper.mask;
324 | }
325 | }
326 | }
327 |
--------------------------------------------------------------------------------
/Benchmark.Arch/Benchmark.Arch.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0
5 | 12
6 | disable
7 | disable
8 | true
9 | true
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Benchmark.Arch/System.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.CompilerServices;
2 | using Arch.Core;
3 |
4 | namespace Benchmark.Arch;
5 |
6 | public unsafe class System(delegate*[ method)
7 | {
8 | private readonly struct ForEach(delegate*][ method) : IForEach
9 | {
10 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
11 | public void Update(ref T t0) => method(ref t0);
12 | }
13 |
14 | private ForEach _forEach = new(method);
15 | private readonly QueryDescription _query = new([typeof(T)]);
16 |
17 | public void ForEachQuery(World world) => world.InlineQuery(_query, ref _forEach);
18 | }
19 |
20 | public unsafe class System(delegate*][ method)
21 | {
22 | private readonly struct ForEach(delegate*][ method) : IForEach
23 | {
24 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
25 | public void Update(ref T1 t1, ref T2 t2) => method(ref t1, ref t2);
26 | }
27 |
28 | private ForEach _forEach = new(method);
29 | private readonly QueryDescription _query = new([typeof(T1), typeof(T2)]);
30 |
31 | public void ForEachQuery(World world) => world.InlineQuery(_query, ref _forEach);
32 | }
33 |
34 | public unsafe class System(delegate*][ method)
35 | {
36 | private readonly struct ForEach(delegate*][ method) : IForEach
37 | {
38 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
39 | public void Update(ref T1 t1, ref T2 t2, ref T3 t3) => method(ref t1, ref t2, ref t3);
40 | }
41 |
42 | private ForEach _forEach = new(method);
43 |
44 | private readonly QueryDescription _query = new([typeof(T1), typeof(T2), typeof(T3)]);
45 |
46 | public void ForEachQuery(World world) => world.InlineQuery(_query, ref _forEach);
47 | }
48 |
49 | public unsafe class System(delegate*][ method)
50 | {
51 | private readonly struct ForEach(delegate*][ method) : IForEach
52 | {
53 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
54 | public void Update(ref T1 t1, ref T2 t2, ref T3 t3, ref T4 t4) => method(ref t1, ref t2, ref t3, ref t4);
55 | }
56 |
57 | private ForEach _forEach = new(method);
58 |
59 | private readonly QueryDescription _query = new([typeof(T1), typeof(T2), typeof(T3), typeof(T4)]);
60 |
61 | public void ForEachQuery(World world) => world.InlineQuery(_query, ref _forEach);
62 | }
--------------------------------------------------------------------------------
/Benchmark.ArrayECS/Benchmark.ArrayECS.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0
5 | enable
6 | disable
7 |
8 |
9 |
10 | true
11 |
12 |
13 |
14 | true
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Benchmark.DefaultECS/Benchmark.DefaultECS.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0
5 | 12
6 | disable
7 | disable
8 | true
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | all
19 | runtime; build; native; contentfiles; analyzers; buildtransitive
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Benchmark.DefaultECS/System.cs:
--------------------------------------------------------------------------------
1 | using DefaultEcs;
2 | using DefaultEcs.System;
3 |
4 | namespace Benchmark.DefaultECS;
5 |
6 | public sealed unsafe class System(EntityQueryBuilder query, delegate*][ method) : ISystem
7 | {
8 | public bool IsEnabled { get; set; } = true;
9 | public void Dispose() { }
10 |
11 | public void Update(float state)
12 | {
13 | foreach (var entity in query.AsEnumerable())
14 | method(ref entity.Get());
15 | }
16 | }
17 |
18 | public sealed unsafe class System(EntityQueryBuilder query, delegate*][ method) : ISystem
19 | {
20 | public bool IsEnabled { get; set; } = true;
21 | public void Dispose() { }
22 |
23 | public void Update(float state)
24 | {
25 | foreach (var entity in query.AsEnumerable())
26 | method(ref entity.Get(), ref entity.Get());
27 | }
28 | }
29 |
30 | public sealed unsafe class System(EntityQueryBuilder query, delegate*][ method) : ISystem
31 | {
32 | public bool IsEnabled { get; set; } = true;
33 | public void Dispose() { }
34 |
35 | public void Update(float state)
36 | {
37 | foreach (var entity in query.AsEnumerable())
38 | method(ref entity.Get(), ref entity.Get(), ref entity.Get());
39 | }
40 | }
41 |
42 | public sealed unsafe class System(EntityQueryBuilder query, delegate*][ method) : ISystem
43 | {
44 | public bool IsEnabled { get; set; } = true;
45 | public void Dispose() { }
46 |
47 | public void Update(float state)
48 | {
49 | foreach (var entity in query.AsEnumerable())
50 | method(ref entity.Get(), ref entity.Get(), ref entity.Get(), ref entity.Get());
51 | }
52 | }
--------------------------------------------------------------------------------
/Benchmark.DragonECS/Benchmark.DragonECS.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0
5 | 12
6 | disable
7 | disable
8 | true
9 | true
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/Benchmark.DragonECS/GenericAspects.cs:
--------------------------------------------------------------------------------
1 | using DCFApixels.DragonECS;
2 |
3 | namespace Benchmark.DragonECS;
4 |
5 | public class Aspect : EcsAspect
6 | where T1 : struct, IEcsComponent
7 | {
8 | public EcsPool Components1;
9 |
10 | protected override void Init(Builder b)
11 | {
12 | base.Init(b);
13 | Components1 = b.Inc();
14 | }
15 | }
16 |
17 | public class Aspect : EcsAspect
18 | where T1 : struct, IEcsComponent
19 | where T2 : struct, IEcsComponent
20 | {
21 | public EcsPool Components1;
22 | public EcsPool Components2;
23 |
24 | protected override void Init(Builder b)
25 | {
26 | base.Init(b);
27 | Components1 = b.Inc();
28 | Components2 = b.Inc();
29 | }
30 | }
31 |
32 | public class Aspect : EcsAspect
33 | where T1 : struct, IEcsComponent
34 | where T2 : struct, IEcsComponent
35 | where T3 : struct, IEcsComponent
36 | {
37 | public EcsPool Components1;
38 | public EcsPool Components2;
39 | public EcsPool Components3;
40 |
41 | protected override void Init(Builder b)
42 | {
43 | base.Init(b);
44 | Components1 = b.Inc();
45 | Components2 = b.Inc();
46 | Components3 = b.Inc();
47 | }
48 | }
49 |
50 | public class Aspect : EcsAspect
51 | where T1 : struct, IEcsComponent
52 | where T2 : struct, IEcsComponent
53 | where T3 : struct, IEcsComponent
54 | where T4 : struct, IEcsComponent
55 | {
56 | public EcsPool Components1;
57 | public EcsPool Components2;
58 | public EcsPool Components3;
59 | public EcsPool Components4;
60 |
61 | protected override void Init(Builder b)
62 | {
63 | base.Init(b);
64 | Components1 = b.Inc();
65 | Components2 = b.Inc();
66 | Components3 = b.Inc();
67 | Components4 = b.Inc();
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/Benchmark.DragonECS/PointerInvocationSystem.cs:
--------------------------------------------------------------------------------
1 | using DCFApixels.DragonECS;
2 |
3 | namespace Benchmark.DragonECS;
4 |
5 | public class PointerInvocationSystem : IEcsRun
6 | where T1 : struct, IEcsComponent
7 | {
8 | private readonly unsafe delegate*][ _method;
9 | private readonly EcsWorld _world;
10 | private readonly Aspect _aspect;
11 |
12 | public unsafe PointerInvocationSystem(EcsWorld world, delegate*][ method)
13 | {
14 | _world = world;
15 | _method = method;
16 | _aspect = _world.GetAspect>();
17 | }
18 |
19 | public unsafe void Run()
20 | {
21 | foreach (var e in _world.Where(_aspect))
22 | _method(ref _aspect.Components1.Get(e));
23 | }
24 | }
25 |
26 | public class PointerInvocationSystem : IEcsRun
27 | where T1 : struct, IEcsComponent
28 | where T2 : struct, IEcsComponent
29 | {
30 | private readonly unsafe delegate*][ _method;
31 | private readonly EcsWorld _world;
32 | private readonly Aspect _aspect;
33 |
34 | public unsafe PointerInvocationSystem(EcsWorld world, delegate*][ method)
35 | {
36 | _world = world;
37 | _method = method;
38 | _aspect = _world.GetAspect>();
39 | }
40 |
41 | public unsafe void Run()
42 | {
43 | foreach (var e in _world.Where(_aspect))
44 | _method(ref _aspect.Components1.Get(e), ref _aspect.Components2.Get(e));
45 | }
46 | }
47 |
48 | public class PointerInvocationSystem : IEcsRun
49 | where T1 : struct, IEcsComponent
50 | where T2 : struct, IEcsComponent
51 | where T3 : struct, IEcsComponent
52 | {
53 | private readonly unsafe delegate*][ _method;
54 | private readonly EcsWorld _world;
55 | private readonly Aspect _aspect;
56 |
57 | public unsafe PointerInvocationSystem(EcsWorld world, delegate*][ method)
58 | {
59 | _world = world;
60 | _method = method;
61 | _aspect = _world.GetAspect>();
62 | }
63 |
64 | public unsafe void Run()
65 | {
66 | foreach (var e in _world.Where(_aspect))
67 | _method(ref _aspect.Components1.Get(e), ref _aspect.Components2.Get(e), ref _aspect.Components3.Get(e));
68 | }
69 | }
70 |
71 | public class PointerInvocationSystem : IEcsRun
72 | where T1 : struct, IEcsComponent
73 | where T2 : struct, IEcsComponent
74 | where T3 : struct, IEcsComponent
75 | where T4 : struct, IEcsComponent
76 | {
77 | private readonly unsafe delegate*][ _method;
78 | private readonly EcsWorld _world;
79 | private readonly Aspect _aspect;
80 |
81 | public unsafe PointerInvocationSystem(EcsWorld world, delegate*][ method)
82 | {
83 | _world = world;
84 | _method = method;
85 | _aspect = _world.GetAspect>();
86 | }
87 |
88 | public unsafe void Run()
89 | {
90 | foreach (var e in _world.Where(_aspect))
91 | _method(ref _aspect.Components1.Get(e), ref _aspect.Components2.Get(e), ref _aspect.Components3.Get(e), ref _aspect.Components4.Get(e));
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/Benchmark.Fennecs/Benchmark.Fennecs.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0
5 | 12
6 | disable
7 | disable
8 | true
9 | true
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/Benchmark.Fennecs/System.cs:
--------------------------------------------------------------------------------
1 | using fennecs;
2 |
3 | #pragma warning disable CS0649 // Field is never assigned to, and will always have its default value
4 |
5 | namespace Benchmark.Fennecs;
6 |
7 | public interface ISystem
8 | {
9 | void Run(float delta);
10 | }
11 |
12 | public unsafe class System : ISystem
13 | where T1 : struct
14 | {
15 | private readonly ComponentAction _del;
16 | private readonly delegate*][ _method;
17 | private readonly Stream _stream;
18 |
19 | public System(delegate*][ method, Stream stream)
20 | {
21 | _del = Invoke;
22 | _method = method;
23 | _stream = stream;
24 | }
25 |
26 | public void Run(float delta)
27 | {
28 | _stream.Job(_del);
29 | }
30 |
31 | private void Invoke(ref T1 c0)
32 | {
33 | _method(ref c0);
34 | }
35 | }
36 |
37 | public unsafe class System : ISystem
38 | where T1 : struct
39 | where T2 : struct
40 | {
41 | private readonly ComponentAction _del;
42 | private readonly delegate*][ _method;
43 | private readonly Stream _stream;
44 |
45 | public System(delegate*][ method, Stream stream)
46 | {
47 | _del = Invoke;
48 | _method = method;
49 | _stream = stream;
50 | }
51 |
52 | public void Run(float delta)
53 | {
54 | _stream.Job(_del);
55 | }
56 |
57 | private void Invoke(ref T1 c0, ref T2 c1)
58 | {
59 | _method(ref c0, ref c1);
60 | }
61 | }
62 |
63 | public unsafe class System : ISystem
64 | where T1 : struct
65 | where T2 : struct
66 | where T3 : struct
67 | {
68 | private readonly ComponentAction _del;
69 | private readonly delegate*][ _method;
70 | private readonly Stream _stream;
71 |
72 | public System(delegate*][ method, Stream stream)
73 | {
74 | _del = Invoke;
75 | _method = method;
76 | _stream = stream;
77 | }
78 |
79 | public void Run(float delta)
80 | {
81 | _stream.Job(_del);
82 | }
83 |
84 | private void Invoke(ref T1 c0, ref T2 c1, ref T3 c2)
85 | {
86 | _method(ref c0, ref c1, ref c2);
87 | }
88 | }
89 |
90 | public unsafe class System : ISystem
91 | where T1 : struct
92 | where T2 : struct
93 | where T3 : struct
94 | where T4 : struct
95 | {
96 | private readonly ComponentAction _del;
97 | private readonly delegate*][ _method;
98 | private readonly Stream _stream;
99 |
100 | public System(delegate*][ method, Stream stream)
101 | {
102 | _del = Invoke;
103 | _method = method;
104 | _stream = stream;
105 | }
106 |
107 | public void Run(float delta)
108 | {
109 | _stream.Job(_del);
110 | }
111 |
112 | private void Invoke(ref T1 c0, ref T2 c1, ref T3 c2, ref T4 c3)
113 | {
114 | _method(ref c0, ref c1, ref c2, ref c3);
115 | }
116 | }
--------------------------------------------------------------------------------
/Benchmark.FlecsNET/Benchmark.FlecsNET.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0
5 | 12
6 | disable
7 | disable
8 | true
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Benchmark.FriFlo/Benchmark.FriFlo.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0
5 | 12
6 | disable
7 | disable
8 | true
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Benchmark.FriFlo/FriFloSystem.cs:
--------------------------------------------------------------------------------
1 | using Friflo.Engine.ECS;
2 | using Friflo.Engine.ECS.Systems;
3 |
4 | namespace Benchmark.FriFlo;
5 |
6 | public unsafe class FriFloSystem(delegate*][ method) : QuerySystem
7 | where T1 : struct, IComponent
8 | {
9 | protected override void OnUpdate()
10 | {
11 | var job = Query.ForEach(Job);
12 | job.Run();
13 | }
14 |
15 | private void Job(Chunk c1, ChunkEntities entities)
16 | {
17 | for (var i = 0; i < c1.Length; i++) {
18 | method(ref c1[i]);
19 | }
20 | }
21 | }
22 |
23 | public unsafe class FriFloSystem(delegate*][ method) : QuerySystem
24 | where T1 : struct, IComponent
25 | where T2 : struct, IComponent
26 | {
27 | protected override void OnUpdate()
28 | {
29 | var job = Query.ForEach(Job);
30 | job.Run();
31 | }
32 |
33 | private void Job(Chunk c1, Chunk c2, ChunkEntities entities)
34 | {
35 | for (var i = 0; i < entities.Length; i++) {
36 | method(ref c1[i], ref c2[i]);
37 | }
38 | }
39 | }
40 |
41 | public unsafe class FriFloSystem(delegate*][ method) : QuerySystem
42 | where T1 : struct, IComponent
43 | where T2 : struct, IComponent
44 | where T3 : struct, IComponent
45 | {
46 | protected override void OnUpdate()
47 | {
48 | var job = Query.ForEach(Job);
49 | job.Run();
50 | }
51 |
52 | private void Job(Chunk c1, Chunk c2, Chunk c3, ChunkEntities entities)
53 | {
54 | for (var i = 0; i < entities.Length; i++) {
55 | method(ref c1[i], ref c2[i], ref c3[i]);
56 | }
57 | }
58 | }
59 |
60 | public unsafe class FriFloSystem(delegate*][ method) : QuerySystem
61 | where T1 : struct, IComponent
62 | where T2 : struct, IComponent
63 | where T3 : struct, IComponent
64 | where T4 : struct, IComponent
65 | {
66 | protected override void OnUpdate()
67 | {
68 | var job = Query.ForEach(Job);
69 | job.Run();
70 | }
71 |
72 | private void Job(Chunk c1, Chunk c2, Chunk c3, Chunk c4, ChunkEntities entities)
73 | {
74 | for (var i = 0; i < entities.Length; i++) {
75 | method(ref c1[i], ref c2[i], ref c3[i], ref c4[i]);
76 | }
77 | }
78 | }
--------------------------------------------------------------------------------
/Benchmark.LeoEcs/Benchmark.LeoEcs.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0
5 | 12
6 | disable
7 | disable
8 | true
9 | true
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/Benchmark.LeoEcs/System.cs:
--------------------------------------------------------------------------------
1 | using Leopotam.Ecs;
2 |
3 | #pragma warning disable CS0649 // Field is never assigned to, and will always have its default value
4 |
5 | namespace Benchmark.LeoEcs;
6 |
7 | public unsafe class System(delegate*][ method) : IEcsRunSystem
8 | where T1 : struct
9 | {
10 | private readonly EcsFilter _filter;
11 |
12 | public void Run()
13 | {
14 | for (int i = 0, iMax = _filter.GetEntitiesCount(); i < iMax; i++)
15 | method(ref _filter.Get1(i));
16 | }
17 | }
18 |
19 | public unsafe class System(delegate*][ method) : IEcsRunSystem
20 | where T1 : struct
21 | where T2 : struct
22 | {
23 | private readonly EcsFilter _filter;
24 |
25 | public void Run()
26 | {
27 | for (int i = 0, iMax = _filter.GetEntitiesCount(); i < iMax; i++)
28 | method(ref _filter.Get1(i), ref _filter.Get2(i));
29 | }
30 | }
31 |
32 | public unsafe class System(delegate*][ method) : IEcsRunSystem
33 | where T1 : struct
34 | where T2 : struct
35 | where T3 : struct
36 | {
37 | private readonly EcsFilter _filter;
38 |
39 | public void Run()
40 | {
41 | for (int i = 0, iMax = _filter.GetEntitiesCount(); i < iMax; i++)
42 | method(ref _filter.Get1(i), ref _filter.Get2(i), ref _filter.Get3(i));
43 | }
44 | }
45 |
46 | public unsafe class System(delegate*][ method) : IEcsRunSystem
47 | where T1 : struct
48 | where T2 : struct
49 | where T3 : struct
50 | where T4 : struct
51 | {
52 | private readonly EcsFilter _filter;
53 |
54 | public void Run()
55 | {
56 | for (int i = 0, iMax = _filter.GetEntitiesCount(); i < iMax; i++)
57 | method(ref _filter.Get1(i), ref _filter.Get2(i), ref _filter.Get3(i), ref _filter.Get4(i));
58 | }
59 | }
--------------------------------------------------------------------------------
/Benchmark.LeoEcsLite/Benchmark.LeoEcsLite.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0
5 | 12
6 | disable
7 | disable
8 | true
9 | true
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/Benchmark.LeoEcsLite/System.cs:
--------------------------------------------------------------------------------
1 | using Leopotam.EcsLite;
2 |
3 | #pragma warning disable CS0649 // Field is never assigned to, and will always have its default value
4 |
5 | namespace Benchmark.LeoEcsLite;
6 |
7 | public unsafe class System(delegate*][ method) : IEcsInitSystem, IEcsRunSystem
8 | where T1 : struct
9 | {
10 | private EcsFilter _filter;
11 | private EcsPool _p1;
12 |
13 | public void Init(IEcsSystems systems)
14 | {
15 | var world = systems.GetWorld();
16 | _filter = world.Filter().End();
17 | _p1 = world.GetPool();
18 | }
19 |
20 | public void Run(IEcsSystems systems)
21 | {
22 | foreach (var entity in _filter!)
23 | method(ref _p1!.Get(entity));
24 | }
25 | }
26 |
27 | public unsafe class System(delegate*][ method) : IEcsInitSystem, IEcsRunSystem
28 | where T1 : struct
29 | where T2 : struct
30 | {
31 | private EcsFilter _filter;
32 | private EcsPool _p1;
33 | private EcsPool _p2;
34 |
35 | public void Init(IEcsSystems systems)
36 | {
37 | var world = systems.GetWorld();
38 | _filter = world.Filter().End();
39 | _p1 = world.GetPool();
40 | _p2 = world.GetPool();
41 | }
42 |
43 | public void Run(IEcsSystems systems)
44 | {
45 | foreach (var entity in _filter!)
46 | method(ref _p1!.Get(entity), ref _p2!.Get(entity));
47 | }
48 | }
49 |
50 | public unsafe class System(delegate*][ method) : IEcsInitSystem, IEcsRunSystem
51 | where T1 : struct
52 | where T2 : struct
53 | where T3 : struct
54 | {
55 | private EcsFilter _filter;
56 | private EcsPool _p1;
57 | private EcsPool _p2;
58 | private EcsPool _p3;
59 |
60 | public void Init(IEcsSystems systems)
61 | {
62 | var world = systems.GetWorld();
63 | _filter = world.Filter().End();
64 | _p1 = world.GetPool();
65 | _p2 = world.GetPool();
66 | _p3 = world.GetPool();
67 | }
68 |
69 | public void Run(IEcsSystems systems)
70 | {
71 | foreach (var entity in _filter!)
72 | method(ref _p1!.Get(entity), ref _p2!.Get(entity), ref _p3!.Get(entity));
73 | }
74 | }
75 |
76 | public unsafe class System(delegate*][ method)
77 | : IEcsInitSystem, IEcsRunSystem
78 | where T1 : struct
79 | where T2 : struct
80 | where T3 : struct
81 | where T4 : struct
82 | {
83 | private EcsFilter _filter;
84 | private EcsPool _p1;
85 | private EcsPool _p2;
86 | private EcsPool _p3;
87 | private EcsPool _p4;
88 |
89 | public void Init(IEcsSystems systems)
90 | {
91 | var world = systems.GetWorld();
92 | _filter = world.Filter().End();
93 | _p1 = world.GetPool();
94 | _p2 = world.GetPool();
95 | _p3 = world.GetPool();
96 | _p4 = world.GetPool();
97 | }
98 |
99 | public void Run(IEcsSystems systems)
100 | {
101 | foreach (var entity in _filter!)
102 | method(ref _p1!.Get(entity), ref _p2!.Get(entity), ref _p3!.Get(entity), ref _p4!.Get(entity));
103 | }
104 | }
--------------------------------------------------------------------------------
/Benchmark.MassiveECS/Benchmark.MassiveECS.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0
5 | 12
6 | disable
7 | disable
8 | true
9 | true
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Benchmark.Morpeh/Benchmark.Morpeh.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0
5 | 12
6 | disable
7 | disable
8 | true
9 | true
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/Benchmark.Morpeh/PointerInvocationStashSystem.cs:
--------------------------------------------------------------------------------
1 | using Scellecs.Morpeh;
2 |
3 | namespace Benchmark.Morpeh;
4 |
5 | public class PointerInvocationStashSystem : ISystem
6 | where T1 : struct, IComponent
7 | {
8 | private readonly unsafe delegate*][ _method;
9 | private Filter _filter;
10 | private Stash _stash1;
11 |
12 | #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
13 | public unsafe PointerInvocationStashSystem(delegate*][ method)
14 | {
15 | _method = method;
16 | }
17 | #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
18 |
19 | public World World { get; set; }
20 |
21 | public void OnAwake()
22 | {
23 | _stash1 = World.GetStash();
24 | _filter = World.Filter.With().Build();
25 | }
26 |
27 | public void Dispose()
28 | {
29 | }
30 |
31 | public unsafe void OnUpdate(float delta)
32 | {
33 | foreach (var entity in _filter)
34 | _method(ref _stash1.Get(entity));
35 | }
36 | }
37 |
38 | public class PointerInvocationStashSystem : ISystem
39 | where T1 : struct, IComponent
40 | where T2 : struct, IComponent
41 | {
42 | private readonly unsafe delegate*][ _method;
43 | private Filter _filter;
44 | private Stash _stash1;
45 | private Stash _stash2;
46 |
47 | #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
48 | public unsafe PointerInvocationStashSystem(delegate*][ method)
49 | {
50 | _method = method;
51 | }
52 | #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
53 |
54 | public World World { get; set; }
55 |
56 | public void OnAwake()
57 | {
58 | _stash1 = World.GetStash();
59 | _stash2 = World.GetStash();
60 | _filter = World.Filter.With().With().Build();
61 | }
62 |
63 | public void Dispose()
64 | {
65 | }
66 |
67 | public unsafe void OnUpdate(float delta)
68 | {
69 | foreach (var entity in _filter)
70 | _method(ref _stash1.Get(entity), ref _stash2.Get(entity));
71 | }
72 | }
73 |
74 | public class PointerInvocationStashSystem : ISystem
75 | where T1 : struct, IComponent
76 | where T2 : struct, IComponent
77 | where T3 : struct, IComponent
78 | {
79 | private readonly unsafe delegate*][ _method;
80 | private Filter _filter;
81 | private Stash _stash1;
82 | private Stash _stash2;
83 | private Stash _stash3;
84 |
85 | #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
86 | public unsafe PointerInvocationStashSystem(delegate*][ method)
87 | {
88 | _method = method;
89 | }
90 | #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
91 |
92 | public World World { get; set; }
93 |
94 | public unsafe void OnUpdate(float deltaTime)
95 | {
96 | foreach (var entity in _filter)
97 | _method(ref _stash1.Get(entity), ref _stash2.Get(entity), ref _stash3.Get(entity));
98 | }
99 |
100 | public void Dispose()
101 | {
102 | }
103 |
104 | public void OnAwake()
105 | {
106 | _stash1 = World.GetStash();
107 | _stash2 = World.GetStash();
108 | _stash3 = World.GetStash();
109 | _filter = World.Filter.With().With().With().Build();
110 | }
111 | }
112 |
113 | public class PointerInvocationStashSystem : ISystem
114 | where T1 : struct, IComponent
115 | where T2 : struct, IComponent
116 | where T3 : struct, IComponent
117 | where T4 : struct, IComponent
118 | {
119 | private readonly unsafe delegate*][ _method;
120 | private Filter _filter;
121 | private Stash _stash1;
122 | private Stash _stash2;
123 | private Stash _stash3;
124 | private Stash _stash4;
125 |
126 | #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
127 | public unsafe PointerInvocationStashSystem(delegate*][ method)
128 | {
129 | _method = method;
130 | }
131 | #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
132 |
133 | public World World { get; set; }
134 |
135 | public void OnAwake()
136 | {
137 | _stash1 = World.GetStash();
138 | _stash2 = World.GetStash();
139 | _stash3 = World.GetStash();
140 | _stash4 = World.GetStash();
141 | _filter = World.Filter.With().With().With().With().Build();
142 | }
143 |
144 | public void Dispose()
145 | {
146 | }
147 |
148 | public unsafe void OnUpdate(float deltaTime)
149 | {
150 | foreach (var entity in _filter)
151 | _method(ref _stash1.Get(entity), ref _stash2.Get(entity), ref _stash3.Get(entity), ref _stash4.Get(entity));
152 | }
153 | }
154 |
--------------------------------------------------------------------------------
/Benchmark.StaticEcs/Benchmark.StaticEcs.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0
5 | disable
6 | enable
7 | true
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/Benchmark.TinyECS/Benchmark.TinyECS.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0
5 | 13
6 | disable
7 | disable
8 | true
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Benchmark.TinyECS/TinyEcsContext.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Benchmark.Context;
3 | using DCFApixels.DragonECS;
4 | using TinyEcs;
5 | using IComponent = Scellecs.Morpeh.IComponent;
6 |
7 | namespace Benchmark.TinyECS;
8 |
9 | public sealed class TinyEcsContext : IBenchmarkContext
10 | {
11 | private TinyEcs.World _world;
12 | private Scheduler _scheduler;
13 |
14 | public bool DeletesEntityOnLastComponentDeletion => false;
15 | public int NumberOfLivingEntities { get; private set; }
16 |
17 | public void Setup() {
18 | _world = new TinyEcs.World();
19 | _scheduler = new Scheduler(_world);
20 | }
21 |
22 | public void FinishSetup() {}
23 |
24 | public void Warmup(in int poolId) where T1 : struct, IComponent, IEcsComponent, Xeno.IComponent, Friflo.Engine.ECS.IComponent, FFS.Libraries.StaticEcs.IComponent {}
25 |
26 | public void Warmup(in int poolId) where T1 : struct, IComponent, IEcsComponent, Xeno.IComponent, Friflo.Engine.ECS.IComponent, FFS.Libraries.StaticEcs.IComponent where T2 : struct, IComponent, IEcsComponent, Xeno.IComponent, Friflo.Engine.ECS.IComponent, FFS.Libraries.StaticEcs.IComponent {}
27 | public void Warmup(in int poolId) where T1 : struct, IComponent, IEcsComponent, Xeno.IComponent, Friflo.Engine.ECS.IComponent, FFS.Libraries.StaticEcs.IComponent where T2 : struct, IComponent, IEcsComponent, Xeno.IComponent, Friflo.Engine.ECS.IComponent, FFS.Libraries.StaticEcs.IComponent where T3 : struct, IComponent, IEcsComponent, Xeno.IComponent, Friflo.Engine.ECS.IComponent, FFS.Libraries.StaticEcs.IComponent {}
28 | public void Warmup(in int poolId) where T1 : struct, IComponent, IEcsComponent, Xeno.IComponent, Friflo.Engine.ECS.IComponent, FFS.Libraries.StaticEcs.IComponent where T2 : struct, IComponent, IEcsComponent, Xeno.IComponent, Friflo.Engine.ECS.IComponent, FFS.Libraries.StaticEcs.IComponent where T3 : struct, IComponent, IEcsComponent, Xeno.IComponent, Friflo.Engine.ECS.IComponent, FFS.Libraries.StaticEcs.IComponent where T4 : struct, IComponent, IEcsComponent, Xeno.IComponent, Friflo.Engine.ECS.IComponent, FFS.Libraries.StaticEcs.IComponent {}
29 | public void Cleanup() {}
30 | public void Dispose()
31 | {
32 | _scheduler = null;
33 |
34 | _world.Dispose();
35 | _world = null;
36 | }
37 |
38 | public void DeleteEntities(in EntityView[] entities)
39 | {
40 | for (var i = 0; i < entities.Length; i++)
41 | if (entities[i] != default)
42 | entities[i].Delete();
43 |
44 | NumberOfLivingEntities -= entities.Length;
45 | _world.BeginDeferred();
46 | }
47 |
48 | public EntityView[] PrepareSet(in int count) => new EntityView[count];
49 |
50 | public void CreateEntities(in EntityView[] entities)
51 | {
52 | for (var i = 0; i < entities.Length; i++)
53 | entities[i] = _world.Entity();
54 |
55 | NumberOfLivingEntities += entities.Length;
56 | }
57 |
58 | public void CreateEntities(in EntityView[] entities, in int poolId, in T1 c1) where T1 : struct, IComponent, IEcsComponent, Xeno.IComponent, Friflo.Engine.ECS.IComponent, FFS.Libraries.StaticEcs.IComponent
59 | {
60 | for (var i = 0; i < entities.Length; i++)
61 | entities[i] = _world.Entity().Set(c1);
62 |
63 | NumberOfLivingEntities += entities.Length;
64 | }
65 |
66 | public void CreateEntities(in EntityView[] entities, in int poolId, in T1 c1, in T2 c2) where T1 : struct, IComponent, IEcsComponent, Xeno.IComponent, Friflo.Engine.ECS.IComponent, FFS.Libraries.StaticEcs.IComponent where T2 : struct, IComponent, IEcsComponent, Xeno.IComponent, Friflo.Engine.ECS.IComponent, FFS.Libraries.StaticEcs.IComponent
67 | {
68 | for (var i = 0; i < entities.Length; i++)
69 | entities[i] = _world.Entity().Set(c1).Set(c2);
70 |
71 | NumberOfLivingEntities += entities.Length;
72 | }
73 |
74 | public void CreateEntities(in EntityView[] entities, in int poolId, in T1 c1, in T2 c2,
75 | in T3 c3) where T1 : struct, IComponent, IEcsComponent, Xeno.IComponent, Friflo.Engine.ECS.IComponent, FFS.Libraries.StaticEcs.IComponent where T2 : struct, IComponent, IEcsComponent, Xeno.IComponent, Friflo.Engine.ECS.IComponent, FFS.Libraries.StaticEcs.IComponent where T3 : struct, IComponent, IEcsComponent, Xeno.IComponent, Friflo.Engine.ECS.IComponent, FFS.Libraries.StaticEcs.IComponent
76 | {
77 | for (var i = 0; i < entities.Length; i++)
78 | entities[i] = _world.Entity().Set(c1).Set(c2).Set(c3);
79 |
80 | NumberOfLivingEntities += entities.Length;
81 | }
82 |
83 | public void CreateEntities(in EntityView[] entities, in int poolId, in T1 c1, in T2 c2,
84 | in T3 c3, in T4 c4) where T1 : struct, IComponent, IEcsComponent, Xeno.IComponent, Friflo.Engine.ECS.IComponent, FFS.Libraries.StaticEcs.IComponent where T2 : struct, IComponent, IEcsComponent, Xeno.IComponent, Friflo.Engine.ECS.IComponent, FFS.Libraries.StaticEcs.IComponent where T3 : struct, IComponent, IEcsComponent, Xeno.IComponent, Friflo.Engine.ECS.IComponent, FFS.Libraries.StaticEcs.IComponent where T4 : struct, IComponent, IEcsComponent, Xeno.IComponent, Friflo.Engine.ECS.IComponent, FFS.Libraries.StaticEcs.IComponent
85 | {
86 | for (var i = 0; i < entities.Length; i++)
87 | entities[i] = _world.Entity().Set(c1).Set(c2).Set(c3).Set(c4);
88 |
89 | NumberOfLivingEntities += entities.Length;
90 | }
91 |
92 | public void AddComponent(in EntityView[] entities, in int poolId, in T1 c1) where T1 : struct, IComponent, IEcsComponent, Xeno.IComponent, Friflo.Engine.ECS.IComponent, FFS.Libraries.StaticEcs.IComponent
93 | {
94 | for (var i = 0; i < entities.Length; i++)
95 | entities[i].Set(c1);
96 | }
97 |
98 | public void AddComponent(in EntityView[] entities, in int poolId, in T1 c1, in T2 c2) where T1 : struct, IComponent, IEcsComponent, Xeno.IComponent, Friflo.Engine.ECS.IComponent, FFS.Libraries.StaticEcs.IComponent where T2 : struct, IComponent, IEcsComponent, Xeno.IComponent, Friflo.Engine.ECS.IComponent, FFS.Libraries.StaticEcs.IComponent
99 | {
100 | for (var i = 0; i < entities.Length; i++)
101 | entities[i].Set(c1).Set(c2);
102 | }
103 |
104 | public void AddComponent]