├── .github └── workflows │ └── benchmark-ci.yml ├── .gitignore ├── .vscode ├── extensions.json └── settings.json ├── ECS.Benchmark.sln ├── LICENSE ├── README.md ├── docs ├── images │ └── Debugger-Watch.png └── query-components-bench.cpp ├── results └── mac-mini-m2 │ ├── all │ ├── BenchmarkRun-joined-2024-08-08-18-23-57-report-github.md │ ├── BenchmarkRun-joined-2024-08-13-09-55-57-report-github.md │ ├── BenchmarkRun-joined-2024-08-13-23-47-16-report-github.md │ └── BenchmarkRun-joined-2024-08-18-10-50-59-report-github.md │ └── custom │ ├── basic │ ├── BenchmarkRun-joined-2024-07-11-14-14-56-report-github.md │ ├── BenchmarkRun-joined-2024-07-12-19-18-13-report-github.md │ ├── BenchmarkRun-joined-2024-07-12-21-38-48-report-github.md │ ├── BenchmarkRun-joined-2024-07-13-10-00-18-report-github.md │ ├── BenchmarkRun-joined-2024-07-14-14-08-33-report-github.md │ ├── BenchmarkRun-joined-2024-07-15-09-52-50-report-github.md │ ├── BenchmarkRun-joined-2024-07-16-12-35-26-report-github.md │ ├── BenchmarkRun-joined-2024-07-17-17-41-11-report-github.md │ ├── BenchmarkRun-joined-2024-07-20-07-55-15-report-github.md │ ├── BenchmarkRun-joined-2024-07-20-21-39-39-report-github.md │ ├── BenchmarkRun-joined-2024-07-31-19-21-01-report-github.md │ └── BenchmarkRun-joined-2024-08-05-22-43-32-report-github.md │ ├── command-buffer │ ├── BenchmarkRun-joined-2024-07-13-09-53-45-report-github.md │ ├── BenchmarkRun-joined-2024-07-13-13-32-17-report-github.md │ ├── BenchmarkRun-joined-2024-07-14-14-02-29-report-github.md │ ├── BenchmarkRun-joined-2024-07-15-09-46-14-report-github.md │ ├── BenchmarkRun-joined-2024-07-16-12-35-26-report-github.md │ ├── BenchmarkRun-joined-2024-07-20-07-55-15-report-github.md │ └── BenchmarkRun-joined-2024-07-20-21-39-39-report-github.md │ ├── events │ ├── BenchmarkRun-joined-2024-07-15-14-24-58-report-github.md │ ├── BenchmarkRun-joined-2024-07-16-12-35-26-report-github.md │ ├── BenchmarkRun-joined-2024-07-20-07-55-15-report-github.md │ └── BenchmarkRun-joined-2024-07-20-21-39-39-report-github.md │ ├── friflo │ ├── BenchmarkRun-joined-2024-08-26-16-23-11-report-github_3.0.0-preview.13.md │ └── BenchmarkRun-joined-2024-08-26-17-47-15-report-github_3.0.0-preview.12.md │ ├── relations │ ├── BenchmarkRun-joined-2024-07-11-14-25-44-report-github.md │ ├── BenchmarkRun-joined-2024-07-12-19-20-54-report-github.md │ ├── BenchmarkRun-joined-2024-07-14-14-01-42-report-github.md │ ├── BenchmarkRun-joined-2024-07-15-09-45-19-report-github.md │ ├── BenchmarkRun-joined-2024-07-15-18-11-39-report-github.md │ ├── BenchmarkRun-joined-2024-07-16-12-35-26-report-github.md │ ├── BenchmarkRun-joined-2024-07-17-14-25-27-report-github.md │ ├── BenchmarkRun-joined-2024-07-20-07-55-15-report-github.md │ ├── BenchmarkRun-joined-2024-07-20-21-39-39-report-github.md │ └── BenchmarkRun-joined-2024-07-21-21-17-36-report-github.md │ └── search │ ├── BenchmarkRun-joined-2024-07-14-10-52-17-report-github.md │ ├── BenchmarkRun-joined-2024-07-16-12-35-26-report-github.md │ └── BenchmarkRun-joined-2024-07-20-21-39-39-report-github.md └── src ├── .editorconfig ├── Arch ├── AddRemoveComponents.cs ├── AddRemoveLinks.cs ├── AddRemoveRelations.cs ├── ChildEntitiesAddRemove.cs ├── CommandBufferAddRemove.cs ├── CreateBulk.cs ├── CreateEntity.cs ├── CreateWorld.cs ├── DeleteEntity.cs ├── GetSetComponents.cs ├── QueryComponents.cs ├── QueryFragmented.cs ├── Tests.cs ├── _BenchUtils.cs └── _Components.cs ├── Constants.cs ├── DefaultEcs ├── AddRemoveComponents.cs ├── CommandBufferAddRemove.cs ├── ComponentEvents.cs ├── CreateEntity.cs ├── CreateWorld.cs ├── DeleteEntity.cs ├── GetSetComponents.cs ├── QueryComponents.cs ├── QueryFragmented.cs ├── _BenchUtils.cs └── _Components.cs ├── Directory.Build.props ├── ECS.Benchmark.csproj ├── Fennecs ├── AddRemoveComponents.cs ├── AddRemoveLinks.cs ├── AddRemoveRelations.cs ├── ChildEntitiesAddRemove.cs ├── CreateBulk.cs ├── CreateEntity.cs ├── CreateWorld.cs ├── DeleteEntity.cs ├── GetSetComponents.cs ├── QueryComponents.cs ├── QueryFragmented.cs ├── Tests.cs ├── _BenchUtils.cs └── _Components.cs ├── Flecs.NET ├── AddRemoveComponents.cs ├── AddRemoveLinks.cs ├── AddRemoveRelations.cs ├── ChildEntitiesAddRemove.cs ├── CommandBufferAddRemove.cs ├── ComponentEvents.cs ├── CreateEntity.cs ├── CreateWorld.cs ├── DeleteEntity.cs ├── GetSetComponents.cs ├── QueryComponents.cs ├── QueryFragmented.cs ├── Tests.cs ├── _BenchUtils.cs └── _Components.cs ├── Frent ├── AddRemoveComponents.cs ├── CommandBufferAddRemove.cs ├── ComponentEvents.cs ├── CreateBulk.cs ├── CreateEntity.cs ├── CreateWorld.cs ├── DeleteEntity.cs ├── GetSetComponents.cs ├── QueryComponents.cs ├── QueryFragmented.cs ├── _BenchUtils.cs └── _Components.cs ├── Friflo.Engine.ECS ├── AddRemoveComponents.cs ├── AddRemoveLinks.cs ├── AddRemoveRelations.cs ├── ChildEntitiesAddRemove.cs ├── CommandBufferAddRemove.cs ├── ComponentEvents.cs ├── CreateBulk.cs ├── CreateEntity.cs ├── CreateWorld.cs ├── DeleteEntity.cs ├── GetSetComponents.cs ├── QueryComponents.cs ├── QueryFragmented.cs ├── SearchComponentField.cs ├── SearchRange.cs ├── Tests.cs ├── _BenchUtils.cs └── _Components.cs ├── Leopotam.EcsLite ├── AddRemoveComponents.cs ├── CreateEntity.cs ├── CreateWorld.cs ├── DeleteEntity.cs ├── GetSetComponents.cs ├── QueryComponents.cs ├── QueryFragmented.cs ├── _BenchUtils.cs └── _Components.cs ├── Myriad ├── AddRemoveComponents.cs ├── CommandBufferAddRemove.cs ├── CreateEntity.cs ├── CreateWorld.cs ├── DeleteEntity.cs ├── GetSetComponents.cs ├── QueryComponents.cs ├── QueryFragmented.cs ├── _BenchUtils.cs └── _Components.cs ├── Program.cs ├── Properties └── launchSettings.json ├── Scellecs.Morpeh ├── AddRemoveComponents.cs ├── CommandBufferAddRemove.cs ├── CreateEntity.cs ├── CreateWorld.cs ├── DeleteEntity.cs ├── GetSetComponents.cs ├── QueryComponents.cs ├── QueryFragmented.cs ├── _Access.cs ├── _BenchUtils.cs └── _Components.cs ├── TinyEcs ├── AddRemoveComponents.cs ├── AddRemoveLinks.cs ├── AddRemoveRelations.cs ├── ChildEntitiesAddRemove.cs ├── CommandBufferAddRemove.cs ├── ComponentEvents.cs ├── CreateEntity.cs ├── CreateWorld.cs ├── DeleteEntity.cs ├── GetSetComponents.cs ├── QueryComponents.cs ├── QueryFragmented.cs ├── Tests.cs ├── _BenchUtils.cs └── _Components.cs └── _Category ├── AddRemoveComponents.cs ├── AddRemoveLinks.cs ├── AddRemoveRelations.cs ├── ChildEntitiesAddRemove.cs ├── CommandBufferAddRemove.cs ├── ComponentEvents.cs ├── CreateEntity.cs ├── CreateWorld.cs ├── DeleteEntity.cs ├── GetSetComponents.cs ├── QueryComponents.cs └── Search.cs /.github/workflows/benchmark-ci.yml: -------------------------------------------------------------------------------- 1 | name: Benchmark-CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | timeout-minutes: 3 10 | steps: 11 | - uses: actions/checkout@v4 12 | 13 | - name: Setup .NET 8.0 14 | uses: actions/setup-dotnet@v4 15 | with: 16 | dotnet-version: 8.0.101 17 | 18 | - name: Restore dependencies 19 | working-directory: ./src 20 | run: dotnet restore 21 | 22 | - name: Tests 23 | working-directory: ./src 24 | run: dotnet test -c Release 25 | 26 | - name: Benchmark 27 | working-directory: ./src 28 | run: dotnet run -c Release --filter \* --job Dry --disableLogFile 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | bin/ 3 | obj/ 4 | .idea/ 5 | .vs/ 6 | 7 | Artifacts/ -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "streetsidesoftware.code-spell-checker", 4 | "EditorConfig.EditorConfig" 5 | ] 6 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.wordWrap": "off", 3 | "[markdown]": { 4 | "editor.wordWrap": "off", 5 | }, 6 | "files.exclude": { 7 | "**/obj": true, 8 | "**/bin": true, 9 | "**/.vs": true, 10 | "**/.idea": true, 11 | "**/*-report.csv": true, 12 | "**/*-report.html": true, 13 | "src/.editorconfig": true, 14 | "src/Directory.Build.props": true, 15 | "Artifacts/*.log": false, 16 | }, 17 | "cSpell.words": [ 18 | "Diagnoser", 19 | "Flecs", 20 | "Leopotam", 21 | "Morpeh", 22 | "Scellecs", 23 | "SIMD", 24 | "Sonoma", 25 | "Ullrich" 26 | ] 27 | } -------------------------------------------------------------------------------- /ECS.Benchmark.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ECS.Benchmark", "src\ECS.Benchmark.csproj", "{1AA26623-28C8-4EE1-851E-36D410A560B2}" 4 | EndProject 5 | Global 6 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 7 | Debug|Any CPU = Debug|Any CPU 8 | Release|Any CPU = Release|Any CPU 9 | EndGlobalSection 10 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 11 | {1AA26623-28C8-4EE1-851E-36D410A560B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 12 | {1AA26623-28C8-4EE1-851E-36D410A560B2}.Debug|Any CPU.Build.0 = Debug|Any CPU 13 | {1AA26623-28C8-4EE1-851E-36D410A560B2}.Release|Any CPU.ActiveCfg = Release|Any CPU 14 | {1AA26623-28C8-4EE1-851E-36D410A560B2}.Release|Any CPU.Build.0 = Release|Any CPU 15 | EndGlobalSection 16 | EndGlobal 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Ullrich Praetz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /docs/images/Debugger-Watch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/friflo/ECS.CSharp.Benchmark-common-use-cases/e9cacbf539503dcd111158ec6daa4a84a4db1ffa/docs/images/Debugger-Watch.png -------------------------------------------------------------------------------- /docs/query-components-bench.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // Windows > cl /O2 query-components-bench.cpp 5 | // macOS > g++ /O2 query-components-bench.cpp -o bench 6 | 7 | void execute(int& c1, int& c2, int& c3, int& c4, int& c5) 8 | { 9 | c1 = c2 + c3 + c4 + c5; 10 | } 11 | 12 | void benchmark(int* arr1, int* arr2, int* arr3, int* arr4, int* arr5, int count, int repeat) 13 | { 14 | for (int i = 0; i < repeat; i++) { 15 | for (int n = 0; n < count; n++) { 16 | execute(arr1[n], arr2[n], arr3[n], arr4[n], arr5[n]); 17 | // arr1[n] = arr2[n] + arr3[n] + arr4[n] + arr5[n]; 18 | } 19 | } 20 | } 21 | 22 | int main() 23 | { 24 | int count = 100000; 25 | int repeat = 10000; 26 | 27 | int* arr1 = new int[count]; 28 | int* arr2 = new int[count]; 29 | int* arr3 = new int[count]; 30 | int* arr4 = new int[count]; 31 | int* arr5 = new int[count]; 32 | 33 | std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now(); 34 | 35 | benchmark(arr1, arr2, arr3, arr4, arr5, count, repeat); 36 | 37 | std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now(); 38 | long duration = std::chrono::duration_cast (end - begin).count() / repeat; 39 | 40 | std::cout << "entities: " << count << " duration: " << duration << " ns"; 41 | 42 | int dummy = 0; 43 | for (int n = 0; n < count; n++) 44 | dummy += arr1[n] + arr2[n] + arr3[n] + arr4[n] + arr5[n]; 45 | std::cout << "dummy: " << dummy; 46 | } 47 | 48 | -------------------------------------------------------------------------------- /results/mac-mini-m2/custom/basic/BenchmarkRun-joined-2024-07-17-17-41-11-report-github.md: -------------------------------------------------------------------------------- 1 | ``` 2 | 3 | BenchmarkDotNet v0.13.12, macOS Sonoma 14.5 (23F79) [Darwin 23.5.0] 4 | Apple M2, 1 CPU, 8 logical and 8 physical cores 5 | .NET SDK 8.0.100 6 | [Host] : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 7 | Job-GFBCXZ : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 8 | 9 | Method=Run Job=Job-GFBCXZ InvocationCount=1 10 | IterationCount=Default LaunchCount=Default UnrollFactor=1 11 | WarmupCount=Default 12 | 13 | ``` 14 | | Namespace | Type | Mean | Ratio | Allocated | 15 | |------------------ |-------------------------- |------------:|-------:|-----------:| 16 | | Friflo.Engine.ECS | CreateEntityT1_Friflo | 394.6 μs | 1.00 | 3454080 B | 17 | | fennecs | CreateEntityT1_Fennecs | 1,059.3 μs | 2.68 | 6815576 B | 18 | | Flecs.NET | CreateEntityT1_FlecsNet | 1,332.2 μs | 3.36 | 736 B | 19 | | Leopotam.EcsLite | CreateEntityT1_Leopotam | 1,843.4 μs | 4.72 | 7321696 B | 20 | | TinyEcs | CreateEntityT1_TinyEcs | 6,511.4 μs | 16.53 | 10118352 B | 21 | | DefaultEcs | CreateEntityT1_DefaultEcs | 9,520.6 μs | 24.13 | 11591808 B | 22 | | Arch | CreateEntityT1_Arch | 10,326.8 μs | 26.15 | 3255576 B | 23 | | Scellecs.Morpeh | CreateEntityT1_Morpeh | 64,369.5 μs | 162.91 | 42301552 B | 24 | | | | | | | 25 | | Friflo.Engine.ECS | CreateEntityT3_Friflo | 468.7 μs | 1.00 | 4506960 B | 26 | | fennecs | CreateEntityT3_Fennecs | 974.7 μs | 2.02 | 7866864 B | 27 | | Flecs.NET | CreateEntityT3_FlecsNet | 1,465.3 μs | 3.06 | 736 B | 28 | | Arch | CreateEntityT3_Arch | 2,350.6 μs | 5.00 | 4043272 B | 29 | | Leopotam.EcsLite | CreateEntityT3_Leopotam | 3,156.3 μs | 6.54 | 11517736 B | 30 | | DefaultEcs | CreateEntityT3_DefaultEcs | 10,207.1 μs | 21.19 | 19984720 B | 31 | | TinyEcs | CreateEntityT3_TinyEcs | 23,419.3 μs | 48.50 | 23921880 B | 32 | | Scellecs.Morpeh | CreateEntityT3_Morpeh | 47,802.6 μs | 99.27 | 49285544 B | 33 | -------------------------------------------------------------------------------- /results/mac-mini-m2/custom/command-buffer/BenchmarkRun-joined-2024-07-13-09-53-45-report-github.md: -------------------------------------------------------------------------------- 1 | ``` 2 | 3 | BenchmarkDotNet v0.13.12, macOS Sonoma 14.5 (23F79) [Darwin 23.5.0] 4 | Apple M2, 1 CPU, 8 logical and 8 physical cores 5 | .NET SDK 8.0.100 6 | [Host] : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 7 | ShortRun : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 8 | 9 | Method=Run Job=ShortRun InvocationCount=Default 10 | IterationCount=3 LaunchCount=1 UnrollFactor=16 11 | WarmupCount=3 Alloc Ratio=NA 12 | 13 | ``` 14 | | Namespace | Type | Mean | Ratio | Allocated | 15 | |------------------ |------------------------------------ |----------:|------:|----------:| 16 | | Scellecs.Morpeh | CommandBufferAddRemoveT2_Morpeh | 4.944 μs | 0.59 | - | 17 | | Friflo.Engine.ECS | CommandBufferAddRemoveT2_Friflo | 8.328 μs | 1.00 | - | 18 | | DefaultEcs | CommandBufferAddRemoveT2_DefaultEcs | 16.025 μs | 1.92 | - | 19 | | Arch | CommandBufferAddRemoveT2_Arch | 46.894 μs | 5.63 | 4800 B | 20 | -------------------------------------------------------------------------------- /results/mac-mini-m2/custom/command-buffer/BenchmarkRun-joined-2024-07-13-13-32-17-report-github.md: -------------------------------------------------------------------------------- 1 | ``` 2 | 3 | BenchmarkDotNet v0.13.12, macOS Sonoma 14.5 (23F79) [Darwin 23.5.0] 4 | Apple M2, 1 CPU, 8 logical and 8 physical cores 5 | .NET SDK 8.0.100 6 | [Host] : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 7 | ShortRun : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 8 | 9 | Method=Run Job=ShortRun InvocationCount=Default 10 | IterationCount=3 LaunchCount=1 UnrollFactor=16 11 | WarmupCount=3 Alloc Ratio=NA 12 | 13 | ``` 14 | | Namespace | Type | Mean | Ratio | Allocated | 15 | |------------------ |------------------------------------ |----------:|------:|----------:| 16 | | Scellecs.Morpeh | CommandBufferAddRemoveT2_Morpeh | 4.954 μs | 0.58 | - | 17 | | Friflo.Engine.ECS | CommandBufferAddRemoveT2_Friflo | 8.470 μs | 1.00 | - | 18 | | Flecs.NET | CommandBufferAddRemoveT2_FlecsNet | 9.776 μs | 1.15 | - | 19 | | DefaultEcs | CommandBufferAddRemoveT2_DefaultEcs | 16.245 μs | 1.92 | - | 20 | | TinyEcs | CommandBufferAddRemoveT2_TinyEcs | 39.621 μs | 4.68 | 46928 B | 21 | | Arch | CommandBufferAddRemoveT2_Arch | 47.041 μs | 5.55 | 4800 B | 22 | -------------------------------------------------------------------------------- /results/mac-mini-m2/custom/command-buffer/BenchmarkRun-joined-2024-07-14-14-02-29-report-github.md: -------------------------------------------------------------------------------- 1 | ``` 2 | 3 | BenchmarkDotNet v0.13.12, macOS Sonoma 14.5 (23F79) [Darwin 23.5.0] 4 | Apple M2, 1 CPU, 8 logical and 8 physical cores 5 | .NET SDK 8.0.100 6 | [Host] : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 7 | ShortRun : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 8 | 9 | Method=Run Job=ShortRun InvocationCount=Default 10 | IterationCount=3 LaunchCount=1 UnrollFactor=16 11 | WarmupCount=3 Alloc Ratio=NA 12 | 13 | ``` 14 | | Namespace | Type | Mean | Ratio | Allocated | 15 | |------------------ |------------------------------------ |----------:|------:|----------:| 16 | | Scellecs.Morpeh | CommandBufferAddRemoveT2_Morpeh | 4.989 μs | 0.59 | - | 17 | | Friflo.Engine.ECS | CommandBufferAddRemoveT2_Friflo | 8.497 μs | 1.00 | - | 18 | | Flecs.NET | CommandBufferAddRemoveT2_FlecsNet | 9.811 μs | 1.15 | - | 19 | | DefaultEcs | CommandBufferAddRemoveT2_DefaultEcs | 16.520 μs | 1.94 | - | 20 | | TinyEcs | CommandBufferAddRemoveT2_TinyEcs | 40.641 μs | 4.78 | 46928 B | 21 | | Arch | CommandBufferAddRemoveT2_Arch | 51.464 μs | 6.06 | 4800 B | 22 | -------------------------------------------------------------------------------- /results/mac-mini-m2/custom/command-buffer/BenchmarkRun-joined-2024-07-15-09-46-14-report-github.md: -------------------------------------------------------------------------------- 1 | ``` 2 | 3 | BenchmarkDotNet v0.13.12, macOS Sonoma 14.5 (23F79) [Darwin 23.5.0] 4 | Apple M2, 1 CPU, 8 logical and 8 physical cores 5 | .NET SDK 8.0.100 6 | [Host] : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 7 | ShortRun : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 8 | 9 | Method=Run Job=ShortRun InvocationCount=Default 10 | IterationCount=3 LaunchCount=1 UnrollFactor=16 11 | WarmupCount=3 Alloc Ratio=NA 12 | 13 | ``` 14 | | Namespace | Type | Mean | Ratio | Allocated | 15 | |------------------ |------------------------------------ |----------:|------:|----------:| 16 | | Scellecs.Morpeh | CommandBufferAddRemoveT2_Morpeh | 4.959 μs | 0.58 | - | 17 | | Friflo.Engine.ECS | CommandBufferAddRemoveT2_Friflo | 8.624 μs | 1.00 | - | 18 | | Flecs.NET | CommandBufferAddRemoveT2_FlecsNet | 9.811 μs | 1.14 | - | 19 | | DefaultEcs | CommandBufferAddRemoveT2_DefaultEcs | 16.284 μs | 1.89 | - | 20 | | TinyEcs | CommandBufferAddRemoveT2_TinyEcs | 29.374 μs | 3.41 | 20800 B | 21 | | Arch | CommandBufferAddRemoveT2_Arch | 46.297 μs | 5.37 | 4800 B | 22 | -------------------------------------------------------------------------------- /results/mac-mini-m2/custom/command-buffer/BenchmarkRun-joined-2024-07-16-12-35-26-report-github.md: -------------------------------------------------------------------------------- 1 | ``` 2 | 3 | BenchmarkDotNet v0.13.12, macOS Sonoma 14.5 (23F79) [Darwin 23.5.0] 4 | Apple M2, 1 CPU, 8 logical and 8 physical cores 5 | .NET SDK 8.0.100 6 | [Host] : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 7 | DefaultJob : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 8 | Job-QCZYEK : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 9 | 10 | Method=Run IterationCount=Default LaunchCount=Default 11 | WarmupCount=Default 12 | 13 | ``` 14 | | Namespace | Type | RelationCount | Mean | Ratio | Allocated | 15 | |------------------ |------------------------------------ |-------------- |------------------:|---------:|------------:| 16 | | Scellecs.Morpeh | CommandBufferAddRemoveT2_Morpeh | ? | 5,064.27 ns | 0.58 | - | 17 | | Friflo.Engine.ECS | CommandBufferAddRemoveT2_Friflo | ? | 8,736.44 ns | 1.00 | - | 18 | | Flecs.NET | CommandBufferAddRemoveT2_FlecsNet | ? | 9,844.70 ns | 1.13 | - | 19 | | DefaultEcs | CommandBufferAddRemoveT2_DefaultEcs | ? | 16,674.18 ns | 1.91 | - | 20 | | TinyEcs | CommandBufferAddRemoveT2_TinyEcs | ? | 29,329.31 ns | 3.36 | 20800 B | 21 | | Arch | CommandBufferAddRemoveT2_Arch | ? | 48,425.98 ns | 5.54 | 4800 B | 22 | -------------------------------------------------------------------------------- /results/mac-mini-m2/custom/command-buffer/BenchmarkRun-joined-2024-07-20-07-55-15-report-github.md: -------------------------------------------------------------------------------- 1 | ``` 2 | 3 | BenchmarkDotNet v0.13.12, macOS Sonoma 14.5 (23F79) [Darwin 23.5.0] 4 | Apple M2, 1 CPU, 8 logical and 8 physical cores 5 | .NET SDK 8.0.100 6 | [Host] : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 7 | DefaultJob : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 8 | Job-UGKIQF : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 9 | 10 | Method=Run IterationCount=Default LaunchCount=Default 11 | WarmupCount=Default 12 | 13 | ``` 14 | | Namespace | Type | RelationCount | Mean | Ratio | Allocated | 15 | |------------------ |------------------------------------ |-------------- |------------------:|---------:|-----------:| 16 | | Scellecs.Morpeh | CommandBufferAddRemoveT2_Morpeh | ? | 5,002.902 ns | 0.59 | - | 17 | | Friflo.Engine.ECS | CommandBufferAddRemoveT2_Friflo | ? | 8,538.175 ns | 1.00 | - | 18 | | Flecs.NET | CommandBufferAddRemoveT2_FlecsNet | ? | 9,829.185 ns | 1.15 | - | 19 | | TinyEcs | CommandBufferAddRemoveT2_TinyEcs | ? | 13,081.939 ns | 1.53 | 4800 B | 20 | | DefaultEcs | CommandBufferAddRemoveT2_DefaultEcs | ? | 16,153.339 ns | 1.89 | - | 21 | | Arch | CommandBufferAddRemoveT2_Arch | ? | 46,801.663 ns | 5.48 | 4800 B | 22 | -------------------------------------------------------------------------------- /results/mac-mini-m2/custom/command-buffer/BenchmarkRun-joined-2024-07-20-21-39-39-report-github.md: -------------------------------------------------------------------------------- 1 | ``` 2 | 3 | BenchmarkDotNet v0.13.12, macOS Sonoma 14.5 (23F79) [Darwin 23.5.0] 4 | Apple M2, 1 CPU, 8 logical and 8 physical cores 5 | .NET SDK 8.0.100 6 | [Host] : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 7 | DefaultJob : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 8 | Job-HQFOUT : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 9 | 10 | Method=Run IterationCount=Default LaunchCount=Default 11 | WarmupCount=Default 12 | 13 | ``` 14 | | Namespace | Type | RelationCount | Mean | Ratio | Allocated | 15 | |------------------ |------------------------------------ |-------------- |------------------:|---------:|-----------:| 16 | | Scellecs.Morpeh | CommandBufferAddRemoveT2_Morpeh | ? | 5,121.168 ns | 0.60 | - | 17 | | Friflo.Engine.ECS | CommandBufferAddRemoveT2_Friflo | ? | 8,490.131 ns | 1.00 | - | 18 | | TinyEcs | CommandBufferAddRemoveT2_TinyEcs | ? | 12,959.426 ns | 1.53 | 4800 B | 19 | | Flecs.NET | CommandBufferAddRemoveT2_FlecsNet | ? | 14,399.755 ns | 1.70 | - | 20 | | DefaultEcs | CommandBufferAddRemoveT2_DefaultEcs | ? | 16,476.300 ns | 1.94 | - | 21 | | Arch | CommandBufferAddRemoveT2_Arch | ? | 48,222.287 ns | 5.68 | 4800 B | 22 | -------------------------------------------------------------------------------- /results/mac-mini-m2/custom/events/BenchmarkRun-joined-2024-07-15-14-24-58-report-github.md: -------------------------------------------------------------------------------- 1 | ``` 2 | 3 | BenchmarkDotNet v0.13.12, macOS Sonoma 14.5 (23F79) [Darwin 23.5.0] 4 | Apple M2, 1 CPU, 8 logical and 8 physical cores 5 | .NET SDK 8.0.100 6 | [Host] : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 7 | ShortRun : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 8 | 9 | Method=Run Job=ShortRun InvocationCount=Default 10 | IterationCount=3 LaunchCount=1 UnrollFactor=16 11 | WarmupCount=3 Alloc Ratio=NA 12 | 13 | ``` 14 | | Namespace | Type | Mean | Ratio | Allocated | 15 | |------------------ |--------------------------- |----------:|------:|----------:| 16 | | DefaultEcs | ComponentEvents_DefaultEcs | 2.567 μs | 0.34 | - | 17 | | Friflo.Engine.ECS | ComponentEvents_Friflo | 7.577 μs | 1.00 | - | 18 | | Flecs.NET | ComponentEvents_FlecsNet | 10.378 μs | 1.37 | - | 19 | | TinyEcs | ComponentEvents_TinyEcs | 13.834 μs | 1.83 | 6400 B | 20 | -------------------------------------------------------------------------------- /results/mac-mini-m2/custom/events/BenchmarkRun-joined-2024-07-16-12-35-26-report-github.md: -------------------------------------------------------------------------------- 1 | ``` 2 | 3 | BenchmarkDotNet v0.13.12, macOS Sonoma 14.5 (23F79) [Darwin 23.5.0] 4 | Apple M2, 1 CPU, 8 logical and 8 physical cores 5 | .NET SDK 8.0.100 6 | [Host] : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 7 | DefaultJob : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 8 | Job-QCZYEK : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 9 | 10 | Method=Run IterationCount=Default LaunchCount=Default 11 | WarmupCount=Default 12 | 13 | ``` 14 | | Namespace | Type | RelationCount | Mean | Ratio | Allocated | 15 | |------------------ |------------------------------------ |-------------- |------------------:|---------:|------------:| 16 | | DefaultEcs | ComponentEvents_DefaultEcs | ? | 2,602.10 ns | 0.35 | - | 17 | | Friflo.Engine.ECS | ComponentEvents_Friflo | ? | 7,480.85 ns | 1.00 | - | 18 | | Flecs.NET | ComponentEvents_FlecsNet | ? | 10,419.53 ns | 1.39 | - | 19 | | TinyEcs | ComponentEvents_TinyEcs | ? | 13,781.00 ns | 1.84 | 6400 B | 20 | -------------------------------------------------------------------------------- /results/mac-mini-m2/custom/events/BenchmarkRun-joined-2024-07-20-07-55-15-report-github.md: -------------------------------------------------------------------------------- 1 | ``` 2 | 3 | BenchmarkDotNet v0.13.12, macOS Sonoma 14.5 (23F79) [Darwin 23.5.0] 4 | Apple M2, 1 CPU, 8 logical and 8 physical cores 5 | .NET SDK 8.0.100 6 | [Host] : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 7 | DefaultJob : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 8 | Job-UGKIQF : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 9 | 10 | Method=Run IterationCount=Default LaunchCount=Default 11 | WarmupCount=Default 12 | 13 | ``` 14 | | Namespace | Type | RelationCount | Mean | Ratio | Allocated | 15 | |------------------ |------------------------------------ |-------------- |------------------:|---------:|-----------:| 16 | | DefaultEcs | ComponentEvents_DefaultEcs | ? | 2,582.976 ns | 0.33 | - | 17 | | TinyEcs | ComponentEvents_TinyEcs | ? | 4,422.520 ns | 0.57 | - | 18 | | Friflo.Engine.ECS | ComponentEvents_Friflo | ? | 7,749.574 ns | 1.00 | - | 19 | | Flecs.NET | ComponentEvents_FlecsNet | ? | 10,474.904 ns | 1.35 | - | 20 | -------------------------------------------------------------------------------- /results/mac-mini-m2/custom/events/BenchmarkRun-joined-2024-07-20-21-39-39-report-github.md: -------------------------------------------------------------------------------- 1 | ``` 2 | 3 | BenchmarkDotNet v0.13.12, macOS Sonoma 14.5 (23F79) [Darwin 23.5.0] 4 | Apple M2, 1 CPU, 8 logical and 8 physical cores 5 | .NET SDK 8.0.100 6 | [Host] : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 7 | DefaultJob : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 8 | Job-HQFOUT : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 9 | 10 | Method=Run IterationCount=Default LaunchCount=Default 11 | WarmupCount=Default 12 | 13 | ``` 14 | | Namespace | Type | RelationCount | Mean | Ratio | Allocated | 15 | |------------------ |------------------------------------ |-------------- |------------------:|---------:|-----------:| 16 | | DefaultEcs | ComponentEvents_DefaultEcs | ? | 2,548.685 ns | 0.37 | - | 17 | | TinyEcs | ComponentEvents_TinyEcs | ? | 4,414.266 ns | 0.64 | - | 18 | | Friflo.Engine.ECS | ComponentEvents_Friflo | ? | 6,935.659 ns | 1.00 | - | 19 | | Flecs.NET | ComponentEvents_FlecsNet | ? | 11,421.612 ns | 1.65 | - | 20 | -------------------------------------------------------------------------------- /results/mac-mini-m2/custom/relations/BenchmarkRun-joined-2024-07-11-14-25-44-report-github.md: -------------------------------------------------------------------------------- 1 | ``` 2 | 3 | BenchmarkDotNet v0.13.12, macOS Sonoma 14.5 (23F79) [Darwin 23.5.0] 4 | Apple M2, 1 CPU, 8 logical and 8 physical cores 5 | .NET SDK 8.0.100 6 | [Host] : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 7 | ShortRun : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 8 | 9 | Method=Run Job=ShortRun InvocationCount=Default 10 | IterationCount=3 LaunchCount=1 UnrollFactor=16 11 | WarmupCount=3 12 | 13 | ``` 14 | | Namespace | Type | TargetCount | Mean | Ratio | Allocated | 15 | |------------------ |----------------------- |------------ |-----------:|------:|------------:| 16 | | Friflo.Engine.ECS | AddRemoveLinks_Friflo | 1 | 51 μs | 1.00 | - | 17 | | fennecs | AddRemoveLinks_Fennecs | 1 | 938 μs | 18.39 | 1800001 B | 18 | | | | | | | | 19 | | Friflo.Engine.ECS | AddRemoveLinks_Friflo | 100 | 11,890 μs | 1.00 | 12 B | 20 | | fennecs | AddRemoveLinks_Fennecs | 100 | 718,838 μs | 60.46 | 931248736 B | 21 | -------------------------------------------------------------------------------- /results/mac-mini-m2/custom/relations/BenchmarkRun-joined-2024-07-12-19-20-54-report-github.md: -------------------------------------------------------------------------------- 1 | ``` 2 | 3 | BenchmarkDotNet v0.13.12, macOS Sonoma 14.5 (23F79) [Darwin 23.5.0] 4 | Apple M2, 1 CPU, 8 logical and 8 physical cores 5 | .NET SDK 8.0.100 6 | [Host] : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 7 | ShortRun : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 8 | 9 | Method=Run Job=ShortRun InvocationCount=Default 10 | IterationCount=3 LaunchCount=1 UnrollFactor=16 11 | WarmupCount=3 12 | 13 | ``` 14 | | Namespace | Type | TargetCount | Mean | Ratio | Allocated | 15 | |------------------ |------------------------ |------------ |--------------:|------:|-----------:| 16 | | Friflo.Engine.ECS | AddRemoveLinks_Friflo | 1 | 5.138 μs | 1.00 | - | 17 | | Flecs.NET | AddRemoveLinks_FlecsNet | 1 | 9.992 μs | 1.94 | - | 18 | | TinyEcs | AddRemoveLinks_TinyEcs | 1 | 25.800 μs | 5.02 | 22400 B | 19 | | fennecs | AddRemoveLinks_Fennecs | 1 | 93.045 μs | 18.11 | 180000 B | 20 | | | | | | | | 21 | | Flecs.NET | AddRemoveLinks_FlecsNet | 100 | 936.396 μs | 0.80 | 1 B | 22 | | Friflo.Engine.ECS | AddRemoveLinks_Friflo | 100 | 1,171.335 μs | 1.00 | 1 B | 23 | | TinyEcs | AddRemoveLinks_TinyEcs | 100 | 8,680.643 μs | 7.41 | 18080012 B | 24 | | fennecs | AddRemoveLinks_Fennecs | 100 | 71,750.446 μs | 61.26 | 93124905 B | 25 | -------------------------------------------------------------------------------- /results/mac-mini-m2/custom/relations/BenchmarkRun-joined-2024-07-14-14-01-42-report-github.md: -------------------------------------------------------------------------------- 1 | ``` 2 | 3 | BenchmarkDotNet v0.13.12, macOS Sonoma 14.5 (23F79) [Darwin 23.5.0] 4 | Apple M2, 1 CPU, 8 logical and 8 physical cores 5 | .NET SDK 8.0.100 6 | [Host] : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 7 | ShortRun : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 8 | 9 | Method=Run Job=ShortRun InvocationCount=Default 10 | IterationCount=3 LaunchCount=1 UnrollFactor=16 11 | WarmupCount=3 12 | 13 | ``` 14 | | Namespace | Type | TargetCount | Mean | Ratio | Allocated | 15 | |------------------ |------------------------ |------------ |--------------:|------:|-----------:| 16 | | Friflo.Engine.ECS | AddRemoveLinks_Friflo | 1 | 5.217 μs | 1.00 | - | 17 | | Flecs.NET | AddRemoveLinks_FlecsNet | 1 | 10.345 μs | 1.98 | - | 18 | | TinyEcs | AddRemoveLinks_TinyEcs | 1 | 25.764 μs | 4.94 | 22400 B | 19 | | fennecs | AddRemoveLinks_Fennecs | 1 | 90.356 μs | 17.32 | 180000 B | 20 | | | | | | | | 21 | | Flecs.NET | AddRemoveLinks_FlecsNet | 100 | 958.783 μs | 0.81 | 1 B | 22 | | Friflo.Engine.ECS | AddRemoveLinks_Friflo | 100 | 1,191.541 μs | 1.00 | 1 B | 23 | | TinyEcs | AddRemoveLinks_TinyEcs | 100 | 8,471.446 μs | 7.11 | 18080012 B | 24 | | fennecs | AddRemoveLinks_Fennecs | 100 | 78,162.343 μs | 65.66 | 93124905 B | 25 | -------------------------------------------------------------------------------- /results/mac-mini-m2/custom/relations/BenchmarkRun-joined-2024-07-15-09-45-19-report-github.md: -------------------------------------------------------------------------------- 1 | ``` 2 | 3 | BenchmarkDotNet v0.13.12, macOS Sonoma 14.5 (23F79) [Darwin 23.5.0] 4 | Apple M2, 1 CPU, 8 logical and 8 physical cores 5 | .NET SDK 8.0.100 6 | [Host] : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 7 | ShortRun : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 8 | 9 | Method=Run Job=ShortRun InvocationCount=Default 10 | IterationCount=3 LaunchCount=1 UnrollFactor=16 11 | WarmupCount=3 12 | 13 | ``` 14 | | Namespace | Type | TargetCount | Mean | Ratio | Allocated | 15 | |------------------ |------------------------ |------------ |--------------:|------:|-----------:| 16 | | Friflo.Engine.ECS | AddRemoveLinks_Friflo | 1 | 5.123 μs | 1.00 | - | 17 | | Flecs.NET | AddRemoveLinks_FlecsNet | 1 | 10.292 μs | 2.01 | - | 18 | | TinyEcs | AddRemoveLinks_TinyEcs | 1 | 28.581 μs | 5.58 | 22400 B | 19 | | fennecs | AddRemoveLinks_Fennecs | 1 | 92.580 μs | 18.07 | 180000 B | 20 | | | | | | | | 21 | | Flecs.NET | AddRemoveLinks_FlecsNet | 100 | 947.199 μs | 0.82 | 1 B | 22 | | Friflo.Engine.ECS | AddRemoveLinks_Friflo | 100 | 1,158.830 μs | 1.00 | 1 B | 23 | | TinyEcs | AddRemoveLinks_TinyEcs | 100 | 8,776.224 μs | 7.57 | 18080012 B | 24 | | fennecs | AddRemoveLinks_Fennecs | 100 | 70,384.219 μs | 60.74 | 93124892 B | 25 | -------------------------------------------------------------------------------- /results/mac-mini-m2/custom/relations/BenchmarkRun-joined-2024-07-15-18-11-39-report-github.md: -------------------------------------------------------------------------------- 1 | ``` 2 | 3 | BenchmarkDotNet v0.13.12, macOS Sonoma 14.5 (23F79) [Darwin 23.5.0] 4 | Apple M2, 1 CPU, 8 logical and 8 physical cores 5 | .NET SDK 8.0.100 6 | [Host] : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 7 | ShortRun : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 8 | 9 | Method=Run Job=ShortRun InvocationCount=Default 10 | IterationCount=3 LaunchCount=1 UnrollFactor=16 11 | WarmupCount=3 12 | 13 | ``` 14 | | Namespace | Type | RelationCount | Mean | Ratio | Allocated | 15 | |------------------ |---------------------------- |-------------- |--------------:|------:|-----------:| 16 | | Friflo.Engine.ECS | AddRemoveLinks_Friflo | 1 | 5.121 μs | 1.00 | - | 17 | | Flecs.NET | AddRemoveLinks_FlecsNet | 1 | 10.288 μs | 2.01 | - | 18 | | TinyEcs | AddRemoveLinks_TinyEcs | 1 | 28.474 μs | 5.56 | 22400 B | 19 | | fennecs | AddRemoveLinks_Fennecs | 1 | 92.031 μs | 17.97 | 180000 B | 20 | | | | | | | | 21 | | Flecs.NET | AddRemoveLinks_FlecsNet | 100 | 951.067 μs | 0.80 | 1 B | 22 | | Friflo.Engine.ECS | AddRemoveLinks_Friflo | 100 | 1,196.263 μs | 1.00 | 1 B | 23 | | TinyEcs | AddRemoveLinks_TinyEcs | 100 | 8,942.207 μs | 7.48 | 18080012 B | 24 | | fennecs | AddRemoveLinks_Fennecs | 100 | 71,197.121 μs | 59.52 | 93124905 B | 25 | | | | | | | | 26 | | Friflo.Engine.ECS | AddRemoveRelations_Friflo | 1 | 3.086 μs | 1.00 | - | 27 | | Flecs.NET | AddRemoveRelations_FlecsNet | 1 | 4.912 μs | 1.59 | - | 28 | | TinyEcs | AddRemoveRelations_TinyEcs | 1 | 32.772 μs | 10.62 | 53600 B | 29 | | fennecs | AddRemoveRelations_Fennecs | 1 | 39.935 μs | 12.94 | 86400 B | 30 | | | | | | | | 31 | | Friflo.Engine.ECS | AddRemoveRelations_Friflo | 10 | 49.105 μs | 1.00 | - | 32 | | Flecs.NET | AddRemoveRelations_FlecsNet | 10 | 107.566 μs | 2.19 | - | 33 | | TinyEcs | AddRemoveRelations_TinyEcs | 10 | 448.744 μs | 9.14 | 694400 B | 34 | | fennecs | AddRemoveRelations_Fennecs | 10 | 979.254 μs | 19.94 | 1704801 B | 35 | -------------------------------------------------------------------------------- /results/mac-mini-m2/custom/relations/BenchmarkRun-joined-2024-07-16-12-35-26-report-github.md: -------------------------------------------------------------------------------- 1 | ``` 2 | 3 | BenchmarkDotNet v0.13.12, macOS Sonoma 14.5 (23F79) [Darwin 23.5.0] 4 | Apple M2, 1 CPU, 8 logical and 8 physical cores 5 | .NET SDK 8.0.100 6 | [Host] : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 7 | DefaultJob : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 8 | Job-QCZYEK : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 9 | 10 | Method=Run IterationCount=Default LaunchCount=Default 11 | WarmupCount=Default 12 | 13 | ``` 14 | | Namespace | Type | RelationCount | Mean | Ratio | Allocated | 15 | |------------------ |------------------------------------ |-------------- |------------------:|---------:|------------:| 16 | | Friflo.Engine.ECS | AddRemoveLinks_Friflo | 1 | 5,079.46 ns | 1.00 | - | 17 | | Flecs.NET | AddRemoveLinks_FlecsNet | 1 | 10,547.73 ns | 2.08 | - | 18 | | TinyEcs | AddRemoveLinks_TinyEcs | 1 | 29,066.88 ns | 5.72 | 22400 B | 19 | | fennecs | AddRemoveLinks_Fennecs | 1 | 94,018.67 ns | 18.51 | 180000 B | 20 | | | | | | | | 21 | | Flecs.NET | AddRemoveLinks_FlecsNet | 100 | 946,965.33 ns | 0.81 | 1 B | 22 | | Friflo.Engine.ECS | AddRemoveLinks_Friflo | 100 | 1,174,364.42 ns | 1.00 | 1 B | 23 | | TinyEcs | AddRemoveLinks_TinyEcs | 100 | 9,017,758.64 ns | 7.68 | 18080012 B | 24 | | fennecs | AddRemoveLinks_Fennecs | 100 | 71,485,885.42 ns | 60.87 | 93124892 B | 25 | | | | | | | | 26 | | Friflo.Engine.ECS | AddRemoveRelations_Friflo | 1 | 3,083.93 ns | 1.00 | - | 27 | | Flecs.NET | AddRemoveRelations_FlecsNet | 1 | 4,894.79 ns | 1.59 | - | 28 | | TinyEcs | AddRemoveRelations_TinyEcs | 1 | 32,151.28 ns | 10.43 | 53600 B | 29 | | fennecs | AddRemoveRelations_Fennecs | 1 | 40,777.41 ns | 13.22 | 86400 B | 30 | | | | | | | | 31 | | Friflo.Engine.ECS | AddRemoveRelations_Friflo | 10 | 48,452.44 ns | 1.00 | - | 32 | | Flecs.NET | AddRemoveRelations_FlecsNet | 10 | 106,787.94 ns | 2.20 | - | 33 | | TinyEcs | AddRemoveRelations_TinyEcs | 10 | 445,950.23 ns | 9.20 | 694400 B | 34 | | fennecs | AddRemoveRelations_Fennecs | 10 | 977,793.31 ns | 20.18 | 1704801 B | 35 | -------------------------------------------------------------------------------- /results/mac-mini-m2/custom/search/BenchmarkRun-joined-2024-07-14-10-52-17-report-github.md: -------------------------------------------------------------------------------- 1 | ``` 2 | 3 | BenchmarkDotNet v0.13.12, macOS Sonoma 14.5 (23F79) [Darwin 23.5.0] 4 | Apple M2, 1 CPU, 8 logical and 8 physical cores 5 | .NET SDK 8.0.100 6 | [Host] : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 7 | ShortRun : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 8 | 9 | Method=Run Job=ShortRun InvocationCount=Default 10 | IterationCount=3 LaunchCount=1 UnrollFactor=16 11 | WarmupCount=3 12 | 13 | ``` 14 | | Type | Mean | Ratio | Allocated | 15 | |---------------------------- |-------------:|------:|----------:| 16 | | SearchComponentField_Friflo | 4.714 μs | 1.00 | - | 17 | | | | | | 18 | | SearchRange_Friflo | 1,328.126 μs | 1.00 | 560001 B | 19 | -------------------------------------------------------------------------------- /results/mac-mini-m2/custom/search/BenchmarkRun-joined-2024-07-16-12-35-26-report-github.md: -------------------------------------------------------------------------------- 1 | ``` 2 | 3 | BenchmarkDotNet v0.13.12, macOS Sonoma 14.5 (23F79) [Darwin 23.5.0] 4 | Apple M2, 1 CPU, 8 logical and 8 physical cores 5 | .NET SDK 8.0.100 6 | [Host] : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 7 | DefaultJob : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 8 | Job-QCZYEK : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 9 | 10 | Method=Run IterationCount=Default LaunchCount=Default 11 | WarmupCount=Default 12 | 13 | ``` 14 | | Namespace | Type | RelationCount | Mean | Ratio | Allocated | 15 | |------------------ |------------------------------------ |-------------- |------------------:|---------:|------------:| 16 | | Friflo.Engine.ECS | SearchComponentField_Friflo | ? | 4,875.02 ns | 1.00 | - | 17 | | | | | | | | 18 | | Friflo.Engine.ECS | SearchRange_Friflo | ? | 1,343,502.49 ns | 1.00 | 560001 B | 19 | -------------------------------------------------------------------------------- /results/mac-mini-m2/custom/search/BenchmarkRun-joined-2024-07-20-21-39-39-report-github.md: -------------------------------------------------------------------------------- 1 | ``` 2 | 3 | BenchmarkDotNet v0.13.12, macOS Sonoma 14.5 (23F79) [Darwin 23.5.0] 4 | Apple M2, 1 CPU, 8 logical and 8 physical cores 5 | .NET SDK 8.0.100 6 | [Host] : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 7 | DefaultJob : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 8 | Job-HQFOUT : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD 9 | 10 | Method=Run IterationCount=Default LaunchCount=Default 11 | WarmupCount=Default 12 | 13 | ``` 14 | | Namespace | Type | RelationCount | Mean | Ratio | Allocated | 15 | |------------------ |------------------------------------ |-------------- |------------------:|---------:|-----------:| 16 | | Friflo.Engine.ECS | SearchComponentField_Friflo | ? | 4,760.410 ns | 1.00 | - | 17 | | | | | | | | 18 | | Friflo.Engine.ECS | SearchRange_Friflo | ? | 1,472,963.891 ns | 1.00 | 560001 B | 19 | -------------------------------------------------------------------------------- /src/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 4 7 | trim_trailing_whitespace = true 8 | 9 | [*.cs] 10 | resharper_unused_auto_property_accessor_global_highlighting=none -------------------------------------------------------------------------------- /src/Arch/AddRemoveComponents.cs: -------------------------------------------------------------------------------- 1 | using Arch.Core; 2 | using BenchmarkDotNet.Attributes; 3 | 4 | namespace Arch; 5 | 6 | // ReSharper disable once InconsistentNaming 7 | public class AddRemoveComponents_Arch : AddRemoveComponents 8 | { 9 | private World world; 10 | private Entity[] entities; 11 | 12 | [GlobalSetup] 13 | public void Setup() 14 | { 15 | world = World.Create(); 16 | entities = world.CreateEntities(Entities); 17 | } 18 | 19 | [GlobalCleanup] 20 | public void Shutdown() 21 | { 22 | World.Destroy(world); 23 | } 24 | 25 | protected override void Run1Component() 26 | { 27 | foreach (var entity in entities) { 28 | world.Add(entity); 29 | } 30 | foreach (var entity in entities) { 31 | world.Remove(entity); 32 | } 33 | } 34 | 35 | protected override void Run5Components() 36 | { 37 | foreach (var entity in entities) { 38 | world.Add(entity, new Component1(), new Component2(), new Component3(), new Component4(), new Component5()); 39 | } 40 | foreach (var entity in entities) { 41 | world.Remove(entity); 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /src/Arch/AddRemoveLinks.cs: -------------------------------------------------------------------------------- 1 | using Arch.Core; 2 | using Arch.Relationships; 3 | using BenchmarkDotNet.Attributes; 4 | 5 | namespace Arch; 6 | 7 | // ReSharper disable once InconsistentNaming 8 | public class AddRemoveLinks_Arch : AddRemoveLinks 9 | { 10 | private World world; 11 | private Entity[] entities; 12 | private Entity[] targets; 13 | 14 | [GlobalSetup] 15 | public void Setup() 16 | { 17 | world = World.Create(); 18 | entities = world.CreateEntities(Entities).AddComponents(); 19 | targets = world.CreateEntities(Relations).AddComponents(); 20 | } 21 | 22 | [GlobalCleanup] 23 | public void Shutdown() 24 | { 25 | World.Destroy(world); 26 | } 27 | 28 | [Benchmark] 29 | public override void Run() 30 | { 31 | foreach (var entity in entities) 32 | { 33 | for (int n = 0; n < Relations; n++) { 34 | entity.AddRelationship(targets[n], new LinkRelation(n)); 35 | } 36 | foreach (var relation in targets) { 37 | entity.RemoveRelationship(relation); 38 | } 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /src/Arch/AddRemoveRelations.cs: -------------------------------------------------------------------------------- 1 | using Arch.Core; 2 | using Arch.Relationships; 3 | using BenchmarkDotNet.Attributes; 4 | 5 | namespace Arch; 6 | 7 | // ReSharper disable once InconsistentNaming 8 | public class AddRemoveRelations_Arch : AddRemoveRelations 9 | { 10 | private World world; 11 | private Entity[] entities; 12 | private Entity[] targets; 13 | 14 | [GlobalSetup] 15 | public void Setup() 16 | { 17 | world = World.Create(); 18 | entities = world.CreateEntities(Entities).AddComponents(); 19 | targets = new Entity[Relations]; 20 | } 21 | 22 | [GlobalCleanup] 23 | public void Shutdown() 24 | { 25 | World.Destroy(world); 26 | } 27 | 28 | protected override void AddRemove1Relation() 29 | { 30 | var target = world.Create(); 31 | foreach (var entity in entities) 32 | { 33 | entity.AddRelationship(target, new LinkRelation(1337)); 34 | entity.RemoveRelationship(target); 35 | } 36 | world.Destroy(target); 37 | } 38 | 39 | protected override void AddRemove10Relations() 40 | { 41 | world.CreateEntities(Relations, targets); 42 | foreach (var entity in entities) 43 | { 44 | for (int n = 0; n < Relations; n++) { 45 | entity.AddRelationship(targets[n], new LinkRelation(1337)); 46 | } 47 | foreach (var relation in targets) { 48 | entity.RemoveRelationship(relation); 49 | } 50 | } 51 | foreach (var target in targets) { 52 | world.Destroy(target); 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /src/Arch/ChildEntitiesAddRemove.cs: -------------------------------------------------------------------------------- 1 | using Arch.Core; 2 | using Arch.Relationships; 3 | using BenchmarkDotNet.Attributes; 4 | 5 | namespace Arch; 6 | 7 | // ReSharper disable once InconsistentNaming 8 | public class ChildEntitiesAddRemove_Arch : ChildEntitiesAddRemove 9 | { 10 | private World world; 11 | private Entity[] parents; 12 | private Entity[][] children; 13 | private int entityCount; 14 | private int childCount; 15 | 16 | [GlobalSetup] 17 | public void Setup() { 18 | world = World.Create(); 19 | entityCount = Entities; 20 | childCount = Constants.ChildCount; 21 | parents = world.CreateEntities(entityCount).AddComponents(); 22 | children = new Entity[entityCount][]; 23 | for (int n = 0; n < entityCount; n++) { 24 | children[n] = world.CreateEntities(childCount).AddComponents(); 25 | } 26 | } 27 | 28 | [GlobalCleanup] 29 | public void Shutdown() { 30 | world.Dispose(); 31 | } 32 | 33 | /// according to example: https://github.com/genaray/Arch.Extended/wiki/Relationships#code-sample 34 | [Benchmark] 35 | public override void Run() 36 | { 37 | for (int n = 0; n < entityCount; n++) { 38 | for (int child = 0; child < childCount; child++) { 39 | parents[n].AddRelationship(children[n][child]); 40 | } 41 | } 42 | // Assert.AreEqual(entityCount * childCount, CountChildren()); 43 | for (int n = 0; n < entityCount; n++) { 44 | for (int child = childCount - 1; child >= 0; child--) { 45 | parents[n].RemoveRelationship(children[n][child]); 46 | } 47 | } 48 | } 49 | 50 | // method only for verification 51 | private int CountChildren() 52 | { 53 | int count = 0; 54 | for (int n = 0; n < entityCount; n++) { 55 | for (int child = 0; child < childCount; child++) { 56 | var hasParent = parents[n].HasRelationship(children[n][child]); 57 | count += hasParent ? 1 : 0; 58 | } 59 | } 60 | return count; 61 | } 62 | } -------------------------------------------------------------------------------- /src/Arch/CommandBufferAddRemove.cs: -------------------------------------------------------------------------------- 1 | using Arch.Buffer; 2 | using Arch.Core; 3 | using BenchmarkDotNet.Attributes; 4 | 5 | namespace Arch; 6 | 7 | // ReSharper disable once InconsistentNaming 8 | public class CommandBufferAddRemove_Arch : CommandBufferAddRemove 9 | { 10 | private World world; 11 | private Entity[] entities; 12 | private CommandBuffer commandBuffer; 13 | 14 | [GlobalSetup] 15 | public void Setup() 16 | { 17 | world = World.Create(); 18 | entities = world.CreateEntities(Entities); 19 | commandBuffer = new CommandBuffer(Entities); 20 | } 21 | 22 | [GlobalCleanup] 23 | public void Shutdown() 24 | { 25 | commandBuffer.Dispose(); 26 | World.Destroy(world); 27 | } 28 | 29 | [Benchmark] 30 | public override void Run() 31 | { 32 | var cb = commandBuffer; 33 | foreach (var entity in entities) { 34 | cb.Add(entity, new Component1()); 35 | cb.Add(entity, new Component2()); 36 | } 37 | cb.Playback(world, false); // Apply changes 1 38 | 39 | foreach (var entity in entities) { 40 | cb.Remove(entity); 41 | cb.Remove(entity); 42 | } 43 | cb.Playback(world, false); // Apply changes 2 44 | } 45 | } -------------------------------------------------------------------------------- /src/Arch/CreateBulk.cs: -------------------------------------------------------------------------------- 1 | using Arch.Core; 2 | using BenchmarkDotNet.Attributes; 3 | 4 | namespace Arch; 5 | 6 | // ReSharper disable once InconsistentNaming 7 | public class CreateBulk_Arch : CreateBulk 8 | { 9 | private World world; 10 | private static readonly Signature ComponentTypes1 = new(typeof(Component1)); 11 | private static readonly Signature ComponentTypes3 = new(typeof(Component1),typeof(Component2),typeof(Component3)); 12 | 13 | [IterationSetup] 14 | public void Setup() 15 | { 16 | world = World.Create(); 17 | } 18 | 19 | [IterationCleanup] 20 | public void Shutdown() 21 | { 22 | World.Destroy(world); 23 | } 24 | 25 | protected override void CreateEntity1Component() 26 | { 27 | world.Reserve(ComponentTypes1, Entities); 28 | for (int n = 0; n < Entities; n++) 29 | { 30 | world.Create(n); 31 | } 32 | } 33 | 34 | protected override void CreateEntity3Components() 35 | { 36 | world.Reserve(ComponentTypes3, Entities); 37 | for (int n = 0; n < Entities; n++) 38 | { 39 | world.Create(n,n,n); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /src/Arch/CreateEntity.cs: -------------------------------------------------------------------------------- 1 | using Arch.Core; 2 | using BenchmarkDotNet.Attributes; 3 | 4 | namespace Arch; 5 | 6 | // ReSharper disable once InconsistentNaming 7 | public class CreateEntity_Arch : CreateEntity 8 | { 9 | private World world; 10 | 11 | [IterationSetup] 12 | public void Setup() 13 | { 14 | world = World.Create(); 15 | } 16 | 17 | [IterationCleanup] 18 | public void Shutdown() 19 | { 20 | World.Destroy(world); 21 | } 22 | 23 | protected override void CreateEntity1Component() 24 | { 25 | for (int n = 0; n < Entities; n++) { 26 | world.Create(new Component1{ Value = n }); 27 | } 28 | } 29 | 30 | protected override void CreateEntity3Components() 31 | { 32 | for (int n = 0; n < Entities; n++) { 33 | world.Create( 34 | new Component1{ Value = n }, 35 | new Component2{ Value = n }, 36 | new Component3{ Value = n }); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /src/Arch/CreateWorld.cs: -------------------------------------------------------------------------------- 1 | using Arch.Core; 2 | using BenchmarkDotNet.Attributes; 3 | 4 | namespace Arch; 5 | 6 | // ReSharper disable once InconsistentNaming 7 | public class CreateWorld_Arch : CreateWorld 8 | { 9 | [Benchmark] 10 | public override void Run() 11 | { 12 | var world = World.Create(); 13 | World.Destroy(world); 14 | } 15 | } -------------------------------------------------------------------------------- /src/Arch/DeleteEntity.cs: -------------------------------------------------------------------------------- 1 | using Arch.Core; 2 | using BenchmarkDotNet.Attributes; 3 | 4 | namespace Arch; 5 | 6 | // ReSharper disable once InconsistentNaming 7 | public class DeleteEntity_Arch : DeleteEntity 8 | { 9 | private World world; 10 | private Entity[] entities; 11 | 12 | [IterationSetup] 13 | public void Setup() 14 | { 15 | world = World.Create(); 16 | entities = world.CreateEntities(Entities).AddComponents(); 17 | } 18 | 19 | [IterationCleanup] 20 | public void Shutdown() 21 | { 22 | World.Destroy(world); 23 | } 24 | 25 | [Benchmark] 26 | public override void Run() 27 | { 28 | foreach (var entity in entities) { 29 | world.Destroy(entity); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /src/Arch/GetSetComponents.cs: -------------------------------------------------------------------------------- 1 | using Arch.Core; 2 | using BenchmarkDotNet.Attributes; 3 | 4 | namespace Arch; 5 | 6 | // ReSharper disable once InconsistentNaming 7 | public class GetSetComponents_Arch : GetSetComponents 8 | { 9 | private World world; 10 | private Entity[] entities; 11 | 12 | [GlobalSetup] 13 | public void Setup() 14 | { 15 | world = World.Create(); 16 | entities = world.CreateEntities(Entities).AddComponents(); 17 | } 18 | 19 | [GlobalCleanup] 20 | public void Shutdown() 21 | { 22 | World.Destroy(world); 23 | } 24 | 25 | protected override void Run1Component() 26 | { 27 | foreach (var entity in entities) { 28 | world.Get(entity) = new Component1(); 29 | } 30 | } 31 | 32 | protected override void Run5Components() 33 | { 34 | foreach (var entity in entities) { 35 | world.Get(entity) = new Component1(); 36 | world.Get(entity) = new Component2(); 37 | world.Get(entity) = new Component3(); 38 | world.Get(entity) = new Component4(); 39 | world.Get(entity) = new Component5(); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /src/Arch/QueryComponents.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | using Arch.Core; 3 | using BenchmarkDotNet.Attributes; 4 | 5 | namespace Arch; 6 | 7 | // ReSharper disable once InconsistentNaming 8 | public class QueryComponents_Arch : QueryComponents 9 | { 10 | private World world; 11 | private QueryDescription query1Description; 12 | private QueryDescription query5Description; 13 | private Query query1; 14 | private Query query5; 15 | private ForEach1 forEach1; 16 | private ForEach5 forEach5; 17 | 18 | [GlobalSetup] 19 | public void Setup() 20 | { 21 | world = World.Create(); 22 | world.CreateEntities(Entities).AddComponents(); 23 | query1Description = new QueryDescription().WithAll(); 24 | query5Description = new QueryDescription().WithAll(); 25 | query1 = world.Query(query1Description); 26 | query5 = world.Query(query5Description); 27 | Check.AreEqual(Entities, world.CountEntities(query5Description)); 28 | } 29 | 30 | [GlobalCleanup] 31 | public void Shutdown() 32 | { 33 | World.Destroy(world); 34 | } 35 | 36 | protected override void Run1Component() 37 | { 38 | foreach (ref var chunk in query1.GetChunkIterator()) 39 | { 40 | ref var first = ref chunk.GetFirst(); 41 | foreach (var entity in chunk) 42 | { 43 | Unsafe.Add(ref first, entity).Value++; 44 | } 45 | } 46 | } 47 | 48 | protected override void Run5Components() 49 | { 50 | foreach (ref var chunk in query5.GetChunkIterator()) 51 | { 52 | ref var first = ref chunk.GetFirst(); 53 | ref var second = ref chunk.GetFirst(); 54 | ref var third = ref chunk.GetFirst(); 55 | ref var fourth = ref chunk.GetFirst(); 56 | ref var fifth = ref chunk.GetFirst(); 57 | foreach (var entity in chunk) 58 | { 59 | Unsafe.Add(ref first, entity).Value = Unsafe.Add(ref second, entity).Value + 60 | Unsafe.Add(ref third, entity).Value + 61 | Unsafe.Add(ref fourth, entity).Value + 62 | Unsafe.Add(ref fifth, entity).Value; 63 | } 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /src/Arch/QueryFragmented.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | using Arch.Core; 3 | using Arch.Core.Extensions; 4 | using BenchmarkDotNet.Attributes; 5 | 6 | namespace Arch; 7 | 8 | // ReSharper disable once InconsistentNaming 9 | public class QueryFragmented_Arch : QueryFragmented 10 | { 11 | private World world; 12 | private QueryDescription queryDescription; 13 | private ForEach1 forEach; 14 | private Query query1; 15 | 16 | [GlobalSetup] 17 | public void Setup() 18 | { 19 | world = World.Create(); 20 | var entities = world.CreateEntities(Entities); 21 | for (int n = 0; n < Entities; n++) { 22 | var entity = entities[n]; 23 | entity.Add(); 24 | if ((n & 1) != 0) entity.Add(); 25 | if ((n & 2) != 0) entity.Add(); 26 | if ((n & 4) != 0) entity.Add(); 27 | if ((n & 8) != 0) entity.Add(); 28 | } 29 | queryDescription = new QueryDescription().WithAll(); 30 | query1 = world.Query(queryDescription); 31 | Check.AreEqual(Entities, world.CountEntities(queryDescription)); 32 | } 33 | 34 | [GlobalCleanup] 35 | public void Shutdown() 36 | { 37 | World.Destroy(world); 38 | } 39 | 40 | [Benchmark] 41 | public override void Run() 42 | { 43 | foreach (ref var chunk in query1.GetChunkIterator()) 44 | { 45 | ref var first = ref chunk.GetFirst(); 46 | foreach (var entity in chunk) 47 | { 48 | Unsafe.Add(ref first, entity).Value++; 49 | } 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /src/Arch/Tests.cs: -------------------------------------------------------------------------------- 1 | using Arch.Core; 2 | using Arch.Relationships; 3 | using Xunit; 4 | 5 | namespace Arch; 6 | 7 | // ReSharper disable once InconsistentNaming 8 | public static class Tests_Arch 9 | { 10 | [Fact] 11 | public static void CheckTreeCycles() 12 | { 13 | using var world = World.Create(); 14 | var entity1 = world.Create(); 15 | var entity2 = world.Create(); 16 | entity1.AddRelationship(entity2); 17 | entity2.AddRelationship(entity1); // creates cycle 18 | 19 | Assert.True(entity1.HasRelationship(entity2)); 20 | Assert.True(entity2.HasRelationship(entity1)); 21 | } 22 | } -------------------------------------------------------------------------------- /src/Arch/_BenchUtils.cs: -------------------------------------------------------------------------------- 1 | using Arch.Core; 2 | using Arch.Core.Extensions; 3 | 4 | namespace Arch; 5 | 6 | public static class BenchUtils 7 | { 8 | public static Entity[] CreateEntities (this World world, int count, Entity[] entities = null) 9 | { 10 | entities ??= new Entity[count]; 11 | for (int n = 0; n < count; n++) { 12 | entities[n] = world.Create(); 13 | } 14 | return entities; 15 | } 16 | 17 | public static Entity[] AddComponents(this Entity[] entities) 18 | { 19 | foreach (var entity in entities) 20 | { 21 | entity.Add(); 22 | entity.Add(); 23 | entity.Add(); 24 | entity.Add(); 25 | entity.Add(); 26 | } 27 | return entities; 28 | } 29 | } 30 | 31 | struct ForEach1 : IForEach 32 | { 33 | public void Update(ref Component1 t0) 34 | { 35 | ++t0.Value; 36 | } 37 | } 38 | 39 | struct ForEach5 : IForEach 40 | { 41 | public void Update(ref Component1 c1, ref Component2 c2, ref Component3 c3, ref Component4 c4, ref Component5 c5) 42 | { 43 | c1.Value = c2.Value + c3.Value + c4.Value + c5.Value; 44 | } 45 | } -------------------------------------------------------------------------------- /src/Arch/_Components.cs: -------------------------------------------------------------------------------- 1 | namespace Arch; 2 | 3 | #pragma warning disable CS0649 // Field is never assigned to, and will always have its default value 4 | 5 | internal record struct Component1(int Value); 6 | 7 | internal record struct Component2(int Value); 8 | 9 | internal record struct Component3(int Value); 10 | 11 | internal record struct Component4(int Value); 12 | 13 | internal record struct Component5(int Value); 14 | 15 | internal record struct LinkRelation(int Value); 16 | 17 | internal record struct ParentOf; -------------------------------------------------------------------------------- /src/Constants.cs: -------------------------------------------------------------------------------- 1 |  2 | public static class Constants 3 | { 4 | // --- for Benchmark: AddRemoveComponentsT1, AddRemoveComponentsT5, QueryT1, QueryT5 5 | public const int EntityCount = 100; 6 | public const int FragmentationCount = 32; 7 | 8 | // --- for Benchmark: ChildEntitiesAddRemove 9 | public const int ChildCount = 10; 10 | 11 | // --- for Benchmark: CreateEntity 12 | public const int CreateEntityCount = 100; 13 | 14 | // --- for Benchmark: CreateBulk 15 | public const int CreateBulkCount = 100; 16 | 17 | // --- for Benchmark: DeleteEntity 18 | public const int DeleteEntityCount = 100_000; // each entity has 5 components 19 | 20 | // --- for Benchmark: SearchComponentField 21 | public const int SearchSetSize = 1_000_000; // number of components having a field that can be searched 22 | public const int SearchCount = 1000; // number of executed searches / range queries 23 | 24 | // --- for Benchmark: ComponentEvents 25 | public const int EventCount = 100; // number of component add / remove events 26 | } 27 | -------------------------------------------------------------------------------- /src/DefaultEcs/AddRemoveComponents.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace DefaultEcs; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class AddRemoveComponents_DefaultEcs : AddRemoveComponents 7 | { 8 | private World world; 9 | private Entity[] entities; 10 | 11 | [GlobalSetup] 12 | public void Setup() 13 | { 14 | world = new World(); 15 | entities = world.CreateEntities(Entities); 16 | } 17 | 18 | [GlobalCleanup] 19 | public void Shutdown() 20 | { 21 | world.Dispose(); 22 | } 23 | 24 | protected override void Run1Component() 25 | { 26 | foreach (var entity in entities) { 27 | entity.Set(new Component1()); 28 | } 29 | foreach (var entity in entities) { 30 | entity.Remove(); 31 | } 32 | } 33 | 34 | protected override void Run5Components() 35 | { 36 | foreach (var entity in entities) { 37 | entity.Set(new Component1()); 38 | entity.Set(new Component2()); 39 | entity.Set(new Component3()); 40 | entity.Set(new Component4()); 41 | entity.Set(new Component5()); 42 | } 43 | foreach (var entity in entities) { 44 | entity.Remove(); 45 | entity.Remove(); 46 | entity.Remove(); 47 | entity.Remove(); 48 | entity.Remove(); 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /src/DefaultEcs/CommandBufferAddRemove.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using DefaultEcs.Command; 3 | 4 | namespace DefaultEcs; 5 | 6 | // ReSharper disable once InconsistentNaming 7 | public class CommandBufferAddRemove_DefaultEcs : CommandBufferAddRemove 8 | { 9 | private World world; 10 | private Entity[] entities; 11 | private EntityCommandRecorder commandBuffer; 12 | 13 | [GlobalSetup] 14 | public void Setup() 15 | { 16 | world = new World(); 17 | entities = world.CreateEntities(Entities); 18 | commandBuffer = new EntityCommandRecorder(); 19 | } 20 | 21 | [GlobalCleanup] 22 | public void Shutdown() 23 | { 24 | commandBuffer.Dispose(); 25 | world.Dispose(); 26 | } 27 | 28 | [Benchmark] 29 | public override void Run() 30 | { 31 | var cb = commandBuffer; 32 | foreach (var entity in entities) { 33 | var entityRecord = cb.Record(entity); 34 | entityRecord.Set(); 35 | entityRecord.Set(); 36 | } 37 | cb.Execute(); // Apply changes 1 38 | 39 | foreach (var entity in entities) { 40 | var entityRecord = cb.Record(entity); 41 | entityRecord.Remove(); 42 | entityRecord.Remove(); 43 | } 44 | cb.Execute(); // Apply changes 2 45 | } 46 | } -------------------------------------------------------------------------------- /src/DefaultEcs/ComponentEvents.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace DefaultEcs; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class ComponentEvents_DefaultEcs : ComponentEvents 7 | { 8 | private World world; 9 | private Entity[] entities; 10 | private int iterations; 11 | private int added; 12 | private int removed; 13 | 14 | [GlobalSetup] 15 | public void Setup() 16 | { 17 | world = new World(); 18 | entities = world.CreateEntities(Constants.EventCount); 19 | world.SubscribeEntityComponentAdded ((in Entity _, in Component1 _) => { added++; }); 20 | world.SubscribeEntityComponentRemoved((in Entity _, in Component1 _) => { removed++; }); 21 | } 22 | 23 | [GlobalCleanup] 24 | public void Shutdown() 25 | { 26 | world.Dispose(); 27 | var expect = iterations * Constants.EventCount; 28 | Check.AreEqual(expect, added); 29 | Check.AreEqual(expect, removed); 30 | } 31 | 32 | [Benchmark] 33 | public override void Run() 34 | { 35 | iterations++; 36 | foreach (var entity in entities) { 37 | entity.Set(new Component1()); 38 | entity.Remove(); 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /src/DefaultEcs/CreateEntity.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace DefaultEcs; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class CreateEntity_DefaultEcs : CreateEntity 7 | { 8 | private World world; 9 | 10 | [IterationSetup] 11 | public void Setup() 12 | { 13 | world = new World(); 14 | } 15 | 16 | [IterationCleanup] 17 | public void Shutdown() 18 | { 19 | world.Dispose(); 20 | } 21 | 22 | protected override void CreateEntity1Component() 23 | { 24 | for (int n = 0; n < Entities; n++) { 25 | var entity = world.CreateEntity(); 26 | entity.Set(new Component1 { Value = n }); 27 | } 28 | } 29 | 30 | protected override void CreateEntity3Components() 31 | { 32 | for (int n = 0; n < Entities; n++) { 33 | var entity = world.CreateEntity(); 34 | entity.Set(new Component1 { Value = n }); 35 | entity.Set(new Component2 { Value = n }); 36 | entity.Set(new Component3 { Value = n }); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /src/DefaultEcs/CreateWorld.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace DefaultEcs; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class CreateWorld_DefaultEcs : CreateWorld 7 | { 8 | [Benchmark] 9 | public override void Run() 10 | { 11 | var world = new World(); 12 | world.Dispose(); 13 | } 14 | } -------------------------------------------------------------------------------- /src/DefaultEcs/DeleteEntity.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace DefaultEcs; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class DeleteEntity_DefaultEcs : DeleteEntity 7 | { 8 | private World world; 9 | private Entity[] entities; 10 | 11 | [IterationSetup] 12 | public void Setup() 13 | { 14 | world = new World(); 15 | entities = world.CreateEntities(Entities).AddComponents(); 16 | } 17 | 18 | [IterationCleanup] 19 | public void Shutdown() 20 | { 21 | world.Dispose(); 22 | } 23 | 24 | [Benchmark] 25 | public override void Run() 26 | { 27 | foreach (var entity in entities) { 28 | entity.Dispose(); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/DefaultEcs/GetSetComponents.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace DefaultEcs; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class GetSetComponents_DefaultEcs : GetSetComponents 7 | { 8 | private World world; 9 | private Entity[] entities; 10 | 11 | 12 | [GlobalSetup] 13 | public void Setup() 14 | { 15 | world = new World(); 16 | entities = world.CreateEntities(Entities).AddComponents(); 17 | } 18 | 19 | [GlobalCleanup] 20 | public void Shutdown() 21 | { 22 | world.Dispose(); 23 | } 24 | 25 | protected override void Run1Component() 26 | { 27 | foreach (var entity in entities) { 28 | entity.Get() = new Component1(); 29 | } 30 | } 31 | 32 | protected override void Run5Components() 33 | { 34 | foreach (var entity in entities) { 35 | entity.Get() = new Component1(); 36 | entity.Get() = new Component2(); 37 | entity.Get() = new Component3(); 38 | entity.Get() = new Component4(); 39 | entity.Get() = new Component5(); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /src/DefaultEcs/QueryComponents.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using DefaultEcs.System; 3 | 4 | namespace DefaultEcs; 5 | 6 | // ReSharper disable once InconsistentNaming 7 | public class QueryComponents_DefaultEcs : QueryComponents 8 | { 9 | private World world; 10 | private ComponentSystem1 componentSystem1; 11 | private EntitySetSystem5 entitySetSystem5; 12 | 13 | [GlobalSetup] 14 | public void Setup() 15 | { 16 | world = new World(); 17 | world.CreateEntities(Entities).AddComponents(); 18 | componentSystem1 = new ComponentSystem1(world); 19 | entitySetSystem5 = new EntitySetSystem5(world); 20 | // query = world.Query(); 21 | // Assert.AreEqual(Constants.EntityCount, query.Count); 22 | } 23 | 24 | [GlobalCleanup] 25 | public void Shutdown() 26 | { 27 | world.Dispose(); 28 | } 29 | 30 | protected override void Run1Component() 31 | { 32 | componentSystem1.Update(0); 33 | } 34 | 35 | protected override void Run5Components() 36 | { 37 | entitySetSystem5.Update(0); 38 | } 39 | } 40 | 41 | internal class ComponentSystem1 : AComponentSystem 42 | { 43 | internal ComponentSystem1(World world) : base(world) { } 44 | protected override void Update(int state, Span components) 45 | { 46 | foreach (ref Component1 component in components) { 47 | ++component.Value; 48 | } 49 | } 50 | } 51 | 52 | internal partial class EntitySetSystem5 : AEntitySetSystem 53 | { 54 | internal EntitySetSystem5(World world) : base(world, CreateEntityContainer, null, 0) { } 55 | 56 | [Update] 57 | private static void Update(ref Component1 c1, in Component2 c2, in Component3 c3, in Component4 c4, in Component5 c5) 58 | { 59 | c1.Value = c2.Value + c3.Value + c4.Value + c5.Value; 60 | } 61 | } -------------------------------------------------------------------------------- /src/DefaultEcs/QueryFragmented.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using DefaultEcs.System; 3 | 4 | namespace DefaultEcs; 5 | 6 | // ReSharper disable once InconsistentNaming 7 | public class QueryFragmented_Default : QueryFragmented 8 | { 9 | private World world; 10 | private ComponentSystem componentSystem; 11 | 12 | [GlobalSetup] 13 | public void Setup() 14 | { 15 | world = new World(); 16 | var entities = world.CreateEntities(Entities); 17 | componentSystem = new ComponentSystem(world); 18 | for (int n = 0; n < Entities; n++) { 19 | var entity = entities[n]; 20 | entity.Set(); 21 | if ((n & 1) != 0) entity.Set(); 22 | if ((n & 2) != 0) entity.Set(); 23 | if ((n & 4) != 0) entity.Set(); 24 | if ((n & 8) != 0) entity.Set(); 25 | } 26 | } 27 | 28 | [GlobalCleanup] 29 | public void Shutdown() 30 | { 31 | world.Dispose(); 32 | } 33 | 34 | [Benchmark] 35 | public override void Run() 36 | { 37 | componentSystem.Update(0); 38 | } 39 | 40 | private class ComponentSystem : AComponentSystem 41 | { 42 | internal ComponentSystem(World world) : base(world) { } 43 | protected override void Update(int state, Span components) 44 | { 45 | foreach (ref Component1 component in components) { 46 | ++component.Value; 47 | } 48 | } 49 | } 50 | } 51 | 52 | -------------------------------------------------------------------------------- /src/DefaultEcs/_BenchUtils.cs: -------------------------------------------------------------------------------- 1 | namespace DefaultEcs; 2 | 3 | public static class BenchUtils 4 | { 5 | public static Entity[] CreateEntities (this World world, int count) 6 | { 7 | var entities = new Entity[count]; 8 | for (int n = 0; n < count; n++) { 9 | entities[n] = world.CreateEntity(); 10 | } 11 | return entities; 12 | } 13 | 14 | public static Entity[] AddComponents(this Entity[] entities) 15 | { 16 | foreach (var entity in entities) 17 | { 18 | entity.Set(); 19 | entity.Set(); 20 | entity.Set(); 21 | entity.Set(); 22 | entity.Set(); 23 | } 24 | return entities; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/DefaultEcs/_Components.cs: -------------------------------------------------------------------------------- 1 | namespace DefaultEcs; 2 | 3 | #pragma warning disable CS0649 // Field is never assigned to, and will always have its default value 4 | 5 | internal record struct Component1(int Value); 6 | 7 | internal record struct Component2(int Value); 8 | 9 | internal record struct Component3(int Value); 10 | 11 | internal record struct Component4(int Value); 12 | 13 | internal record struct Component5(int Value); 14 | 15 | -------------------------------------------------------------------------------- /src/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | ..\obj\$(MSBuildProjectName)\ 4 | 5 | -------------------------------------------------------------------------------- /src/ECS.Benchmark.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | disable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | all 34 | runtime; build; native; contentfiles; analyzers; buildtransitive 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | false 46 | TRACE 47 | ..\bin\Debug\ 48 | true 49 | 50 | 51 | 52 | false 53 | ..\bin\Release\ 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /src/Fennecs/AddRemoveComponents.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace fennecs; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class AddRemoveComponents_Fennecs : AddRemoveComponents 7 | { 8 | private World world; 9 | private Entity[] entities; 10 | 11 | [GlobalSetup] 12 | public void Setup() { 13 | world = new World(); 14 | entities = world.CreateEntities(Entities); 15 | } 16 | 17 | [GlobalCleanup] 18 | public void Shutdown() { 19 | world.Dispose(); 20 | } 21 | 22 | protected override void Run1Component() 23 | { 24 | foreach (var entity in entities) { 25 | entity.Add(new Component1()); 26 | } 27 | foreach (var entity in entities) { 28 | entity.Remove(); 29 | } 30 | } 31 | 32 | protected override void Run5Components() 33 | { 34 | foreach (var entity in entities) { 35 | entity.Add(new Component1()); 36 | entity.Add(new Component2()); 37 | entity.Add(new Component3()); 38 | entity.Add(new Component4()); 39 | entity.Add(new Component5()); 40 | } 41 | foreach (var entity in entities) { 42 | entity.Remove(); 43 | entity.Remove(); 44 | entity.Remove(); 45 | entity.Remove(); 46 | entity.Remove(); 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /src/Fennecs/AddRemoveLinks.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace fennecs; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class AddRemoveLinks_Fennecs : AddRemoveLinks 7 | { 8 | private World world; 9 | private Entity[] sources; 10 | private Entity[] targets; 11 | 12 | [GlobalSetup] 13 | public void Setup() 14 | { 15 | world = new World(); 16 | sources = world.CreateEntities(Entities).AddComponents(); 17 | targets = world.CreateEntities(Relations).AddComponents(); 18 | } 19 | 20 | [GlobalCleanup] 21 | public void Shutdown() 22 | { 23 | world.Dispose(); 24 | } 25 | 26 | [Benchmark] 27 | public override void Run() 28 | { 29 | foreach (var source in sources) 30 | { 31 | for (int n = 0; n < Relations; n++) { 32 | source.Add(new LinkRelation { Value = n }, targets[n] ); 33 | } 34 | for (int n = 0; n < Relations; n++) { 35 | source.Remove(targets[n]); 36 | } 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /src/Fennecs/AddRemoveRelations.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace fennecs; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class AddRemoveRelations_Fennecs : AddRemoveRelations 7 | { 8 | private World world; 9 | private Entity[] entities; 10 | 11 | [GlobalSetup] 12 | public void Setup() { 13 | world = new World(); 14 | entities = world.CreateEntities(Entities).AddComponents(); 15 | } 16 | 17 | [GlobalCleanup] 18 | public void Shutdown() { 19 | world.Dispose(); 20 | } 21 | 22 | protected override void AddRemove1Relation() 23 | { 24 | foreach (var entity in entities) 25 | { 26 | entity.Add (Link.With(RelationKey.Key1)); 27 | entity.Remove(Link.With(RelationKey.Key1)); 28 | } 29 | } 30 | 31 | protected override void AddRemove10Relations() 32 | { 33 | foreach (var entity in entities) 34 | { 35 | foreach (var key in RelationKey.Keys) { 36 | entity.Add (Link.With(key)); 37 | } 38 | foreach (var key in RelationKey.Keys) { 39 | entity.Remove(Link.With(key)); 40 | } 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /src/Fennecs/ChildEntitiesAddRemove.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace fennecs; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class ChildEntitiesAddRemove_Fennecs : ChildEntitiesAddRemove 7 | { 8 | private World world; 9 | private Entity[] parents; 10 | private Entity[][] children; 11 | private int entityCount; 12 | private int childCount; 13 | 14 | [GlobalSetup] 15 | public void Setup() { 16 | world = new World(); 17 | entityCount = Entities; 18 | childCount = Constants.ChildCount; 19 | parents = world.CreateEntities(entityCount).AddComponents(); 20 | children = new Entity[entityCount][]; 21 | for (int n = 0; n < entityCount; n++) { 22 | children[n] = world.CreateEntities(childCount).AddComponents(); 23 | } 24 | } 25 | 26 | [GlobalCleanup] 27 | public void Shutdown() { 28 | world.Dispose(); 29 | } 30 | 31 | // found no concrete example. Chose ChildOf approach. An alternative is ParentOf apprach. 32 | // see: https://fennecs.tech/docs/Components/Relation.html 33 | [Benchmark] 34 | public override void Run() 35 | { 36 | for (int n = 0; n < entityCount; n++) { 37 | for (int child = 0; child < childCount; child++) { 38 | children[n][child].Add(parents[n]); 39 | } 40 | } 41 | // Assert.AreEqual(entityCount * childCount, CountChildren()); 42 | for (int n = 0; n < entityCount; n++) { 43 | for (int child = childCount - 1; child >= 0; child--) { 44 | children[n][child].Remove(parents[n]); 45 | } 46 | } 47 | } 48 | 49 | // method only for verification 50 | private int CountChildren() 51 | { 52 | int count = 0; 53 | for (int n = 0; n < entityCount; n++) { 54 | for (int child = 0; child < childCount; child++) { 55 | var hasParent = children[n][child].Has(parents[n]); 56 | count += hasParent ? 1 : 0; 57 | } 58 | } 59 | return count; 60 | } 61 | } -------------------------------------------------------------------------------- /src/Fennecs/CreateBulk.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace fennecs; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class CreateBulk_Fennecs : CreateBulk 7 | { 8 | private World world; 9 | 10 | [IterationSetup] 11 | public void Setup() 12 | { 13 | world = new World(); 14 | } 15 | 16 | [IterationCleanup] 17 | public void Shutdown() 18 | { 19 | world.Dispose(); 20 | } 21 | 22 | protected override void CreateEntity1Component() 23 | { 24 | world.Entity() 25 | .Add(new Component1()) 26 | .Spawn(Entities); 27 | for (int n = 0; n < world.Count; n++) { 28 | world[n].Ref().Value = n; 29 | } 30 | } 31 | 32 | protected override void CreateEntity3Components() 33 | { 34 | world.Entity() 35 | .Add(new Component1()) 36 | .Add(new Component2()) 37 | .Add(new Component3()) 38 | .Spawn(Entities); 39 | for (int n = 0; n < world.Count; n++) { 40 | var entity = world[n]; 41 | entity.Ref().Value = n; 42 | entity.Ref().Value = n; 43 | entity.Ref().Value = n; 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /src/Fennecs/CreateEntity.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace fennecs; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class CreateEntity_Fennecs : CreateEntity 7 | { 8 | private World world; 9 | 10 | [IterationSetup] 11 | public void Setup() 12 | { 13 | world = new World(); 14 | } 15 | 16 | [IterationCleanup] 17 | public void Shutdown() 18 | { 19 | world.Dispose(); 20 | } 21 | 22 | protected override void CreateEntity1Component() 23 | { 24 | for (int n = 0; n < Entities; n++) { 25 | world.Spawn().Add(new Component1{ Value = n }); 26 | } 27 | } 28 | 29 | protected override void CreateEntity3Components() 30 | { 31 | for (int n = 0; n < Entities; n++) { 32 | var entity = world.Spawn(); 33 | entity.Add(new Component1{ Value = n }); 34 | entity.Add(new Component2{ Value = n }); 35 | entity.Add(new Component3{ Value = n }); 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /src/Fennecs/CreateWorld.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace fennecs; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class CreateWorld_Fennecs : CreateWorld 7 | { 8 | [Benchmark] 9 | public override void Run() 10 | { 11 | var world = new World(); 12 | world.Dispose(); 13 | } 14 | } -------------------------------------------------------------------------------- /src/Fennecs/DeleteEntity.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace fennecs; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class DeleteEntity_Fennecs : DeleteEntity 7 | { 8 | private World world; 9 | private Entity[] entities; 10 | 11 | [IterationSetup] 12 | public void Setup() 13 | { 14 | world = new World(); 15 | entities = world.CreateEntities(Entities).AddComponents(); 16 | } 17 | 18 | [IterationCleanup] 19 | public void Shutdown() 20 | { 21 | world.Dispose(); 22 | } 23 | 24 | [Benchmark] 25 | public override void Run() 26 | { 27 | foreach (var entity in entities) { 28 | entity.Despawn(); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/Fennecs/GetSetComponents.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace fennecs; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class GetSetComponents_Fennecs : GetSetComponents 7 | { 8 | private World world; 9 | private Entity[] entities; 10 | 11 | [GlobalSetup] 12 | public void Setup() { 13 | world = new World(); 14 | entities = world.CreateEntities(Entities).AddComponents(); 15 | } 16 | 17 | [GlobalCleanup] 18 | public void Shutdown() { 19 | world.Dispose(); 20 | } 21 | 22 | protected override void Run1Component() 23 | { 24 | foreach (var entity in entities) { 25 | entity.Ref() = new Component1(); 26 | } 27 | } 28 | 29 | protected override void Run5Components() 30 | { 31 | foreach (var entity in entities) { 32 | entity.Ref() = new Component1(); 33 | entity.Ref() = new Component2(); 34 | entity.Ref() = new Component3(); 35 | entity.Ref() = new Component4(); 36 | entity.Ref() = new Component5(); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /src/Fennecs/QueryComponents.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace fennecs; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class QueryComponents_Fennecs : QueryComponents 7 | { 8 | private World world; 9 | private Stream stream1; 10 | private Stream stream5; 11 | 12 | [GlobalSetup] 13 | public void Setup() { 14 | world = new World(); 15 | world.CreateEntities(Entities).AddComponents(); 16 | stream1 = world.Query().Compile().Stream(); 17 | stream5 = world.Query().Compile().Stream(); 18 | Check.AreEqual(Entities, stream5.Count); 19 | } 20 | 21 | [GlobalCleanup] 22 | public void Shutdown() { 23 | world.Dispose(); 24 | } 25 | 26 | protected override void Run1Component() 27 | { 28 | stream1.Raw(components => { 29 | foreach (ref Component1 component1 in components.Span) { 30 | component1.Value++; 31 | } 32 | }); 33 | } 34 | 35 | protected override void Run5Components() 36 | { 37 | stream5.Raw((components1, components2, components3, components4, components5) => 38 | { 39 | var span1 = components1.Span; 40 | var span2 = components2.Span; 41 | var span3 = components3.Span; 42 | var span4 = components4.Span; 43 | var span5 = components5.Span; 44 | for (int n = 0; n < components1.Length; n++) { 45 | span1[n].Value = span2[n].Value + span3[n].Value + span4[n].Value + span5[n].Value; 46 | } 47 | }); 48 | } 49 | } -------------------------------------------------------------------------------- /src/Fennecs/QueryFragmented.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace fennecs; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class QueryFragmented_Fennecs : QueryFragmented 7 | { 8 | private World world; 9 | private Stream stream; 10 | 11 | [GlobalSetup] 12 | public void Setup() 13 | { 14 | world = new World(); 15 | var entities = world.CreateEntities(Entities); 16 | stream = world.Query().Compile().Stream(); 17 | for (int n = 0; n < Entities; n++) { 18 | var entity = entities[n]; 19 | entity.Add(new Component1()); 20 | if ((n & 1) != 0) entity.Add(new Component2()); 21 | if ((n & 2) != 0) entity.Add(new Component3()); 22 | if ((n & 4) != 0) entity.Add(new Component4()); 23 | if ((n & 8) != 0) entity.Add(new Component5()); 24 | } 25 | Check.AreEqual(Entities, stream.Count); 26 | } 27 | 28 | [GlobalCleanup] 29 | public void Shutdown() 30 | { 31 | world.Dispose(); 32 | } 33 | 34 | [Benchmark] 35 | public override void Run() 36 | { 37 | stream.Raw(components => { 38 | foreach (ref Component1 component1 in components.Span) { 39 | component1.Value++; 40 | } 41 | }); 42 | } 43 | } -------------------------------------------------------------------------------- /src/Fennecs/Tests.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | 3 | namespace fennecs; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public static class Tests_Fennecs 7 | { 8 | [Fact] 9 | public static void CheckTreeCycles() 10 | { 11 | using var world = new World(); 12 | var entity1 = world.Spawn(); 13 | var entity2 = world.Spawn(); 14 | entity1.Add(entity2); 15 | entity2.Add(entity1); // creates cycle 16 | 17 | Assert.True(entity1.Has(entity2)); 18 | Assert.True(entity2.Has(entity1)); 19 | } 20 | } -------------------------------------------------------------------------------- /src/Fennecs/_BenchUtils.cs: -------------------------------------------------------------------------------- 1 | namespace fennecs; 2 | 3 | public static class BenchUtils 4 | { 5 | public static Entity[] CreateEntities (this World world, int count) 6 | { 7 | var entities = new Entity[count]; 8 | for (int n = 0; n < count; n++) { 9 | entities[n] = world.Spawn(); 10 | } 11 | return entities; 12 | } 13 | 14 | public static Entity[] AddComponents(this Entity[] entities) 15 | { 16 | foreach (var entity in entities) 17 | { 18 | entity.Add(); 19 | entity.Add(); 20 | entity.Add(); 21 | entity.Add(); 22 | entity.Add(); 23 | } 24 | return entities; 25 | } 26 | } -------------------------------------------------------------------------------- /src/Fennecs/_Components.cs: -------------------------------------------------------------------------------- 1 | namespace fennecs; 2 | 3 | #pragma warning disable CS0649 // Field is never assigned to, and will always have its default value 4 | 5 | internal record struct Component1(int Value); 6 | 7 | internal record struct Component2(int Value); 8 | 9 | internal record struct Component3(int Value); 10 | 11 | internal record struct Component4(int Value); 12 | 13 | internal record struct Component5(int Value); 14 | 15 | // ReSharper disable once NotAccessedPositionalProperty.Global 16 | internal record struct LinkRelation(int Value); 17 | 18 | internal static class RelationKey 19 | { 20 | public const string Key1 = nameof(Key1); 21 | public const string Key2 = nameof(Key2); 22 | public const string Key3 = nameof(Key3); 23 | public const string Key4 = nameof(Key4); 24 | public const string Key5 = nameof(Key5); 25 | public const string Key6 = nameof(Key6); 26 | public const string Key7 = nameof(Key7); 27 | public const string Key8 = nameof(Key8); 28 | public const string Key9 = nameof(Key9); 29 | public const string Key10= nameof(Key10); 30 | 31 | public static readonly string[] Keys = new [] { Key1, Key2, Key3, Key4, Key5, Key6, Key7, Key8, Key9, Key10 }; 32 | } 33 | 34 | internal record struct ChildOf; 35 | -------------------------------------------------------------------------------- /src/Flecs.NET/AddRemoveComponents.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using Flecs.NET.Core; 3 | 4 | namespace Flecs.NET; 5 | 6 | // ReSharper disable once InconsistentNaming 7 | public class AddRemoveComponents_FlecsNet : AddRemoveComponents 8 | { 9 | private World world; 10 | private Entity[] entities; 11 | 12 | [GlobalSetup] 13 | public void Setup() { 14 | world = World.Create(); 15 | entities = world.CreateEntities(Entities); 16 | } 17 | 18 | [GlobalCleanup] 19 | public void Shutdown() { 20 | world.Dispose(); 21 | } 22 | 23 | protected override void Run1Component() 24 | { 25 | foreach (var entity in entities) { 26 | entity.Set(new Component1()); 27 | } 28 | foreach (var entity in entities) { 29 | entity.Remove(); 30 | } 31 | } 32 | 33 | protected override void Run5Components() 34 | { 35 | foreach (var entity in entities) { 36 | entity.Set(new Component1()); 37 | entity.Set(new Component2()); 38 | entity.Set(new Component3()); 39 | entity.Set(new Component4()); 40 | entity.Set(new Component5()); 41 | } 42 | foreach (var entity in entities) { 43 | entity.Remove(); 44 | entity.Remove(); 45 | entity.Remove(); 46 | entity.Remove(); 47 | entity.Remove(); 48 | } 49 | } 50 | 51 | } -------------------------------------------------------------------------------- /src/Flecs.NET/AddRemoveLinks.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using Flecs.NET.Core; 3 | 4 | namespace Flecs.NET; 5 | 6 | // ReSharper disable once InconsistentNaming 7 | public class AddRemoveLinks_FlecsNet : AddRemoveLinks 8 | { 9 | private World world; 10 | private Entity[] sources; 11 | private Entity[] targets; 12 | private Entity[] relations; 13 | 14 | [GlobalSetup] 15 | public void Setup() 16 | { 17 | world = World.Create(); 18 | sources = world.CreateEntities(Entities).AddComponents(); 19 | targets = world.CreateEntities(Relations).AddComponents(); 20 | relations = world.CreateEntities(Relations); 21 | foreach (var relation in relations) { 22 | relation.Set(new LinkRelation()); // add a component with data to every relation entity 23 | } 24 | } 25 | 26 | [GlobalCleanup] 27 | public void Shutdown() 28 | { 29 | world.Dispose(); 30 | } 31 | 32 | [Benchmark] 33 | public override void Run() 34 | { 35 | foreach (var source in sources) 36 | { 37 | for (int n = 0; n < Relations; n++) { 38 | source.Add(relations[n], targets[n]); 39 | } 40 | for (int n = 0; n < Relations; n++) { 41 | source.Remove(relations[n], targets[n]); 42 | } 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /src/Flecs.NET/AddRemoveRelations.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using Flecs.NET.Core; 3 | 4 | namespace Flecs.NET; 5 | 6 | // ReSharper disable once InconsistentNaming 7 | public class AddRemoveRelations_FlecsNet : AddRemoveRelations 8 | { 9 | private World world; 10 | private Entity[] entities; 11 | 12 | [GlobalSetup] 13 | public void Setup() 14 | { 15 | world = World.Create(); 16 | entities = world.CreateEntities(Entities).AddComponents(); 17 | } 18 | 19 | [GlobalCleanup] 20 | public void Shutdown() 21 | { 22 | world.Dispose(); 23 | } 24 | 25 | protected override void AddRemove1Relation() 26 | { 27 | foreach (var entity in entities) 28 | { 29 | entity.Set(new Relation(1337)); 30 | entity.Remove(); 31 | } 32 | } 33 | 34 | protected override void AddRemove10Relations() 35 | { 36 | foreach (var entity in entities) 37 | { 38 | entity.Set(new Relation(1337)); 39 | entity.Set(new Relation(1337)); 40 | entity.Set(new Relation(1337)); 41 | entity.Set(new Relation(1337)); 42 | entity.Set(new Relation(1337)); 43 | entity.Set(new Relation(1337)); 44 | entity.Set(new Relation(1337)); 45 | entity.Set(new Relation(1337)); 46 | entity.Set(new Relation(1337)); 47 | entity.Set(new Relation(1337)); 48 | 49 | entity.Remove(); 50 | entity.Remove(); 51 | entity.Remove(); 52 | entity.Remove(); 53 | entity.Remove(); 54 | entity.Remove(); 55 | entity.Remove(); 56 | entity.Remove(); 57 | entity.Remove(); 58 | entity.Remove(); 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /src/Flecs.NET/ChildEntitiesAddRemove.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using Flecs.NET.Core; 3 | 4 | namespace Flecs.NET; 5 | 6 | // ReSharper disable once InconsistentNaming 7 | public class ChildEntitiesAddRemove_FlecsNet : ChildEntitiesAddRemove 8 | { 9 | private World world; 10 | private Entity[] parents; 11 | private Entity[][] children; 12 | private int entityCount; 13 | private int childCount; 14 | 15 | [GlobalSetup] 16 | public void Setup() { 17 | world = World.Create(); 18 | entityCount = Entities; 19 | childCount = Constants.ChildCount; 20 | parents = world.CreateEntities(entityCount).AddComponents(); 21 | children = new Entity[entityCount][]; 22 | for (int n = 0; n < entityCount; n++) { 23 | children[n] = world.CreateEntities(childCount).AddComponents(); 24 | } 25 | } 26 | 27 | [GlobalCleanup] 28 | public void Shutdown() { 29 | world.Dispose(); 30 | } 31 | 32 | // according to example : https://github.com/SanderMertens/flecs/blob/master/docs/Relationships.md#the-childof-relationship 33 | [Benchmark] 34 | public override void Run() 35 | { 36 | for (int n = 0; n < entityCount; n++) { 37 | for (int child = 0; child < childCount; child++) { 38 | children[n][child].Add(Ecs.ChildOf, parents[n]); 39 | } 40 | } 41 | // Assert.AreEqual(entityCount * childCount, CountChildren()); 42 | for (int n = 0; n < entityCount; n++) { 43 | for (int child = childCount - 1; child >= 0; child--) { 44 | children[n][child].Remove(Ecs.ChildOf, parents[n]); 45 | } 46 | } 47 | } 48 | 49 | // method only for verification 50 | private int CountChildren() 51 | { 52 | int count = 0; 53 | for (int n = 0; n < entityCount; n++) { 54 | for (int child = 0; child < childCount; child++) { 55 | var hasParent = children[n][child].Has(Ecs.ChildOf, parents[n]); 56 | count += hasParent ? 1 : 0; 57 | } 58 | } 59 | return count; 60 | } 61 | } -------------------------------------------------------------------------------- /src/Flecs.NET/CommandBufferAddRemove.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using Flecs.NET.Core; 3 | 4 | namespace Flecs.NET; 5 | 6 | // ReSharper disable once InconsistentNaming 7 | public class CommandBufferAddRemove_FlecsNet : CommandBufferAddRemove 8 | { 9 | private World world; 10 | private Entity[] entities; 11 | 12 | [GlobalSetup] 13 | public void Setup() { 14 | world = World.Create(); 15 | entities = world.CreateEntities(Entities); 16 | } 17 | 18 | [GlobalCleanup] 19 | public void Shutdown() { 20 | world.Dispose(); 21 | } 22 | 23 | [Benchmark] 24 | public override void Run() 25 | { 26 | world.DeferBegin(); 27 | foreach (var entity in entities) { 28 | entity 29 | .Set(new Component1()) 30 | .Set(new Component2()); 31 | } 32 | world.DeferEnd(); // Apply changes 1 33 | 34 | world.DeferBegin(); 35 | foreach (var entity in entities) { 36 | entity 37 | .Remove() 38 | .Remove(); 39 | } 40 | world.DeferEnd(); // Apply changes 2 41 | } 42 | } -------------------------------------------------------------------------------- /src/Flecs.NET/ComponentEvents.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using Flecs.NET.Core; 3 | 4 | namespace Flecs.NET; 5 | 6 | // ReSharper disable once InconsistentNaming 7 | public class ComponentEvents_FlecsNet : ComponentEvents 8 | { 9 | private World world; 10 | private Entity[] entities; 11 | private int iterations; 12 | private int added; 13 | private int removed; 14 | 15 | [GlobalSetup] 16 | public void Setup() { 17 | world = World.Create(); 18 | entities = world.CreateEntities(Constants.EventCount); 19 | world.Observer().Event(Ecs.OnAdd).Each((Entity _, ref Component1 _) => { 20 | added++; 21 | }); 22 | world.Observer().Event(Ecs.OnRemove).Each((Entity _, ref Component1 _) => { 23 | removed++; 24 | }); 25 | } 26 | 27 | [GlobalCleanup] 28 | public void Shutdown() { 29 | world.Dispose(); 30 | var expect = iterations * Constants.EventCount; 31 | Check.AreEqual(expect, added); 32 | Check.AreEqual(expect, removed); 33 | } 34 | 35 | [Benchmark] 36 | public override void Run() 37 | { 38 | iterations++; 39 | foreach (var entity in entities) { 40 | entity.Set(new Component1()); 41 | entity.Remove(); 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /src/Flecs.NET/CreateEntity.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using Flecs.NET.Core; 3 | 4 | namespace Flecs.NET; 5 | 6 | // ReSharper disable once InconsistentNaming 7 | public class CreateEntity_FlecsNet : CreateEntity 8 | { 9 | private World world; 10 | 11 | [IterationSetup] 12 | public void Setup() 13 | { 14 | world = World.Create(); 15 | } 16 | 17 | [IterationCleanup] 18 | public void Shutdown() 19 | { 20 | world.Dispose(); 21 | } 22 | 23 | protected override void CreateEntity1Component() 24 | { 25 | var table = world.Table().Add(); 26 | for (int n = 0; n < Entities; n++) { 27 | var entity = world.Entity(table); 28 | entity.GetMut().Value = n; 29 | } 30 | } 31 | 32 | protected override void CreateEntity3Components() 33 | { 34 | var table = world.Table() 35 | .Add() 36 | .Add() 37 | .Add(); 38 | for (int n = 0; n < Entities; n++) { 39 | world.Entity(table); 40 | var entity = world.Entity(table); 41 | entity.GetMut().Value = n; 42 | entity.GetMut().Value = n; 43 | entity.GetMut().Value = n; 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /src/Flecs.NET/CreateWorld.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using Flecs.NET.Core; 3 | 4 | namespace Flecs.NET; 5 | 6 | // ReSharper disable once InconsistentNaming 7 | public class CreateWorld_FlecsNet : CreateWorld 8 | { 9 | [Benchmark] 10 | public override void Run() 11 | { 12 | var world = World.Create(); 13 | world.Dispose(); 14 | } 15 | } -------------------------------------------------------------------------------- /src/Flecs.NET/DeleteEntity.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using Flecs.NET.Core; 3 | 4 | namespace Flecs.NET; 5 | 6 | // ReSharper disable once InconsistentNaming 7 | public class DeleteEntity_FlecsNet : DeleteEntity 8 | { 9 | private World world; 10 | private Entity[] entities; 11 | 12 | [IterationSetup] 13 | public void Setup() 14 | { 15 | world = World.Create(); 16 | entities = world.CreateEntities(Entities).AddComponents(); 17 | } 18 | 19 | [IterationCleanup] 20 | public void Shutdown() 21 | { 22 | world.Dispose(); 23 | } 24 | 25 | [Benchmark] 26 | public override void Run() 27 | { 28 | foreach (var entity in entities) { 29 | entity.Destruct(); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /src/Flecs.NET/GetSetComponents.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using Flecs.NET.Core; 3 | 4 | namespace Flecs.NET; 5 | 6 | // ReSharper disable once InconsistentNaming 7 | public class GetSetComponents_FlecsNet : GetSetComponents 8 | { 9 | private World world; 10 | private Entity[] entities; 11 | 12 | [GlobalSetup] 13 | public void Setup() { 14 | world = World.Create(); 15 | entities = world.CreateEntities(Entities).AddComponents(); 16 | } 17 | 18 | [GlobalCleanup] 19 | public void Shutdown() { 20 | world.Dispose(); 21 | } 22 | 23 | protected override void Run1Component() 24 | { 25 | foreach (var entity in entities) { 26 | entity.GetMut() = new Component1(); 27 | } 28 | } 29 | 30 | protected override void Run5Components() 31 | { 32 | foreach (var entity in entities) { 33 | entity.GetMut() = new Component1(); 34 | entity.GetMut() = new Component2(); 35 | entity.GetMut() = new Component3(); 36 | entity.GetMut() = new Component4(); 37 | entity.GetMut() = new Component5(); 38 | } 39 | } 40 | 41 | } -------------------------------------------------------------------------------- /src/Flecs.NET/QueryComponents.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using Flecs.NET.Core; 3 | 4 | namespace Flecs.NET; 5 | 6 | // ReSharper disable once InconsistentNaming 7 | public class QueryComponents_FlecsNet : QueryComponents 8 | { 9 | private World world; 10 | private Query query1; 11 | private Query query5; 12 | 13 | [GlobalSetup] 14 | public void Setup() { 15 | world = World.Create(); 16 | world.CreateEntities(Entities).AddComponents(); 17 | query1 = world.QueryBuilder().With().Build(); 18 | query5 = world.QueryBuilder().With().With().With().With().With().Build(); 19 | } 20 | 21 | [GlobalCleanup] 22 | public void Shutdown() { 23 | world.Dispose(); 24 | } 25 | 26 | protected override void Run1Component() 27 | { 28 | query1.Iter((Iter _, Span c1Span) => { 29 | foreach (ref var c1 in c1Span) { 30 | c1.Value++; 31 | } 32 | }); 33 | } 34 | 35 | protected override void Run5Components() 36 | { 37 | query5.Iter((Iter _, 38 | Span c1Span, 39 | Span c2Span, 40 | Span c3Span, 41 | Span c4Span, 42 | Span c5Span) => 43 | { 44 | int len = c1Span.Length; 45 | for (int n = 0; n < len; n++) { 46 | c1Span[n].Value = c2Span[n].Value + c3Span[n].Value + c4Span[n].Value + c5Span[n].Value; 47 | } 48 | }); 49 | } 50 | } -------------------------------------------------------------------------------- /src/Flecs.NET/QueryFragmented.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using Flecs.NET.Core; 3 | 4 | namespace Flecs.NET; 5 | 6 | // ReSharper disable once InconsistentNaming 7 | public class QueryFragmented_FlecsNet : QueryFragmented 8 | { 9 | private World world; 10 | private Query query; 11 | 12 | 13 | [GlobalSetup] 14 | public void Setup() 15 | { 16 | world = World.Create(); 17 | var entities = world.CreateEntities(Entities); 18 | query = world.QueryBuilder().With().Build(); 19 | for (int n = 0; n < Entities; n++) { 20 | var entity = entities[n]; 21 | entity.Set(new Component1()); 22 | if ((n & 1) != 0) entity.Set(new Component2()); 23 | if ((n & 2) != 0) entity.Set(new Component3()); 24 | if ((n & 4) != 0) entity.Set(new Component4()); 25 | if ((n & 8) != 0) entity.Set(new Component5()); 26 | } 27 | Check.AreEqual(Entities, query.Count()); 28 | } 29 | 30 | [GlobalCleanup] 31 | public void Shutdown() 32 | { 33 | world.Dispose(); 34 | } 35 | 36 | [Benchmark] 37 | public override void Run() 38 | { 39 | query.Iter((Iter _, Span c1Span) => { 40 | foreach (ref var c1 in c1Span) { 41 | c1.Value++; 42 | } 43 | }); 44 | } 45 | } -------------------------------------------------------------------------------- /src/Flecs.NET/Tests.cs: -------------------------------------------------------------------------------- 1 | using Flecs.NET.Core; 2 | using Xunit; 3 | 4 | namespace Flecs.NET; 5 | 6 | // ReSharper disable once InconsistentNaming 7 | public static class Tests_FlecsNet 8 | { 9 | [Fact] 10 | public static void CheckTreeCycles() 11 | { 12 | using var world = World.Create(); 13 | var entity1 = world.Entity(); 14 | var entity2 = world.Entity(); 15 | entity1.Add(Ecs.ChildOf, entity2); 16 | // entity2.Add(Ecs.ChildOf, entity1); // process exit - cause: Stack overflow. 17 | } 18 | } -------------------------------------------------------------------------------- /src/Flecs.NET/_BenchUtils.cs: -------------------------------------------------------------------------------- 1 | using Flecs.NET.Core; 2 | 3 | namespace Flecs.NET; 4 | 5 | public static class BenchUtils 6 | { 7 | public static Entity[] CreateEntities (this World world, int count) 8 | { 9 | var entities = new Entity[count]; 10 | for (int n = 0; n < count; n++) { 11 | entities[n] = world.Entity(); 12 | } 13 | return entities; 14 | } 15 | 16 | public static Entity[] AddComponents(this Entity[] entities) 17 | { 18 | foreach (var entity in entities) 19 | { 20 | entity.Set(new Component1()); 21 | entity.Set(new Component2()); 22 | entity.Set(new Component3()); 23 | entity.Set(new Component4()); 24 | entity.Set(new Component5()); 25 | } 26 | return entities; 27 | } 28 | } -------------------------------------------------------------------------------- /src/Flecs.NET/_Components.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable NotAccessedPositionalProperty.Global 2 | namespace Flecs.NET; 3 | 4 | #pragma warning disable CS0649 // Field is never assigned to, and will always have its default value 5 | 6 | internal record struct Component1(int Value); 7 | 8 | internal record struct Component2(int Value); 9 | 10 | internal record struct Component3(int Value); 11 | 12 | internal record struct Component4(int Value); 13 | 14 | internal record struct Component5(int Value); 15 | 16 | 17 | internal record struct LinkRelation(int Value); 18 | 19 | internal record struct Tag1; 20 | internal record struct Tag2; 21 | internal record struct Tag3; 22 | internal record struct Tag4; 23 | internal record struct Tag5; 24 | internal record struct Tag6; 25 | internal record struct Tag7; 26 | internal record struct Tag8; 27 | internal record struct Tag9; 28 | internal record struct Tag10; 29 | 30 | // --- flecs specific: discriminator for relations are tags 31 | internal record struct Relation(int Value); -------------------------------------------------------------------------------- /src/Frent/AddRemoveComponents.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using Frent.Core; 3 | 4 | namespace Frent; 5 | 6 | // ReSharper disable once InconsistentNaming 7 | public class AddRemoveComponents_Frent : AddRemoveComponents 8 | { 9 | private World world; 10 | private Entity[] entities; 11 | 12 | [GlobalSetup] 13 | public void Setup() { 14 | world = new World(); 15 | entities = world.CreateEntities(Entities); 16 | } 17 | 18 | [GlobalCleanup] 19 | public void Shutdown() { 20 | world.Dispose(); 21 | } 22 | 23 | protected override void Run1Component() 24 | { 25 | foreach (var entity in entities) { 26 | entity.Add(new Component1()); 27 | } 28 | foreach (var entity in entities) { 29 | entity.Remove(); 30 | } 31 | } 32 | 33 | protected override void Run5Components() 34 | { 35 | foreach (var entity in entities) 36 | { 37 | entity.Add(default, default, default, default, default); 38 | } 39 | foreach (var entity in entities) 40 | { 41 | entity.Remove(); 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /src/Frent/CommandBufferAddRemove.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using Frent.Core; 3 | using Frent; 4 | 5 | namespace Frent; 6 | 7 | // ReSharper disable once InconsistentNaming 8 | public class CommandBufferAddRemove_Frent : CommandBufferAddRemove 9 | { 10 | private Entity[] entities; 11 | private CommandBuffer commandBuffer; 12 | 13 | [GlobalSetup] 14 | public void Setup() 15 | { 16 | var world = new World(); 17 | entities = world.CreateEntities(Entities); 18 | commandBuffer = new CommandBuffer(world); 19 | } 20 | 21 | [Benchmark(Baseline = true)] 22 | public override void Run() 23 | { 24 | var cb = commandBuffer; 25 | foreach (var entity in entities) { 26 | cb.AddComponent(entity, new Component1()); 27 | cb.AddComponent(entity, new Component2()); 28 | } 29 | cb.Playback(); // Apply changes 1 30 | 31 | foreach (var entity in entities) { 32 | cb.RemoveComponent(entity); 33 | cb.RemoveComponent(entity); 34 | } 35 | cb.Playback(); // Apply changes 2 36 | } 37 | } -------------------------------------------------------------------------------- /src/Frent/ComponentEvents.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace Frent; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class ComponentEvents_Frent : ComponentEvents 7 | { 8 | private Entity[] entities; 9 | private int iterations; 10 | private int added; 11 | private int removed; 12 | 13 | [GlobalSetup] 14 | public void Setup() 15 | { 16 | var world = new World(); 17 | entities = world.CreateEntities(Constants.EventCount); 18 | world.ComponentRemoved += (_, _) => { removed++; }; 19 | world.ComponentAdded += (_, _) => { added++; }; 20 | } 21 | 22 | [GlobalCleanup] 23 | public void Shutdown() { 24 | var expect = iterations * Constants.EventCount; 25 | Check.AreEqual(expect, added); 26 | Check.AreEqual(expect, removed); 27 | } 28 | 29 | [Benchmark(Baseline = true)] 30 | public override void Run() 31 | { 32 | iterations++; 33 | foreach (var entity in entities) { 34 | entity.Add(new Component1()); 35 | entity.Remove(); 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /src/Frent/CreateBulk.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace Frent; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class CreateBulk_Frent : CreateBulk 7 | { 8 | private World world; 9 | 10 | [IterationSetup] 11 | public void Setup() 12 | { 13 | world = new World(); 14 | } 15 | 16 | [IterationCleanup] 17 | public void Shutdown() 18 | { 19 | world.Dispose(); 20 | } 21 | 22 | protected override void CreateEntity1Component() 23 | { 24 | var span = world.CreateMany(Entities).Span; 25 | for (int n = 0; n < span.Length; n++) 26 | { 27 | span[n].Value = n; 28 | } 29 | } 30 | 31 | protected override void CreateEntity3Components() 32 | { 33 | var data = world.CreateMany(Entities); 34 | var span1 = data.Span1; 35 | var span2 = data.Span2[..span1.Length]; 36 | var span3 = data.Span3[..span1.Length]; 37 | for (int n = 0; n < span1.Length; n++) 38 | { 39 | span1[n].Value = n; 40 | span2[n].Value = n; 41 | span3[n].Value = n; 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /src/Frent/CreateEntity.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using CommandLine.Text; 3 | 4 | namespace Frent; 5 | 6 | // ReSharper disable once InconsistentNaming 7 | public class CreateEntity_Frent : CreateEntity 8 | { 9 | private World world; 10 | 11 | [IterationSetup] 12 | public void Setup() 13 | { 14 | world = new World(); 15 | } 16 | 17 | [IterationCleanup] 18 | public void Shutdown() 19 | { 20 | world.Dispose(); 21 | } 22 | 23 | protected override void CreateEntity1Component() 24 | { 25 | for (int n = 0; n < Entities; n++) 26 | { 27 | world.Create(new(n)); 28 | } 29 | } 30 | 31 | protected override void CreateEntity3Components() 32 | { 33 | for (int n = 0; n < Entities; n++) 34 | { 35 | world.Create(new(n), new(n), new(n)); 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /src/Frent/CreateWorld.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace Frent; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class CreateWorld_Frent : CreateWorld 7 | { 8 | [Benchmark] 9 | public override void Run() 10 | { 11 | var world = new World(); 12 | world.Dispose(); 13 | } 14 | } -------------------------------------------------------------------------------- /src/Frent/DeleteEntity.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using System.Diagnostics; 3 | 4 | namespace Frent; 5 | 6 | // ReSharper disable once InconsistentNaming 7 | public class DeleteEntity_Frent : DeleteEntity 8 | { 9 | private World world; 10 | private Entity[] entities; 11 | 12 | [IterationSetup] 13 | public void Setup() 14 | { 15 | world = new World(); 16 | entities = world.CreateEntities(Entities).AddComponents(); 17 | } 18 | 19 | [IterationCleanup] 20 | public void Shutdown() 21 | { 22 | world.Dispose(); 23 | } 24 | 25 | [Benchmark] 26 | public override void Run() 27 | { 28 | foreach (var entity in entities) { 29 | entity.Delete(); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /src/Frent/GetSetComponents.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using Frent.Core; 3 | using Microsoft.CodeAnalysis; 4 | using System.Runtime.CompilerServices; 5 | 6 | namespace Frent; 7 | 8 | // ReSharper disable once InconsistentNaming 9 | public class GetSetComponents_Frent : GetSetComponents 10 | { 11 | private World world; 12 | private Entity[] entities; 13 | 14 | [GlobalSetup] 15 | public void Setup() { 16 | world = new World(); 17 | entities = world.CreateEntities(Entities).AddComponents(); 18 | } 19 | 20 | [GlobalCleanup] 21 | public void Shutdown() { 22 | world.Dispose(); 23 | } 24 | 25 | protected override void Run1Component() 26 | { 27 | foreach(var entity in entities) 28 | { 29 | entity.Get().Value = new(); 30 | } 31 | } 32 | 33 | protected override void Run5Components() 34 | { 35 | foreach (var entity in entities) { 36 | entity.Deconstruct(out Ref c1, out Ref c2, out Ref c3, out Ref c4, out Ref c5); 37 | c1.Value = new Component1(); 38 | c2.Value = new Component2(); 39 | c3.Value = new Component3(); 40 | c4.Value = new Component4(); 41 | c5.Value = new Component5(); 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /src/Frent/QueryComponents.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using Frent.Systems; 3 | 4 | namespace Frent; 5 | 6 | // ReSharper disable once InconsistentNaming 7 | public class QueryComponents_Frent : QueryComponents 8 | { 9 | private World world; 10 | private Query query1; 11 | private Query query5; 12 | 13 | [GlobalSetup] 14 | public void Setup() { 15 | world = new World(); 16 | world.CreateEntities(Entities).AddComponents(); 17 | query1 = world.Query>(); 18 | query5 = world.Query, With, With, With, With>(); 19 | Check.AreEqual(Entities, world.EntityCount); 20 | } 21 | 22 | [GlobalCleanup] 23 | public void Shutdown() { 24 | world.Dispose(); 25 | } 26 | 27 | protected override void Run1Component() 28 | { 29 | query1.Inline(default); 30 | } 31 | 32 | protected override void Run5Components() 33 | { 34 | query5.Inline(default); 35 | } 36 | 37 | internal struct Increment : IAction, IAction 38 | { 39 | public void Run(ref Component1 arg) => arg.Value++; 40 | public void Run(ref Component1 arg1, ref Component2 arg2, ref Component3 arg3, ref Component4 arg4, ref Component5 arg5) => 41 | arg1.Value = arg2.Value + arg3.Value + arg4.Value + arg5.Value; 42 | } 43 | } -------------------------------------------------------------------------------- /src/Frent/QueryFragmented.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using Frent.Systems; 3 | using Microsoft.Diagnostics.Tracing.Parsers.Kernel; 4 | using System.Numerics; 5 | using System.Runtime.CompilerServices; 6 | using System.Runtime.InteropServices; 7 | 8 | namespace Frent; 9 | 10 | // ReSharper disable once InconsistentNaming 11 | public class QueryFragmented_Frent : QueryFragmented 12 | { 13 | private World world; 14 | private Query query; 15 | 16 | [GlobalSetup] 17 | public void Setup() 18 | { 19 | world = new World(); 20 | var entities = world.CreateEntities(Entities); 21 | query = world.Query>(); 22 | 23 | for (int n = 0; n < Entities; n++) { 24 | var entity = entities[n]; 25 | entity.Add(new Component1()); 26 | if ((n & 1) != 0) entity.Add(new Component2()); 27 | if ((n & 2) != 0) entity.Add(new Component3()); 28 | if ((n & 4) != 0) entity.Add(new Component4()); 29 | if ((n & 8) != 0) entity.Add(new Component5()); 30 | } 31 | Check.AreEqual(Entities, world.EntityCount); 32 | } 33 | 34 | 35 | [GlobalCleanup] 36 | public void Shutdown() 37 | { 38 | world.Dispose(); 39 | } 40 | 41 | [Benchmark] 42 | public override void Run() 43 | { 44 | query.Inline(default); 45 | } 46 | 47 | internal struct Increment : IAction 48 | { 49 | public void Run(ref Component1 arg) => arg.Value++; 50 | } 51 | } -------------------------------------------------------------------------------- /src/Frent/_BenchUtils.cs: -------------------------------------------------------------------------------- 1 | namespace Frent; 2 | 3 | public static class BenchUtils 4 | { 5 | public static Entity[] CreateEntities (this World world, int count) 6 | { 7 | var entities = new Entity[count]; 8 | for (int n = 0; n < count; n++) { 9 | entities[n] = world.Create(); 10 | } 11 | return entities; 12 | } 13 | 14 | public static Entity[] AddComponents(this Entity[] entities) 15 | { 16 | foreach (var entity in entities) 17 | { 18 | entity.Add(default); 19 | entity.Add(default); 20 | entity.Add(default); 21 | entity.Add(default); 22 | entity.Add(default); 23 | } 24 | return entities; 25 | } 26 | } -------------------------------------------------------------------------------- /src/Frent/_Components.cs: -------------------------------------------------------------------------------- 1 | namespace Frent; 2 | 3 | internal record struct Component1(int Value); 4 | 5 | internal record struct Component2(int Value); 6 | 7 | internal record struct Component3(int Value); 8 | 9 | internal record struct Component4(int Value); 10 | 11 | internal record struct Component5(int Value); -------------------------------------------------------------------------------- /src/Friflo.Engine.ECS/AddRemoveComponents.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace Friflo.Engine.ECS; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class AddRemoveComponents_Friflo : AddRemoveComponents 7 | { 8 | private Entity[] entities; 9 | 10 | [GlobalSetup] 11 | public void Setup() 12 | { 13 | var world = new EntityStore(); 14 | entities = world.CreateEntities(Entities); 15 | } 16 | 17 | [Benchmark(Baseline = true)] 18 | public override void Run() => base.Run(); 19 | 20 | protected override void Run1Component() 21 | { 22 | foreach (var entity in entities) { 23 | entity.AddComponent(new Component1()); 24 | } 25 | foreach (var entity in entities) { 26 | entity.RemoveComponent(); 27 | } 28 | } 29 | 30 | protected override void Run5Components() 31 | { 32 | foreach (var entity in entities) { 33 | entity.Add(new Component1(), new Component2(), new Component3(), new Component4(), new Component5()); 34 | } 35 | foreach (var entity in entities) { 36 | entity.Remove(); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /src/Friflo.Engine.ECS/AddRemoveLinks.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace Friflo.Engine.ECS; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class AddRemoveLinks_Friflo : AddRemoveLinks 7 | { 8 | private Entity[] sources; 9 | private Entity[] targets; 10 | 11 | [GlobalSetup] 12 | public void Setup() 13 | { 14 | var world = new EntityStore(); 15 | sources = world.CreateEntities(Entities).AddComponents(); 16 | targets = world.CreateEntities(Relations).AddComponents(); 17 | } 18 | 19 | [Benchmark(Baseline = true)] 20 | public override void Run() 21 | { 22 | foreach (var source in sources) 23 | { 24 | for (int n = 0; n < Relations; n++) { 25 | source.AddRelation(new LinkRelation { Value = n, Target = targets[n] }); 26 | // Assert.AreEqual(n, source.GetRelation(targets[n]).Value); // O(1) 27 | } 28 | // Assert.AreEqual(Relations, source.GetRelations().Length); // O(1) 29 | for (int n = 0; n < Relations; n++) { 30 | source.RemoveRelation(targets[n]); 31 | } 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /src/Friflo.Engine.ECS/AddRemoveRelations.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace Friflo.Engine.ECS; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class AddRemoveRelations_Friflo : AddRemoveRelations 7 | { 8 | private Entity[] entities; 9 | 10 | [GlobalSetup] 11 | public void Setup() 12 | { 13 | var world = new EntityStore(); 14 | entities = world.CreateEntities(Entities).AddComponents(); 15 | } 16 | 17 | [Benchmark(Baseline = true)] 18 | public override void Run() => base.Run(); 19 | 20 | protected override void AddRemove1Relation() 21 | { 22 | foreach (var entity in entities) 23 | { 24 | entity.AddRelation(new Relation(RelationKey.Key1, 1337)); 25 | entity.RemoveRelation(RelationKey.Key1); 26 | } 27 | } 28 | 29 | private static readonly RelationKey[] Keys = Enum.GetValues(typeof(RelationKey)).Cast().ToArray(); 30 | 31 | protected override void AddRemove10Relations() 32 | { 33 | foreach (var entity in entities) 34 | { 35 | foreach (var key in Keys) { 36 | entity.AddRelation(new Relation(key, 1337)); 37 | // Assert.AreEqual(1337, entity.GetRelation(key).Value); // O(1) 38 | } 39 | // Assert.AreEqual(Relations, entity.GetRelations().Length); // O(1) 40 | foreach (var key in Keys) { 41 | entity.RemoveRelation(key); 42 | } 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /src/Friflo.Engine.ECS/ChildEntitiesAddRemove.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace Friflo.Engine.ECS; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class ChildEntitiesAddRemove_Friflo : ChildEntitiesAddRemove 7 | { 8 | private Entity[] parents; 9 | private Entity[][] children; 10 | private int entityCount; 11 | private int childCount; 12 | 13 | [GlobalSetup] 14 | public void Setup() { 15 | var world = new EntityStore(); 16 | entityCount = Entities; 17 | childCount = Constants.ChildCount; 18 | parents = world.CreateEntities(entityCount).AddComponents(); 19 | children = new Entity[entityCount][]; 20 | for (int n = 0; n < entityCount; n++) { 21 | children[n] = world.CreateEntities(childCount).AddComponents(); 22 | } 23 | } 24 | 25 | // according to example: https://github.com/friflo/Friflo.Json.Fliox/wiki/Examples-~-General#child-entities 26 | [Benchmark(Baseline = true)] 27 | public override void Run() 28 | { 29 | for (int n = 0; n < entityCount; n++) { 30 | for (int child = 0; child < childCount; child++) { 31 | parents[n].AddChild(children[n][child]); 32 | } 33 | } 34 | // Assert.AreEqual(entityCount * childCount, CountChildren()); 35 | for (int n = 0; n < entityCount; n++) { 36 | for (int child = childCount - 1; child >= 0; child--) { 37 | parents[n].RemoveChild(children[n][child]); 38 | } 39 | } 40 | } 41 | 42 | // method only for verification 43 | private int CountChildren() 44 | { 45 | int count = 0; 46 | foreach (var parent in parents) { 47 | count += parent.ChildEntities.Count; 48 | } 49 | return count; 50 | } 51 | } -------------------------------------------------------------------------------- /src/Friflo.Engine.ECS/CommandBufferAddRemove.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace Friflo.Engine.ECS; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class CommandBufferAddRemove_Friflo : CommandBufferAddRemove 7 | { 8 | private Entity[] entities; 9 | private CommandBuffer commandBuffer; 10 | 11 | [GlobalSetup] 12 | public void Setup() 13 | { 14 | var world = new EntityStore(); 15 | entities = world.CreateEntities(Entities); 16 | commandBuffer = world.GetCommandBuffer(); 17 | commandBuffer.ReuseBuffer = true; 18 | } 19 | 20 | [Benchmark(Baseline = true)] 21 | public override void Run() 22 | { 23 | var cb = commandBuffer; 24 | foreach (var entity in entities) { 25 | cb.AddComponent(entity.Id, new Component1()); 26 | cb.AddComponent(entity.Id, new Component2()); 27 | } 28 | cb.Playback(); // Apply changes 1 29 | 30 | foreach (var entity in entities) { 31 | cb.RemoveComponent(entity.Id); 32 | cb.RemoveComponent(entity.Id); 33 | } 34 | cb.Playback(); // Apply changes 2 35 | } 36 | } -------------------------------------------------------------------------------- /src/Friflo.Engine.ECS/ComponentEvents.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace Friflo.Engine.ECS; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class ComponentEvents_Friflo : ComponentEvents 7 | { 8 | private Entity[] entities; 9 | private int iterations; 10 | private int added; 11 | private int removed; 12 | 13 | [GlobalSetup] 14 | public void Setup() 15 | { 16 | var world = new EntityStore(); 17 | entities = world.CreateEntities(Constants.EventCount); 18 | world.OnComponentRemoved += _ => { removed++; }; 19 | world.OnComponentAdded += _ => { added++; }; 20 | } 21 | 22 | [GlobalCleanup] 23 | public void Shutdown() { 24 | var expect = iterations * Constants.EventCount; 25 | Check.AreEqual(expect, added); 26 | Check.AreEqual(expect, removed); 27 | } 28 | 29 | [Benchmark(Baseline = true)] 30 | public override void Run() 31 | { 32 | iterations++; 33 | foreach (var entity in entities) { 34 | entity.AddComponent(new Component1()); 35 | entity.RemoveComponent(); 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /src/Friflo.Engine.ECS/CreateBulk.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace Friflo.Engine.ECS; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class CreateBulk_Friflo : CreateBulk 7 | { 8 | private EntityStore world; 9 | 10 | [IterationSetup] 11 | public void Setup() 12 | { 13 | world = new EntityStore(); 14 | } 15 | 16 | [IterationCleanup] 17 | public void Shutdown() 18 | { 19 | world = null; 20 | } 21 | 22 | [Benchmark(Baseline = true)] 23 | public override void Run() => base.Run(); 24 | 25 | protected override void CreateEntity1Component() 26 | { 27 | var archetype1 = world.GetArchetype(ComponentTypes.Get()); 28 | var entities = archetype1.CreateEntities(Entities); 29 | for (int n = 0; n < entities.Count; n++) { 30 | var data = entities[n].Data; 31 | data.Get().Value = n; 32 | } 33 | } 34 | 35 | protected override void CreateEntity3Components() 36 | { 37 | var archetype3 = world.GetArchetype(ComponentTypes.Get()); 38 | var entities = archetype3.CreateEntities(Entities); 39 | for (int n = 0; n < entities.Count; n++) { 40 | var data = entities[n].Data; 41 | data.Get().Value = n; 42 | data.Get().Value = n; 43 | data.Get().Value = n; 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /src/Friflo.Engine.ECS/CreateEntity.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace Friflo.Engine.ECS; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class CreateEntity_Friflo : CreateEntity 7 | { 8 | private EntityStore world; 9 | 10 | [IterationSetup] 11 | public void Setup() 12 | { 13 | world = new EntityStore(); 14 | } 15 | 16 | [IterationCleanup] 17 | public void Shutdown() 18 | { 19 | world = null; 20 | } 21 | 22 | [Benchmark(Baseline = true)] 23 | public override void Run() => base.Run(); 24 | 25 | protected override void CreateEntity1Component() 26 | { 27 | for (int n = 0; n < Entities; n++) { 28 | world.CreateEntity(new Component1 { Value = n }); 29 | } 30 | } 31 | 32 | protected override void CreateEntity3Components() 33 | { 34 | for (int n = 0; n < Entities; n++) { 35 | world.CreateEntity( 36 | new Component1 { Value = n }, 37 | new Component2 { Value = n }, 38 | new Component3 { Value = n }); 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /src/Friflo.Engine.ECS/CreateWorld.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace Friflo.Engine.ECS; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class CreateWorld_Friflo : CreateWorld 7 | { 8 | [Benchmark(Baseline = true)] 9 | public override void Run() 10 | { 11 | _ = new EntityStore(); 12 | // nothing to Dispose() - has no unmanaged resources 13 | } 14 | } -------------------------------------------------------------------------------- /src/Friflo.Engine.ECS/DeleteEntity.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace Friflo.Engine.ECS; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class DeleteEntity_Friflo : DeleteEntity 7 | { 8 | private EntityStore world; 9 | private Entity[] entities; 10 | 11 | [IterationSetup] 12 | public void Setup() 13 | { 14 | world = new EntityStore { RecycleIds = false }; 15 | entities = world.CreateEntities(Entities).AddComponents(); 16 | } 17 | 18 | [IterationCleanup] 19 | public void Shutdown() 20 | { 21 | world = null; 22 | } 23 | 24 | [Benchmark(Baseline = true)] 25 | public override void Run() 26 | { 27 | foreach (var entity in entities) { 28 | entity.DeleteEntity(); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/Friflo.Engine.ECS/GetSetComponents.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace Friflo.Engine.ECS; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class GetSetComponents_Friflo : GetSetComponents 7 | { 8 | private Entity[] entities; 9 | 10 | [GlobalSetup] 11 | public void Setup() 12 | { 13 | var world = new EntityStore(); 14 | entities = world.CreateEntities(Entities).AddComponents(); 15 | } 16 | 17 | [Benchmark(Baseline = true)] 18 | public override void Run() => base.Run(); 19 | 20 | protected override void Run1Component() 21 | { 22 | foreach (var entity in entities) { 23 | entity.GetComponent() = new Component1(); 24 | } 25 | } 26 | 27 | protected override void Run5Components() 28 | { 29 | foreach (var entity in entities) { 30 | var data = entity.Data; 31 | data.Get() = new Component1(); 32 | data.Get() = new Component2(); 33 | data.Get() = new Component3(); 34 | data.Get() = new Component4(); 35 | data.Get() = new Component5(); 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /src/Friflo.Engine.ECS/QueryComponents.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace Friflo.Engine.ECS; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class QueryComponents_Friflo : QueryComponents 7 | { 8 | private ArchetypeQuery query1; 9 | private ArchetypeQuery query5; 10 | 11 | [GlobalSetup] 12 | public void Setup() 13 | { 14 | var world = new EntityStore(); 15 | world.CreateEntities(Entities).AddComponents(); 16 | query1 = world.Query(); 17 | query5 = world.Query(); 18 | Check.AreEqual(Entities, query5.Count); 19 | } 20 | 21 | [Benchmark(Baseline = true)] 22 | public override void Run() => base.Run(); 23 | 24 | protected override void Run1Component() 25 | { 26 | foreach (var (components, _) in query1.Chunks) { 27 | foreach (ref var component in components.Span) { 28 | component.Value++; 29 | } 30 | } 31 | } 32 | 33 | protected override void Run5Components() 34 | { 35 | query5.Each(new Each5()); 36 | } 37 | } 38 | 39 | internal readonly struct Each5 : IEach 40 | { 41 | public void Execute(ref Component1 c1, ref Component2 c2, ref Component3 c3, ref Component4 c4, ref Component5 c5) { 42 | c1.Value = c2.Value + c3.Value + c4.Value + c5.Value; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Friflo.Engine.ECS/QueryFragmented.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace Friflo.Engine.ECS; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class QueryFragmented_Friflo : QueryFragmented 7 | { 8 | private ArchetypeQuery query; 9 | 10 | [GlobalSetup] 11 | public void Setup() 12 | { 13 | var world = new EntityStore(); 14 | var entities = world.CreateEntities(Entities); 15 | query = world.Query(); 16 | for (int n = 0; n < Entities; n++) { 17 | var entity = entities[n]; 18 | entity.AddComponent(); 19 | if ((n & 1) != 0) entity.AddComponent(); 20 | if ((n & 2) != 0) entity.AddComponent(); 21 | if ((n & 4) != 0) entity.AddComponent(); 22 | if ((n & 8) != 0) entity.AddComponent(); 23 | } 24 | Check.AreEqual(Entities, query.Count); 25 | } 26 | 27 | [Benchmark(Baseline = true)] 28 | public override void Run() 29 | { 30 | foreach (var (components, _) in query.Chunks) { 31 | foreach (ref var component in components.Span) { 32 | component.Value++; 33 | } 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /src/Friflo.Engine.ECS/SearchComponentField.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace Friflo.Engine.ECS; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class SearchComponentField_Friflo : SearchComponentField 7 | { 8 | private EntityStore world; 9 | 10 | [GlobalSetup] 11 | public void Setup() 12 | { 13 | world = new EntityStore(); 14 | var entities = world.CreateEntities(Entities); 15 | // add a component to every entity that can be searched 16 | for (int value = 0; value < entities.Length; value++) { 17 | entities[value].AddComponent(new SearchableComponent(value)); 18 | } 19 | } 20 | 21 | [Benchmark(Baseline = true)] 22 | public override void Run() 23 | { 24 | for (int searchValue = 0; searchValue < Constants.SearchCount; searchValue++) 25 | { 26 | var result = world.GetEntitiesWithComponentValue(searchValue); 27 | Check.AreEqual(1, result.Count); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/Friflo.Engine.ECS/SearchRange.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace Friflo.Engine.ECS; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class SearchRange_Friflo : SearchRange 7 | { 8 | private EntityStore world; 9 | 10 | [GlobalSetup] 11 | public void Setup() 12 | { 13 | world = new EntityStore(); 14 | var entities = world.CreateEntities(Entities); 15 | // add a component to every entity that can be searched 16 | for (int value = 0; value < entities.Length; value++) { 17 | entities[value].AddComponent(new SearchableComponent(value)); 18 | } 19 | } 20 | 21 | [Benchmark(Baseline = true)] 22 | public override void Run() 23 | { 24 | for (int searchValue = 0; searchValue < Constants.SearchCount; searchValue++) 25 | { 26 | var min = searchValue * 100; 27 | var max = searchValue * 100 + 99; 28 | var result = world.Query().ValueInRange(min, max); 29 | Check.AreEqual(100, result.Count); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /src/Friflo.Engine.ECS/Tests.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | 3 | namespace Friflo.Engine.ECS; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public static class Tests_Friflo 7 | { 8 | [Fact] 9 | public static void CheckTreeCycles() 10 | { 11 | var world = new EntityStore(); 12 | var entity1 = world.CreateEntity(1); 13 | var entity2 = world.CreateEntity(2); 14 | entity1.AddChild(entity2); 15 | var e = Assert.Throws(() => { 16 | entity2.AddChild(entity1); 17 | }); 18 | Assert.Equal("operation would cause a cycle: 2 -> 1 -> 2", e!.Message); 19 | Assert.True(entity1 == entity2.Parent); 20 | Assert.True(entity1.Parent.IsNull); 21 | } 22 | } -------------------------------------------------------------------------------- /src/Friflo.Engine.ECS/_BenchUtils.cs: -------------------------------------------------------------------------------- 1 | namespace Friflo.Engine.ECS; 2 | 3 | public static class BenchUtils 4 | { 5 | public static Entity[] CreateEntities (this EntityStore world, int count) 6 | { 7 | var entities = new Entity[count]; 8 | for (int n = 0; n < count; n++) { 9 | entities[n] = world.CreateEntity(); 10 | } 11 | return entities; 12 | } 13 | 14 | public static Entity[] AddComponents(this Entity[] entities) 15 | { 16 | foreach (var entity in entities) 17 | { 18 | entity.AddComponent(); 19 | entity.AddComponent(); 20 | entity.AddComponent(); 21 | entity.AddComponent(); 22 | entity.AddComponent(); 23 | } 24 | return entities; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Friflo.Engine.ECS/_Components.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable StructCanBeMadeReadOnly 2 | // ReSharper disable NotAccessedPositionalProperty.Global 3 | namespace Friflo.Engine.ECS; 4 | 5 | #pragma warning disable CS0649 // Field is never assigned to, and will always have its default value 6 | 7 | internal record struct Component1(int Value) : IComponent; 8 | 9 | internal record struct Component2(int Value) : IComponent; 10 | 11 | internal record struct Component3(int Value) : IComponent; 12 | 13 | internal record struct Component4(int Value) : IComponent; 14 | 15 | internal record struct Component5(int Value) : IComponent; 16 | 17 | 18 | internal record struct LinkRelation(int Value, Entity Target) : ILinkRelation 19 | { 20 | public Entity GetRelationKey() => Target; 21 | } 22 | 23 | enum RelationKey : byte 24 | { 25 | Key1, 26 | Key2, 27 | Key3, 28 | Key4, 29 | Key5, 30 | Key6, 31 | Key7, 32 | Key8, 33 | Key9, 34 | Key10, 35 | } 36 | 37 | // --- Friflo specific: discriminator for relations can be any type 38 | // Used an enum to establish similarity to flecs like relations 39 | internal record struct Relation(RelationKey Key, int Value) : IRelationComponent 40 | { 41 | public RelationKey GetRelationKey() => Key; 42 | } 43 | 44 | internal record struct SearchableComponent(int Value) : IIndexedComponent 45 | { 46 | public int GetIndexedValue() => Value; // indexed field 47 | } -------------------------------------------------------------------------------- /src/Leopotam.EcsLite/AddRemoveComponents.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace Leopotam.EcsLite; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class AddRemoveComponents_Leopotam : AddRemoveComponents 7 | { 8 | private EcsWorld world; 9 | private int[] entities; 10 | private EcsPool ecsPoolC1; 11 | private EcsPool ecsPoolC2; 12 | private EcsPool ecsPoolC3; 13 | private EcsPool ecsPoolC4; 14 | private EcsPool ecsPoolC5; 15 | 16 | [GlobalSetup] 17 | public void Setup() { 18 | world = new EcsWorld(); 19 | entities = world.CreateEntities(Entities); 20 | ecsPoolC1 = world.GetPool(); 21 | ecsPoolC2 = world.GetPool(); 22 | ecsPoolC3 = world.GetPool(); 23 | ecsPoolC4 = world.GetPool(); 24 | ecsPoolC5 = world.GetPool(); 25 | } 26 | 27 | [GlobalCleanup] 28 | public void Shutdown() { 29 | world.Destroy(); 30 | } 31 | 32 | protected override void Run1Component() 33 | { 34 | foreach (var entity in entities) { 35 | ecsPoolC1.Add(entity); 36 | } 37 | foreach (var entity in entities) { 38 | ecsPoolC1.Del(entity); 39 | } 40 | } 41 | 42 | protected override void Run5Components() 43 | { 44 | foreach (var entity in entities) { 45 | ecsPoolC1.Add(entity); 46 | ecsPoolC2.Add(entity); 47 | ecsPoolC3.Add(entity); 48 | ecsPoolC4.Add(entity); 49 | ecsPoolC5.Add(entity); 50 | } 51 | foreach (var entity in entities) { 52 | ecsPoolC1.Del(entity); 53 | ecsPoolC2.Del(entity); 54 | ecsPoolC3.Del(entity); 55 | ecsPoolC4.Del(entity); 56 | ecsPoolC5.Del(entity); 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /src/Leopotam.EcsLite/CreateEntity.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace Leopotam.EcsLite; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class CreateEntity_Leopotam : CreateEntity 7 | { 8 | private EcsWorld world; 9 | 10 | [IterationSetup] 11 | public void Setup() 12 | { 13 | world = new EcsWorld(); 14 | } 15 | 16 | [IterationCleanup] 17 | public void Shutdown() 18 | { 19 | world.Destroy(); 20 | } 21 | 22 | protected override void CreateEntity1Component() 23 | { 24 | var ecsPoolC1 = world.GetPool(); 25 | for (int n = 0; n < Entities; n++) { 26 | var entity = world.NewEntity(); 27 | ecsPoolC1.Add(entity).Value = n; 28 | } 29 | } 30 | 31 | protected override void CreateEntity3Components() 32 | { 33 | var ecsPoolC1 = world.GetPool(); 34 | var ecsPoolC2 = world.GetPool(); 35 | var ecsPoolC3 = world.GetPool(); 36 | for (int n = 0; n < Entities; n++) { 37 | var entity = world.NewEntity(); 38 | ecsPoolC1.Add(entity).Value = n; 39 | ecsPoolC2.Add(entity).Value = n; 40 | ecsPoolC3.Add(entity).Value = n; 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /src/Leopotam.EcsLite/CreateWorld.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace Leopotam.EcsLite; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class CreateWorld_Leopotam : CreateWorld 7 | { 8 | [Benchmark] 9 | public override void Run() 10 | { 11 | var world = new EcsWorld(); 12 | world.Destroy(); 13 | } 14 | } -------------------------------------------------------------------------------- /src/Leopotam.EcsLite/DeleteEntity.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace Leopotam.EcsLite; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class DeleteEntity_Leopotam : DeleteEntity 7 | { 8 | private EcsWorld world; 9 | private int[] entities; 10 | 11 | [IterationSetup] 12 | public void Setup() 13 | { 14 | world = new EcsWorld(); 15 | entities = world.CreateEntities(Entities).AddComponents(world); 16 | } 17 | 18 | [IterationCleanup] 19 | public void Shutdown() 20 | { 21 | world.Destroy(); 22 | } 23 | 24 | [Benchmark] 25 | public override void Run() 26 | { 27 | foreach (var entity in entities) { 28 | world.DelEntity(entity); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/Leopotam.EcsLite/GetSetComponents.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace Leopotam.EcsLite; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class GetSetComponents_Leopotam : GetSetComponents 7 | { 8 | private EcsWorld world; 9 | private int[] entities; 10 | private EcsPool ecsPoolC1; 11 | private EcsPool ecsPoolC2; 12 | private EcsPool ecsPoolC3; 13 | private EcsPool ecsPoolC4; 14 | private EcsPool ecsPoolC5; 15 | 16 | [GlobalSetup] 17 | public void Setup() { 18 | world = new EcsWorld(); 19 | entities = world.CreateEntities(Entities).AddComponents(world); 20 | ecsPoolC1 = world.GetPool(); 21 | ecsPoolC2 = world.GetPool(); 22 | ecsPoolC3 = world.GetPool(); 23 | ecsPoolC4 = world.GetPool(); 24 | ecsPoolC5 = world.GetPool(); 25 | } 26 | 27 | [GlobalCleanup] 28 | public void Shutdown() { 29 | world.Destroy(); 30 | } 31 | 32 | protected override void Run1Component() 33 | { 34 | foreach (var entity in entities) { 35 | ecsPoolC1.Get(entity) = new Component1(); 36 | } 37 | } 38 | 39 | protected override void Run5Components() 40 | { 41 | foreach (var entity in entities) { 42 | ecsPoolC1.Get(entity) = new Component1(); 43 | ecsPoolC2.Get(entity) = new Component2(); 44 | ecsPoolC3.Get(entity) = new Component3(); 45 | ecsPoolC4.Get(entity) = new Component4(); 46 | ecsPoolC5.Get(entity) = new Component5(); 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /src/Leopotam.EcsLite/QueryComponents.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace Leopotam.EcsLite; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class QueryComponents_Leopotam : QueryComponents 7 | { 8 | private EcsWorld world; 9 | private EcsFilter filter1; 10 | private EcsFilter filter5; 11 | private EcsPool c1; 12 | private EcsPool c2; 13 | private EcsPool c3; 14 | private EcsPool c4; 15 | private EcsPool c5; 16 | 17 | [GlobalSetup] 18 | public void Setup() { 19 | world = new EcsWorld(); 20 | world.CreateEntities(Entities).AddComponents(world); 21 | c1 = world.GetPool(); 22 | c2 = world.GetPool(); 23 | c3 = world.GetPool(); 24 | c4 = world.GetPool(); 25 | c5 = world.GetPool(); 26 | filter1 = world.Filter().End(); 27 | filter5 = world.Filter().Inc().Inc().Inc().Inc().End(); 28 | Check.AreEqual(Entities, filter5.GetEntitiesCount()); 29 | } 30 | 31 | [GlobalCleanup] 32 | public void Shutdown() { 33 | world.Destroy(); 34 | } 35 | 36 | protected override void Run1Component() 37 | { 38 | int[] entities = filter1.GetRawEntities(); 39 | for (int i = 0, iMax = filter1.GetEntitiesCount(); i < iMax; i++) { 40 | ++c1.Get(entities[i]).Value; 41 | } 42 | } 43 | 44 | protected override void Run5Components() 45 | { 46 | int[] entities = filter5.GetRawEntities(); 47 | for (int i = 0, iMax = filter5.GetEntitiesCount(); i < iMax; i++) 48 | { 49 | var entity = entities[i]; 50 | c1.Get(entity).Value = 51 | c2.Get(entity).Value + 52 | c3.Get(entity).Value + 53 | c4.Get(entity).Value + 54 | c5.Get(entity).Value; 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /src/Leopotam.EcsLite/QueryFragmented.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace Leopotam.EcsLite; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class QueryFragmented_Leopotam : QueryFragmented 7 | { 8 | private EcsWorld world; 9 | private EcsFilter filter; 10 | private EcsPool c1; 11 | 12 | [GlobalSetup] 13 | public void Setup() 14 | { 15 | world = new EcsWorld(); 16 | var entities = world.CreateEntities(Entities); 17 | c1 = world.GetPool(); 18 | var c2 = world.GetPool(); 19 | var c3 = world.GetPool(); 20 | var c4 = world.GetPool(); 21 | var c5 = world.GetPool(); 22 | filter = world.Filter().End(); 23 | for (int n = 0; n < Entities; n++) { 24 | var entity = entities[n]; 25 | c1.Add(entity); 26 | if ((n & 1) != 0) c2.Add(entity); 27 | if ((n & 2) != 0) c3.Add(entity); 28 | if ((n & 4) != 0) c4.Add(entity); 29 | if ((n & 8) != 0) c5.Add(entity); 30 | } 31 | Check.AreEqual(Entities, filter.GetEntitiesCount()); 32 | } 33 | 34 | [GlobalCleanup] 35 | public void Shutdown() 36 | { 37 | world.Destroy(); 38 | } 39 | 40 | [Benchmark] 41 | public override void Run() 42 | { 43 | int[] entities = filter.GetRawEntities(); 44 | for (int i = 0, iMax = filter.GetEntitiesCount(); i < iMax; i++) { 45 | ++c1.Get(entities[i]).Value; 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /src/Leopotam.EcsLite/_BenchUtils.cs: -------------------------------------------------------------------------------- 1 | namespace Leopotam.EcsLite; 2 | 3 | public static class BenchUtils 4 | { 5 | public static int[] CreateEntities (this EcsWorld world, int count) 6 | { 7 | var entities = new int[count]; 8 | for (int n = 0; n < count; n++) { 9 | entities[n] = world.NewEntity(); 10 | } 11 | return entities; 12 | } 13 | 14 | public static int[] AddComponents(this int[] entities, EcsWorld world) 15 | { 16 | EcsPool c1 = world.GetPool(); 17 | EcsPool c2 = world.GetPool(); 18 | EcsPool c3 = world.GetPool(); 19 | EcsPool c4 = world.GetPool(); 20 | EcsPool c5 = world.GetPool(); 21 | 22 | foreach (var entity in entities) 23 | { 24 | c1.Add(entity); 25 | c2.Add(entity); 26 | c3.Add(entity); 27 | c4.Add(entity); 28 | c5.Add(entity); 29 | } 30 | return entities; 31 | } 32 | } -------------------------------------------------------------------------------- /src/Leopotam.EcsLite/_Components.cs: -------------------------------------------------------------------------------- 1 | namespace Leopotam.EcsLite; 2 | 3 | #pragma warning disable CS0649 // Field is never assigned to, and will always have its default value 4 | 5 | public record struct Component1(int Value); 6 | 7 | public record struct Component2(int Value); 8 | 9 | public record struct Component3(int Value); 10 | 11 | public record struct Component4(int Value); 12 | 13 | public record struct Component5(int Value); 14 | -------------------------------------------------------------------------------- /src/Myriad/AddRemoveComponents.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using Myriad.ECS; 3 | using Myriad.ECS.Command; 4 | using Myriad.ECS.Worlds; 5 | 6 | namespace Myriad; 7 | 8 | // ReSharper disable once InconsistentNaming 9 | public class AddRemoveComponents_Myriad : AddRemoveComponents 10 | { 11 | private World world; 12 | private Entity[] entities; 13 | private CommandBuffer cmd; 14 | 15 | [GlobalSetup] 16 | public void Setup() 17 | { 18 | world = new WorldBuilder().Build(); 19 | 20 | entities = world.CreateEntities(Entities); 21 | 22 | cmd = new CommandBuffer(world); 23 | } 24 | 25 | [GlobalCleanup] 26 | public void Shutdown() 27 | { 28 | world.Dispose(); 29 | } 30 | 31 | protected override void Run1Component() 32 | { 33 | foreach (var entity in entities) 34 | { 35 | cmd.Set(entity, new Component1()); 36 | } 37 | cmd.Playback().Dispose(); 38 | 39 | foreach (var entity in entities) 40 | { 41 | cmd.Remove(entity); 42 | } 43 | cmd.Playback().Dispose(); 44 | } 45 | 46 | protected override void Run5Components() 47 | { 48 | foreach (var entity in entities) 49 | { 50 | cmd.Set(entity, new Component1()); 51 | cmd.Set(entity, new Component2()); 52 | cmd.Set(entity, new Component3()); 53 | cmd.Set(entity, new Component4()); 54 | cmd.Set(entity, new Component5()); 55 | } 56 | cmd.Playback().Dispose(); 57 | 58 | foreach (var entity in entities) 59 | { 60 | cmd.Remove(entity); 61 | cmd.Remove(entity); 62 | cmd.Remove(entity); 63 | cmd.Remove(entity); 64 | cmd.Remove(entity); 65 | } 66 | cmd.Playback().Dispose(); 67 | } 68 | } -------------------------------------------------------------------------------- /src/Myriad/CommandBufferAddRemove.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using Myriad.ECS; 3 | using Myriad.ECS.Command; 4 | using Myriad.ECS.Worlds; 5 | 6 | namespace Myriad; 7 | 8 | // ReSharper disable once InconsistentNaming 9 | public class CommandBufferAddRemove_Myriad : CommandBufferAddRemove 10 | { 11 | private World world; 12 | private Entity[] entities; 13 | private CommandBuffer cmd; 14 | 15 | [GlobalSetup] 16 | public void Setup() 17 | { 18 | world = new WorldBuilder().Build(); 19 | 20 | entities = world.CreateEntities(Entities); 21 | 22 | cmd = new CommandBuffer(world); 23 | } 24 | 25 | [GlobalCleanup] 26 | public void Shutdown() 27 | { 28 | world.Dispose(); 29 | } 30 | 31 | [Benchmark] 32 | public override void Run() 33 | { 34 | foreach (var entity in entities) 35 | { 36 | cmd.Set(entity, new Component1()); 37 | cmd.Set(entity, new Component2()); 38 | } 39 | cmd.Playback().Dispose(); 40 | 41 | foreach (var entity in entities) 42 | { 43 | cmd.Remove(entity); 44 | cmd.Remove(entity); 45 | } 46 | cmd.Playback().Dispose(); 47 | } 48 | } -------------------------------------------------------------------------------- /src/Myriad/CreateEntity.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using Myriad.ECS.Command; 3 | using Myriad.ECS.Worlds; 4 | 5 | namespace Myriad; 6 | 7 | // ReSharper disable once InconsistentNaming 8 | public class CreateEntity_Myriad : CreateEntity 9 | { 10 | private World world; 11 | private CommandBuffer buffer; 12 | 13 | [IterationSetup] 14 | public void Setup() 15 | { 16 | world = new WorldBuilder().Build(); 17 | buffer = new CommandBuffer(world); 18 | } 19 | 20 | [IterationCleanup] 21 | public void Shutdown() 22 | { 23 | world.Dispose(); 24 | } 25 | 26 | protected override void CreateEntity1Component() 27 | { 28 | for (var n = 0; n < Entities; n++) 29 | buffer.Create().Set(new Component1(n)); 30 | 31 | buffer.Playback().Dispose(); 32 | } 33 | 34 | protected override void CreateEntity3Components() 35 | { 36 | for (var n = 0; n < Entities; n++) 37 | { 38 | buffer.Create() 39 | .Set(new Component1(n)) 40 | .Set(new Component2(n)) 41 | .Set(new Component3(n)); 42 | } 43 | 44 | buffer.Playback().Dispose(); 45 | } 46 | } -------------------------------------------------------------------------------- /src/Myriad/CreateWorld.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using Myriad.ECS.Worlds; 3 | 4 | namespace Myriad; 5 | 6 | // ReSharper disable once InconsistentNaming 7 | public class CreateWorld_Myriad : CreateWorld 8 | { 9 | [Benchmark] 10 | public override void Run() 11 | { 12 | var world = new WorldBuilder().Build(); 13 | world.Dispose(); 14 | } 15 | } -------------------------------------------------------------------------------- /src/Myriad/DeleteEntity.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using Myriad.ECS; 3 | using Myriad.ECS.Command; 4 | using Myriad.ECS.Worlds; 5 | 6 | namespace Myriad; 7 | 8 | // ReSharper disable once InconsistentNaming 9 | public class DeleteEntity_Myriad : DeleteEntity 10 | { 11 | private World world; 12 | private Entity[] entities; 13 | private CommandBuffer cmd; 14 | 15 | [IterationSetup] 16 | public void Setup() 17 | { 18 | world = new WorldBuilder().Build(); 19 | entities = world.CreateEntities(Entities); 20 | entities.AddComponents(world); 21 | cmd = new CommandBuffer(world); 22 | } 23 | 24 | [IterationCleanup] 25 | public void Shutdown() 26 | { 27 | world.Dispose(); 28 | } 29 | 30 | [Benchmark] 31 | public override void Run() 32 | { 33 | foreach (var entity in entities) { 34 | cmd.Delete(entity); 35 | } 36 | cmd.Playback(); 37 | } 38 | } -------------------------------------------------------------------------------- /src/Myriad/GetSetComponents.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using Myriad.ECS; 3 | using Myriad.ECS.Worlds; 4 | 5 | namespace Myriad; 6 | 7 | // ReSharper disable once InconsistentNaming 8 | public class GetSetComponents_Myriad : GetSetComponents 9 | { 10 | private World world; 11 | private Entity[] entities; 12 | 13 | [GlobalSetup] 14 | public void Setup() 15 | { 16 | world = new WorldBuilder().Build(); 17 | 18 | entities = world.CreateEntities(Entities); 19 | entities.AddComponents(world); 20 | } 21 | 22 | [GlobalCleanup] 23 | public void Shutdown() 24 | { 25 | world.Dispose(); 26 | } 27 | 28 | protected override void Run1Component() 29 | { 30 | foreach (var entity in entities) 31 | entity.GetComponentRef() = new Component1(); 32 | } 33 | 34 | protected override void Run5Components() 35 | { 36 | foreach (var entity in entities) { 37 | entity.GetComponentRef() = new Component1(); 38 | entity.GetComponentRef() = new Component2(); 39 | entity.GetComponentRef() = new Component3(); 40 | entity.GetComponentRef() = new Component4(); 41 | entity.GetComponentRef() = new Component5(); 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /src/Myriad/QueryComponents.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using Myriad.ECS; 3 | using Myriad.ECS.Queries; 4 | using Myriad.ECS.Worlds; 5 | 6 | namespace Myriad; 7 | 8 | // ReSharper disable once InconsistentNaming 9 | public class QueryComponents_Myriad : QueryComponents 10 | { 11 | private World world; 12 | private QueryDescription query1; 13 | private QueryDescription query5; 14 | 15 | [GlobalSetup] 16 | public void Setup() 17 | { 18 | world = new WorldBuilder().Build(); 19 | 20 | var entities = world.CreateEntities(Entities); 21 | entities.AddComponents(world); 22 | 23 | query1 = new QueryBuilder().Include().Build(world); 24 | query5 = new QueryBuilder().Include().Build(world); 25 | if (query5.Count() != Entities) 26 | throw new Exception("Setup failed to create correct number of entities (Myriad bug?)"); 27 | } 28 | 29 | [GlobalCleanup] 30 | public void Shutdown() 31 | { 32 | world.Dispose(); 33 | } 34 | 35 | protected override void Run1Component() 36 | { 37 | world.Execute(query1); 38 | } 39 | 40 | protected override void Run5Components() 41 | { 42 | world.Execute(query5); 43 | } 44 | } 45 | 46 | internal readonly struct IncrementComponent1 47 | : IQuery 48 | { 49 | public void Execute(Entity e, ref Component1 t0) 50 | { 51 | t0.Value++; 52 | } 53 | } 54 | 55 | internal readonly struct Add5Components 56 | : IQuery 57 | { 58 | public void Execute(Entity e, ref Component1 c1, ref Component2 c2, ref Component3 c3, ref Component4 c4, ref Component5 c5) 59 | { 60 | c1.Value = c2.Value + c3.Value + c4.Value + c5.Value; 61 | } 62 | } -------------------------------------------------------------------------------- /src/Myriad/QueryFragmented.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using Myriad.ECS; 3 | using Myriad.ECS.Command; 4 | using Myriad.ECS.Queries; 5 | using Myriad.ECS.Worlds; 6 | 7 | namespace Myriad; 8 | 9 | // ReSharper disable once InconsistentNaming 10 | public class QueryFragmented_Myriad : QueryFragmented 11 | { 12 | private World world; 13 | private QueryDescription query; 14 | 15 | [GlobalSetup] 16 | public void Setup() 17 | { 18 | world = new WorldBuilder().Build(); 19 | 20 | // Create all the entities using a command buffer 21 | var cmd = new CommandBuffer(world); 22 | for (var i = 0; i < Entities; i++) 23 | { 24 | var entity = cmd.Create(); 25 | 26 | entity.Set(new Component1()); 27 | 28 | if ((i & 1) != 0) entity.Set(default); 29 | if ((i & 2) != 0) entity.Set(default); 30 | if ((i & 4) != 0) entity.Set(default); 31 | if ((i & 8) != 0) entity.Set(default); 32 | } 33 | 34 | cmd.Playback().Dispose(); 35 | 36 | query = new QueryBuilder().Include().Build(world); 37 | if (query.Count() != Entities) 38 | throw new Exception("Setup failed to create correct number of entities (Myriad bug?)"); 39 | } 40 | 41 | [GlobalCleanup] 42 | public void Shutdown() 43 | { 44 | world.Dispose(); 45 | } 46 | 47 | [Benchmark] 48 | public override void Run() 49 | { 50 | world.Execute(query); 51 | } 52 | 53 | private readonly struct IncrementComponent1 54 | : IQuery 55 | { 56 | public void Execute(Entity e, ref Component1 t0) 57 | { 58 | t0.Value++; 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /src/Myriad/_BenchUtils.cs: -------------------------------------------------------------------------------- 1 | using Myriad.ECS; 2 | using Myriad.ECS.Command; 3 | using Myriad.ECS.Worlds; 4 | 5 | namespace Myriad; 6 | 7 | public static class BenchUtils 8 | { 9 | public static Entity[] CreateEntities(this World world, int count) 10 | { 11 | var buffer = new CommandBuffer(world); 12 | for (var n = 0; n < count; n++) 13 | buffer.Create(); 14 | 15 | using var resolver = buffer.Playback(); 16 | var result = new Entity[count]; 17 | for (var i = 0; i < resolver.Count; i++) 18 | result[i] = resolver[i]; 19 | 20 | return result; 21 | } 22 | 23 | public static void AddComponents(this Entity[] entities, World world) 24 | { 25 | var buffer = new CommandBuffer(world); 26 | 27 | foreach (var entity in entities) 28 | { 29 | buffer.Set(entity, new Component1()); 30 | buffer.Set(entity, new Component2()); 31 | buffer.Set(entity, new Component3()); 32 | buffer.Set(entity, new Component4()); 33 | buffer.Set(entity, new Component5()); 34 | } 35 | 36 | buffer.Playback().Dispose(); 37 | } 38 | } -------------------------------------------------------------------------------- /src/Myriad/_Components.cs: -------------------------------------------------------------------------------- 1 | 2 | using Myriad.ECS; 3 | 4 | namespace Myriad; 5 | 6 | #pragma warning disable CS0649 // Field is never assigned to, and will always have its default value 7 | 8 | internal record struct Component1(int Value) : IComponent; 9 | 10 | internal record struct Component2(int Value) : IComponent; 11 | 12 | internal record struct Component3(int Value) : IComponent; 13 | 14 | internal record struct Component4(int Value) : IComponent; 15 | 16 | internal record struct Component5(int Value) : IComponent; -------------------------------------------------------------------------------- /src/Program.cs: -------------------------------------------------------------------------------- 1 | // See https://aka.ms/new-console-template for more information 2 | 3 | using System.Reflection; 4 | using BenchmarkDotNet.Configs; 5 | using BenchmarkDotNet.Diagnosers; 6 | using BenchmarkDotNet.Loggers; 7 | using BenchmarkDotNet.Order; 8 | using BenchmarkDotNet.Running; 9 | 10 | var header = 11 | $@" 12 | ECS.CSharp.Benchmark Common use-cases 13 | ------------------------------------- 14 | {nameof(Constants.EntityCount)}: {Constants.EntityCount} 15 | {nameof(Constants.CreateEntityCount)}: {Constants.CreateEntityCount} 16 | {nameof(Constants.CreateBulkCount)}: {Constants.CreateBulkCount} 17 | {nameof(Constants.DeleteEntityCount)}: {Constants.DeleteEntityCount} 18 | args: {string.Join(' ', args)} 19 | "; 20 | 21 | ManualConfig customConfig = DefaultConfig.Instance 22 | .WithOption(ConfigOptions.JoinSummary, true) 23 | .WithArtifactsPath(@"../Artifacts") 24 | .WithOrderer(new DefaultOrderer(SummaryOrderPolicy.FastestToSlowest)) 25 | .AddLogicalGroupRules(BenchmarkLogicalGroupRule.ByCategory) 26 | // .WithSummaryStyle(SummaryStyle.Default.WithTimeUnit(TimeUnit.Microsecond)) 27 | .AddDiagnoser(MemoryDiagnoser.Default) // adds column: Allocated 28 | .HideColumns( 29 | "Method", "Error", "StdDev", "Median", // removed to reduce noise 30 | "RatioSD", // added by using: [Benchmark(Baseline = true)] 31 | "InvocationCount", "IterationCount", "UnrollFactor", // added by using: [InvocationCount()] & [IterationCount()] 32 | "Job", "LaunchCount", "WarmupCount", // added by using: [IterationSetup] & [IterationCleanup] 33 | "Gen0", "Gen1", "Gen2", "Alloc Ratio"); // removing last column "Alloc Ratio" makes Markdown table valid 34 | 35 | // ReSharper disable once RedundantAssignment 36 | IConfig config = customConfig; 37 | #if DEBUG 38 | config = new DebugInProcessConfig(); 39 | #endif 40 | foreach (var logger in config.GetLoggers()) { logger.WriteLine(header); } 41 | 42 | BenchmarkSwitcher.FromAssembly(Assembly.GetExecutingAssembly()).Run(args, config); 43 | 44 | 45 | public static class Check 46 | { 47 | public static void AreEqual(int expect, int actual) 48 | { 49 | var isEqual = expect == actual; 50 | if (!isEqual) { 51 | throw new AssertException($"expect: {expect}, was: {actual}"); 52 | } 53 | } 54 | } 55 | 56 | public class AssertException : Exception 57 | { 58 | public AssertException(string message) : base (message) { } 59 | } -------------------------------------------------------------------------------- /src/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "ECS.Benchmark": { 4 | "commandName": "Project", 5 | "dotnetRunMessages": true 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /src/Scellecs.Morpeh/AddRemoveComponents.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace Scellecs.Morpeh; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class AddRemoveComponents_Morpeh : AddRemoveComponents 7 | { 8 | private World world; 9 | private Entity[] entities; 10 | private Access access; 11 | 12 | [GlobalSetup] 13 | public void Setup() { 14 | world = World.Create(); 15 | access = new Access(world); 16 | entities = world.CreateEntities(Entities); 17 | } 18 | 19 | [GlobalCleanup] 20 | public void Shutdown() { 21 | world.Dispose(); 22 | } 23 | 24 | protected override void Run1Component() 25 | { 26 | foreach (var entity in entities) { 27 | access.stash1.Add(entity, new Component1()); 28 | } 29 | foreach (var entity in entities) { 30 | access.stash1.Remove(entity); 31 | } 32 | world.Commit(); 33 | } 34 | 35 | protected override void Run5Components() 36 | { 37 | foreach (var entity in entities) { 38 | access.stash1.Add(entity, new Component1()); 39 | access.stash2.Add(entity, new Component2()); 40 | access.stash3.Add(entity, new Component3()); 41 | access.stash4.Add(entity, new Component4()); 42 | access.stash5.Add(entity, new Component5()); 43 | } 44 | foreach (var entity in entities) { 45 | access.stash1.Remove(entity); 46 | access.stash2.Remove(entity); 47 | access.stash3.Remove(entity); 48 | access.stash4.Remove(entity); 49 | access.stash5.Remove(entity); 50 | } 51 | world.Commit(); 52 | } 53 | } -------------------------------------------------------------------------------- /src/Scellecs.Morpeh/CommandBufferAddRemove.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace Scellecs.Morpeh; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class CommandBufferAddRemove_Morpeh : CommandBufferAddRemove 7 | { 8 | private World world; 9 | private Entity[] entities; 10 | 11 | [GlobalSetup] 12 | public void Setup() { 13 | world = World.Create(); 14 | entities = world.CreateEntities(Entities); 15 | } 16 | 17 | [GlobalCleanup] 18 | public void Shutdown() { 19 | world.Dispose(); 20 | } 21 | 22 | [Benchmark] 23 | public override void Run() 24 | { 25 | foreach (var entity in entities) { 26 | entity.AddComponent(); 27 | entity.AddComponent(); 28 | } 29 | world.Commit(); // Apply changes 1 30 | 31 | foreach (var entity in entities) { 32 | entity.RemoveComponent(); 33 | entity.RemoveComponent(); 34 | } 35 | world.Commit(); // Apply changes 2 36 | } 37 | } -------------------------------------------------------------------------------- /src/Scellecs.Morpeh/CreateEntity.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace Scellecs.Morpeh; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class CreateEntity_Morpeh : CreateEntity 7 | { 8 | private World world; 9 | 10 | [IterationSetup] 11 | public void Setup() 12 | { 13 | world = World.Create(); 14 | } 15 | 16 | [IterationCleanup] 17 | public void Shutdown() 18 | { 19 | world.Dispose(); 20 | } 21 | 22 | protected override void CreateEntity1Component() 23 | { 24 | var stash1 = world.GetStash(); 25 | for (int n = 0; n < Entities; n++) 26 | { 27 | var entity = world.CreateEntity(); 28 | stash1.Add(entity).Value = n; 29 | } 30 | world.Commit(); 31 | } 32 | 33 | protected override void CreateEntity3Components() 34 | { 35 | var stash1 = world.GetStash(); 36 | var stash2 = world.GetStash(); 37 | var stash3 = world.GetStash(); 38 | for (int n = 0; n < Entities; n++) 39 | { 40 | var entity = world.CreateEntity(); 41 | stash1.Add(entity).Value = n; 42 | stash2.Add(entity).Value = n; 43 | stash3.Add(entity).Value = n; 44 | } 45 | world.Commit(); 46 | } 47 | } -------------------------------------------------------------------------------- /src/Scellecs.Morpeh/CreateWorld.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace Scellecs.Morpeh; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class CreateWorld_Morpeh : CreateWorld 7 | { 8 | [Benchmark] 9 | public override void Run() 10 | { 11 | var world = World.Create(); 12 | world.Dispose(); 13 | } 14 | } -------------------------------------------------------------------------------- /src/Scellecs.Morpeh/DeleteEntity.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace Scellecs.Morpeh; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class DeleteEntity_Morpeh : DeleteEntity 7 | { 8 | private World world; 9 | private Entity[] entities; 10 | 11 | [IterationSetup] 12 | public void Setup() 13 | { 14 | world = World.Create(); 15 | entities = world.CreateEntities(Entities).AddComponents(world); 16 | } 17 | 18 | [IterationCleanup] 19 | public void Shutdown() 20 | { 21 | world.Dispose(); 22 | } 23 | 24 | [Benchmark] 25 | public override void Run() 26 | { 27 | foreach (var entity in entities) { 28 | entity.Dispose(); 29 | } 30 | world.Commit(); 31 | } 32 | } -------------------------------------------------------------------------------- /src/Scellecs.Morpeh/GetSetComponents.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace Scellecs.Morpeh; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class GetSetComponents_Morpeh : GetSetComponents 7 | { 8 | private World world; 9 | private Entity[] entities; 10 | private Access access; 11 | 12 | [GlobalSetup] 13 | public void Setup() { 14 | world = World.Create(); 15 | access = new Access(world); 16 | entities = world.CreateEntities(Entities).AddComponents(world); 17 | } 18 | 19 | [GlobalCleanup] 20 | public void Shutdown() { 21 | world.Dispose(); 22 | } 23 | 24 | protected override void Run1Component() 25 | { 26 | foreach (var entity in entities) { 27 | access.stash1.Get(entity) = new Component1(); 28 | } 29 | } 30 | 31 | protected override void Run5Components() 32 | { 33 | foreach (var entity in entities) { 34 | access.stash1.Get(entity) = new Component1(); 35 | access.stash2.Get(entity) = new Component2(); 36 | access.stash3.Get(entity) = new Component3(); 37 | access.stash4.Get(entity) = new Component4(); 38 | access.stash5.Get(entity) = new Component5(); 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /src/Scellecs.Morpeh/QueryComponents.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace Scellecs.Morpeh; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class QueryComponents_Morpeh : QueryComponents 7 | { 8 | private World world; 9 | private StashSystem1 stashSystem1; 10 | private StashSystem5 stashSystem5; 11 | 12 | [GlobalSetup] 13 | public void Setup() { 14 | world = World.Create(); 15 | world.CreateEntities(Entities).AddComponents(world); 16 | 17 | stashSystem1 = new StashSystem1 { World = world }; 18 | stashSystem1.OnAwake(); 19 | 20 | stashSystem5 = new StashSystem5 { World = world }; 21 | stashSystem5.OnAwake(); 22 | } 23 | 24 | [GlobalCleanup] 25 | public void Shutdown() { 26 | world.Dispose(); 27 | } 28 | 29 | protected override void Run1Component() 30 | { 31 | stashSystem1.OnUpdate(0); 32 | } 33 | 34 | protected override void Run5Components() 35 | { 36 | stashSystem5.OnUpdate(0); 37 | } 38 | } 39 | 40 | internal sealed class StashSystem1 : ISystem 41 | { 42 | public World World { get; set; } 43 | private Stash stash1; 44 | private Filter filter; 45 | 46 | public void OnAwake() 47 | { 48 | stash1 = World.GetStash(); 49 | filter = World.Filter.With().Build(); 50 | } 51 | 52 | public void OnUpdate(float deltaTime) 53 | { 54 | foreach (Entity entity in filter) { 55 | ++stash1.Get(entity).Value; 56 | } 57 | } 58 | 59 | public void Dispose() { 60 | stash1.Dispose(); 61 | } 62 | } 63 | 64 | internal sealed class StashSystem5 : ISystem 65 | { 66 | public World World { get; set; } 67 | private Access access; 68 | private Filter filter; 69 | 70 | public void OnAwake() 71 | { 72 | access = new Access(World); 73 | filter = World.Filter.With().With().With().With().With().Build(); 74 | } 75 | 76 | public void OnUpdate(float deltaTime) 77 | { 78 | foreach (Entity entity in filter) { 79 | access.stash1.Get(entity).Value = 80 | access.stash2.Get(entity).Value + 81 | access.stash3.Get(entity).Value + 82 | access.stash4.Get(entity).Value + 83 | access.stash5.Get(entity).Value; 84 | } 85 | } 86 | 87 | public void Dispose() 88 | { 89 | access.Dispose(); 90 | } 91 | } -------------------------------------------------------------------------------- /src/Scellecs.Morpeh/QueryFragmented.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace Scellecs.Morpeh; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class QueryFragmented_Morpeh : QueryFragmented 7 | { 8 | private World world; 9 | private StashSystem stashSystem; 10 | 11 | [GlobalSetup] 12 | public void Setup() 13 | { 14 | world = World.Create(); 15 | var entities = world.CreateEntities(Entities); 16 | stashSystem = new StashSystem { World = world }; 17 | stashSystem.OnAwake(); 18 | for (int n = 0; n < Entities; n++) { 19 | var entity = entities[n]; 20 | entity.AddComponent(); 21 | if ((n & 1) != 0) entity.AddComponent(); 22 | if ((n & 2) != 0) entity.AddComponent(); 23 | if ((n & 4) != 0) entity.AddComponent(); 24 | if ((n & 8) != 0) entity.AddComponent(); 25 | } 26 | } 27 | 28 | [GlobalCleanup] 29 | public void Shutdown() 30 | { 31 | world.Dispose(); 32 | } 33 | 34 | [Benchmark] 35 | public override void Run() 36 | { 37 | stashSystem.OnUpdate(0); 38 | } 39 | 40 | private sealed class StashSystem : ISystem 41 | { 42 | public World World { get; set; } 43 | private Stash stash1; 44 | private Filter filter; 45 | 46 | public void OnAwake() 47 | { 48 | stash1 = World.GetStash(); 49 | filter = World.Filter.With().Build(); 50 | } 51 | 52 | public void OnUpdate(float deltaTime) 53 | { 54 | foreach (Entity entity in filter) { 55 | ++stash1.Get(entity).Value; 56 | } 57 | } 58 | 59 | public void Dispose() { 60 | stash1.Dispose(); 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /src/Scellecs.Morpeh/_Access.cs: -------------------------------------------------------------------------------- 1 | namespace Scellecs.Morpeh; 2 | 3 | public readonly struct Access 4 | { 5 | internal readonly Stash stash1; 6 | internal readonly Stash stash2; 7 | internal readonly Stash stash3; 8 | internal readonly Stash stash4; 9 | internal readonly Stash stash5; 10 | 11 | internal Access(World world) { 12 | stash1 = world.GetStash(); 13 | stash2 = world.GetStash(); 14 | stash3 = world.GetStash(); 15 | stash4 = world.GetStash(); 16 | stash5 = world.GetStash(); 17 | } 18 | 19 | internal void Dispose() 20 | { 21 | stash1.Dispose(); 22 | stash2.Dispose(); 23 | stash3.Dispose(); 24 | stash4.Dispose(); 25 | stash5.Dispose(); 26 | } 27 | } -------------------------------------------------------------------------------- /src/Scellecs.Morpeh/_BenchUtils.cs: -------------------------------------------------------------------------------- 1 | namespace Scellecs.Morpeh; 2 | 3 | public static class BenchUtils 4 | { 5 | public static Entity[] CreateEntities (this World world, int count) 6 | { 7 | var entities = new Entity[count]; 8 | for (int n = 0; n < count; n++) { 9 | var entity = entities[n] = world.CreateEntity(); 10 | entity.AddComponent(); 11 | } 12 | world.Commit(); 13 | return entities; 14 | } 15 | 16 | public static Entity[] AddComponents(this Entity[] entities, World world) 17 | { 18 | foreach (var entity in entities) 19 | { 20 | entity.AddComponent(); 21 | entity.AddComponent(); 22 | entity.AddComponent(); 23 | entity.AddComponent(); 24 | entity.AddComponent(); 25 | } 26 | world.Commit(); 27 | return entities; 28 | } 29 | } -------------------------------------------------------------------------------- /src/Scellecs.Morpeh/_Components.cs: -------------------------------------------------------------------------------- 1 | namespace Scellecs.Morpeh; 2 | 3 | #pragma warning disable CS0649 // Field is never assigned to, and will always have its default value 4 | 5 | internal record struct Component1(int Value) : IComponent; 6 | 7 | internal record struct Component2(int Value) : IComponent; 8 | 9 | internal record struct Component3(int Value) : IComponent; 10 | 11 | internal record struct Component4(int Value) : IComponent; 12 | 13 | internal record struct Component5(int Value) : IComponent; 14 | 15 | /// https://github.com/scellecs/morpeh?tab=readme-ov-file#-entity 16 | /// "if you remove last component on entity it will be destroyed on next world.Commit()" 17 | internal record struct AliveComponent : IComponent; 18 | 19 | -------------------------------------------------------------------------------- /src/TinyEcs/AddRemoveComponents.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace TinyEcs; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class AddRemoveComponents_TinyEcs : AddRemoveComponents 7 | { 8 | private World world; 9 | private EntityView[] entities; 10 | 11 | [GlobalSetup] 12 | public void Setup() { 13 | world = new World(); 14 | entities = world.CreateEntities(Entities); 15 | } 16 | 17 | [GlobalCleanup] 18 | public void Shutdown() { 19 | world.Dispose(); 20 | } 21 | 22 | protected override void Run1Component() 23 | { 24 | foreach (var entity in entities) { 25 | entity.Set(new Component1()); 26 | } 27 | foreach (var entity in entities) { 28 | entity.Unset(); 29 | } 30 | } 31 | 32 | protected override void Run5Components() 33 | { 34 | foreach (var entity in entities) { 35 | entity.Set(new Component1()); 36 | entity.Set(new Component2()); 37 | entity.Set(new Component3()); 38 | entity.Set(new Component4()); 39 | entity.Set(new Component5()); 40 | } 41 | foreach (var entity in entities) { 42 | entity.Unset(); 43 | entity.Unset(); 44 | entity.Unset(); 45 | entity.Unset(); 46 | entity.Unset(); 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /src/TinyEcs/AddRemoveLinks.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace TinyEcs; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class AddRemoveLinks_TinyEcs : AddRemoveLinks 7 | { 8 | private World world; 9 | private EntityView[] sources; 10 | private EntityView[] targets; 11 | private EntityView[] relations; 12 | 13 | [GlobalSetup] 14 | public void Setup() 15 | { 16 | world = new World(); 17 | sources = world.CreateEntities(Entities).AddComponents(); 18 | targets = world.CreateEntities(Relations).AddComponents(); 19 | relations = world.CreateEntities(Relations); 20 | foreach (var relation in relations) { 21 | relation.Add(); // add a component with data to every relation entity 22 | } 23 | } 24 | 25 | [GlobalCleanup] 26 | public void Shutdown() 27 | { 28 | world.Dispose(); 29 | } 30 | 31 | [Benchmark] 32 | public override void Run() 33 | { 34 | foreach (var source in sources) 35 | { 36 | for (int n = 0; n < Relations; n++) { 37 | source.Add(relations[n], targets[n]); 38 | } 39 | for (int n = 0; n < Relations; n++) { 40 | source.Unset(relations[n], targets[n]); 41 | } 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /src/TinyEcs/AddRemoveRelations.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace TinyEcs; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class AddRemoveRelations_TinyEcs : AddRemoveRelations 7 | { 8 | private World world; 9 | private EntityView[] entities; 10 | 11 | [GlobalSetup] 12 | public void Setup() { 13 | world = new World(); 14 | entities = world.CreateEntities(Entities).AddComponents(); 15 | } 16 | 17 | [GlobalCleanup] 18 | public void Shutdown() { 19 | world.Dispose(); 20 | } 21 | 22 | protected override void AddRemove1Relation() 23 | { 24 | foreach (var entity in entities) 25 | { 26 | entity.Set(new Relation(1337)); 27 | entity.Unset(); 28 | } 29 | } 30 | 31 | protected override void AddRemove10Relations() 32 | { 33 | foreach (var entity in entities) 34 | { 35 | entity.Set(new Relation(1337)); 36 | entity.Set(new Relation(1337)); 37 | entity.Set(new Relation(1337)); 38 | entity.Set(new Relation(1337)); 39 | entity.Set(new Relation(1337)); 40 | entity.Set(new Relation(1337)); 41 | entity.Set(new Relation(1337)); 42 | entity.Set(new Relation(1337)); 43 | entity.Set(new Relation(1337)); 44 | entity.Set(new Relation(1337)); 45 | 46 | entity.Unset(); 47 | entity.Unset(); 48 | entity.Unset(); 49 | entity.Unset(); 50 | entity.Unset(); 51 | entity.Unset(); 52 | entity.Unset(); 53 | entity.Unset(); 54 | entity.Unset(); 55 | entity.Unset(); 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /src/TinyEcs/ChildEntitiesAddRemove.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace TinyEcs; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class ChildEntitiesAddRemove_TinyEcs : ChildEntitiesAddRemove 7 | { 8 | private World world; 9 | private EntityView[] parents; 10 | private EntityView[][] children; 11 | private int entityCount; 12 | private int childCount; 13 | 14 | [GlobalSetup] 15 | public void Setup() { 16 | world = new World(); 17 | entityCount = Entities; 18 | childCount = Constants.ChildCount; 19 | parents = world.CreateEntities(entityCount).AddComponents(); 20 | children = new EntityView[entityCount][]; 21 | for (int n = 0; n < entityCount; n++) { 22 | children[n] = world.CreateEntities(childCount).AddComponents(); 23 | } 24 | } 25 | 26 | [GlobalCleanup] 27 | public void Shutdown() { 28 | world.Dispose(); 29 | } 30 | 31 | // according to example: https://github.com/andreakarasho/TinyEcs?tab=readme-ov-file#childof 32 | [Benchmark] 33 | public override void Run() 34 | { 35 | for (int n = 0; n < entityCount; n++) { 36 | for (int child = 0; child < childCount; child++) { 37 | parents[n].AddChild(children[n][child]); 38 | } 39 | } 40 | // Assert.AreEqual(entityCount * childCount, CountChildren()); 41 | for (int n = 0; n < entityCount; n++) { 42 | for (int child = childCount - 1; child >= 0; child--) { 43 | children[n][child].Unset(parents[n]); 44 | } 45 | } 46 | } 47 | 48 | // method only for verification 49 | private int CountChildren() 50 | { 51 | int count = 0; 52 | for (int n = 0; n < entityCount; n++) { 53 | for (int child = 0; child < childCount; child++) { 54 | var hasParent = children[n][child].Has(parents[n]); 55 | count += hasParent ? 1 : 0; 56 | } 57 | } 58 | return count; 59 | } 60 | } -------------------------------------------------------------------------------- /src/TinyEcs/CommandBufferAddRemove.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace TinyEcs; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class CommandBufferAddRemove_TinyEcs : CommandBufferAddRemove 7 | { 8 | private World world; 9 | private EntityView[] entities; 10 | 11 | [GlobalSetup] 12 | public void Setup() { 13 | world = new World(); 14 | entities = world.CreateEntities(Entities); 15 | } 16 | 17 | [GlobalCleanup] 18 | public void Shutdown() { 19 | world.Dispose(); 20 | } 21 | 22 | [Benchmark] 23 | public override void Run() 24 | { 25 | world.BeginDeferred(); 26 | foreach (var entity in entities) { 27 | entity 28 | .Set(new Component1()) 29 | .Set(new Component2()); 30 | } 31 | world.EndDeferred(); // Apply changes 1 32 | 33 | world.BeginDeferred(); 34 | foreach (var entity in entities) { 35 | entity 36 | .Unset() 37 | .Unset(); 38 | } 39 | world.EndDeferred(); // Apply changes 2 40 | } 41 | } -------------------------------------------------------------------------------- /src/TinyEcs/ComponentEvents.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | // ReSharper disable NotAccessedField.Local 4 | namespace TinyEcs; 5 | 6 | // ReSharper disable once InconsistentNaming 7 | public class ComponentEvents_TinyEcs : ComponentEvents 8 | { 9 | private World world; 10 | private EntityView[] entities; 11 | private int iterations; 12 | private int added; 13 | private int removed; 14 | 15 | [GlobalSetup] 16 | public void Setup() { 17 | world = new World(); 18 | entities = world.CreateEntities(Constants.EventCount); 19 | world.OnComponentSet += (_, _) => { added++; }; 20 | world.OnComponentSet += (_, _) => { removed++; }; 21 | } 22 | 23 | [GlobalCleanup] 24 | public void Shutdown() { 25 | world.Dispose(); 26 | // Issue - event count is off by one 27 | // var expect = iterations * Constants.EventCount; 28 | // Assert.AreEqual(expect, added); Exception - expect: 29491300, was: 29491301 29 | // Assert.AreEqual(expect, removed); Exception - expect: 29491300, was: 29491301 30 | } 31 | 32 | [Benchmark] 33 | public override void Run() 34 | { 35 | iterations++; 36 | foreach (var entity in entities) { 37 | entity.Set(new Component1()); 38 | entity.Unset(); 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /src/TinyEcs/CreateEntity.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace TinyEcs; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class CreateEntity_TinyEcs : CreateEntity 7 | { 8 | private World world; 9 | 10 | [IterationSetup] 11 | public void Setup() 12 | { 13 | world = new World(); 14 | } 15 | 16 | [IterationCleanup] 17 | public void Shutdown() 18 | { 19 | world.Dispose(); 20 | } 21 | 22 | protected override void CreateEntity1Component() 23 | { 24 | var archetype = world.Archetype(); 25 | for (int n = 0; n < Entities; n++) { 26 | world.Entity(archetype).Get().Value = n; 27 | } 28 | } 29 | 30 | protected override void CreateEntity3Components() 31 | { 32 | var archetype = world.Archetype(); 33 | for (int n = 0; n < Entities; n++) { 34 | var entity = world.Entity(archetype); 35 | entity.Get().Value = n; 36 | entity.Get().Value = n; 37 | entity.Get().Value = n; 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /src/TinyEcs/CreateWorld.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace TinyEcs; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class CreateWorld_TinyEcs : CreateWorld 7 | { 8 | [Benchmark] 9 | public override void Run() 10 | { 11 | var world = new World(); 12 | world.Dispose(); 13 | } 14 | } -------------------------------------------------------------------------------- /src/TinyEcs/DeleteEntity.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace TinyEcs; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class DeleteEntity_TinyEcs : DeleteEntity 7 | { 8 | private World world; 9 | private EntityView[] entities; 10 | 11 | [IterationSetup] 12 | public void Setup() 13 | { 14 | world = new World(); 15 | entities = world.CreateEntities(Entities).AddComponents(); 16 | } 17 | 18 | [IterationCleanup] 19 | public void Shutdown() 20 | { 21 | world.Dispose(); 22 | } 23 | 24 | [Benchmark] 25 | public override void Run() 26 | { 27 | foreach (var entity in entities) { 28 | entity.Delete(); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/TinyEcs/GetSetComponents.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace TinyEcs; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class GetSetComponents_TinyEcs : GetSetComponents 7 | { 8 | private World world; 9 | private EntityView[] entities; 10 | 11 | [GlobalSetup] 12 | public void Setup() { 13 | world = new World(); 14 | entities = world.CreateEntities(Entities).AddComponents(); 15 | } 16 | 17 | [GlobalCleanup] 18 | public void Shutdown() { 19 | world.Dispose(); 20 | } 21 | 22 | protected override void Run1Component() 23 | { 24 | foreach (var entity in entities) { 25 | world.Get(entity) = new Component1(); 26 | } 27 | } 28 | 29 | protected override void Run5Components() 30 | { 31 | foreach (var entity in entities) { 32 | world.Get(entity) = new Component1(); 33 | world.Get(entity) = new Component2(); 34 | world.Get(entity) = new Component3(); 35 | world.Get(entity) = new Component4(); 36 | world.Get(entity) = new Component5(); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /src/TinyEcs/QueryComponents.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace TinyEcs; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class QueryComponents_TinyEcs : QueryComponents 7 | { 8 | private World world; 9 | private Query query1; 10 | private Query query5; 11 | 12 | [GlobalSetup] 13 | public void Setup() { 14 | world = new World(); 15 | world.CreateEntities(Entities).AddComponents(); 16 | query1 = world.QueryBuilder().With().Build(); 17 | query5 = world.QueryBuilder().With().With().With().With().With().Build(); 18 | Check.AreEqual(Entities, query5.Count()); 19 | } 20 | 21 | [GlobalCleanup] 22 | public void Shutdown() { 23 | world.Dispose(); 24 | } 25 | 26 | protected override void Run1Component() 27 | { 28 | foreach (var (e, c1) in query1.Iter()) { 29 | for (var n = 0; n < e.Length; n++) { 30 | c1[n].Value++; 31 | } 32 | } 33 | } 34 | 35 | protected override void Run5Components() 36 | { 37 | foreach (var (e, c1, c2, c3, c4, c5) in query5.Iter()) { 38 | for (var n = 0; n < e.Length; n++) { 39 | c1[n].Value = c2[n].Value + c3[n].Value + c4[n].Value + c5[n].Value; 40 | } 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /src/TinyEcs/QueryFragmented.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace TinyEcs; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public class QueryFragmented_TinyEcs : QueryFragmented 7 | { 8 | private World world; 9 | private Query query; 10 | 11 | [GlobalSetup] 12 | public void Setup() 13 | { 14 | world = new World(); 15 | var entities = world.CreateEntities(Entities); 16 | query = world.QueryBuilder().With().Build(); 17 | for (int n = 0; n < Entities; n++) { 18 | var entity = entities[n]; 19 | entity.Set(new Component1()); 20 | if ((n & 1) != 0) entity.Set(new Component2()); 21 | if ((n & 2) != 0) entity.Set(new Component3()); 22 | if ((n & 4) != 0) entity.Set(new Component4()); 23 | if ((n & 8) != 0) entity.Set(new Component5()); 24 | } 25 | Check.AreEqual(Entities, query.Count()); 26 | } 27 | 28 | [GlobalCleanup] 29 | public void Shutdown() 30 | { 31 | world.Dispose(); 32 | } 33 | 34 | [Benchmark] 35 | public override void Run() 36 | { 37 | foreach (var (e, c1) in query.Iter()) { 38 | for (var n = 0; n < e.Length; n++) { 39 | c1[n].Value++; 40 | } 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /src/TinyEcs/Tests.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | 3 | namespace TinyEcs; 4 | 5 | // ReSharper disable once InconsistentNaming 6 | public static class Tests_TinyEcs 7 | { 8 | [Fact] 9 | public static void CheckTreeCycles() 10 | { 11 | using var world = new World(); 12 | var entity1 = world.Entity(); 13 | var entity2 = world.Entity(); 14 | entity1.AddChild(entity2); 15 | entity2.AddChild(entity1); // creates cycle 16 | 17 | Assert.True(entity1.Has(entity2)); 18 | Assert.True(entity2.Has(entity1)); 19 | } 20 | } -------------------------------------------------------------------------------- /src/TinyEcs/_BenchUtils.cs: -------------------------------------------------------------------------------- 1 | namespace TinyEcs; 2 | 3 | public static class BenchUtils 4 | { 5 | public static EntityView[] CreateEntities (this World world, int count) 6 | { 7 | var entities = new EntityView[count]; 8 | for (int n = 0; n < count; n++) { 9 | entities[n] = world.Entity(); 10 | } 11 | return entities; 12 | } 13 | 14 | public static EntityView[] AddComponents(this EntityView[] entities) 15 | { 16 | foreach (var entity in entities) 17 | { 18 | entity.Set(new Component1()); 19 | entity.Set(new Component2()); 20 | entity.Set(new Component3()); 21 | entity.Set(new Component4()); 22 | entity.Set(new Component5()); 23 | } 24 | return entities; 25 | } 26 | } -------------------------------------------------------------------------------- /src/TinyEcs/_Components.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable NotAccessedPositionalProperty.Global 2 | namespace TinyEcs; 3 | 4 | #pragma warning disable CS0649 // Field is never assigned to, and will always have its default value 5 | 6 | public record struct Component1(int Value); 7 | 8 | public record struct Component2(int Value); 9 | 10 | public record struct Component3(int Value); 11 | 12 | public record struct Component4(int Value); 13 | 14 | public record struct Component5(int Value); 15 | 16 | // ReSharper disable once NotAccessedPositionalProperty.Global 17 | internal record struct LinkRelation; 18 | 19 | internal record struct Tag1; 20 | internal record struct Tag2; 21 | internal record struct Tag3; 22 | internal record struct Tag4; 23 | internal record struct Tag5; 24 | internal record struct Tag6; 25 | internal record struct Tag7; 26 | internal record struct Tag8; 27 | internal record struct Tag9; 28 | internal record struct Tag10; 29 | 30 | // --- TinyEcs specific: discriminator for relations are tags 31 | internal record struct Relation(int Value); 32 | -------------------------------------------------------------------------------- /src/_Category/AddRemoveComponents.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | // ReSharper disable once CheckNamespace 4 | [BenchmarkCategory(nameof(AddRemoveComponents))] 5 | public abstract class AddRemoveComponents 6 | { 7 | [Params(Constants.EntityCount)] 8 | public int Entities { get; set; } 9 | 10 | [Params(1, 5)] 11 | public int Components { get; set; } 12 | 13 | [Benchmark] 14 | public virtual void Run() 15 | { 16 | switch (Components) { 17 | case 1: Run1Component(); return; 18 | case 5: Run5Components(); return; 19 | } 20 | } 21 | 22 | protected abstract void Run1Component(); 23 | protected abstract void Run5Components(); 24 | } 25 | -------------------------------------------------------------------------------- /src/_Category/AddRemoveLinks.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | // ReSharper disable once CheckNamespace 4 | [BenchmarkCategory(nameof(AddRemoveLinks))] 5 | public abstract class AddRemoveLinks 6 | { 7 | [Params(Constants.EntityCount)] 8 | public int Entities { get; set; } 9 | 10 | [Params(1, 100)] 11 | public int Relations { get; set; } 12 | 13 | public abstract void Run(); 14 | } 15 | -------------------------------------------------------------------------------- /src/_Category/AddRemoveRelations.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | // ReSharper disable once CheckNamespace 4 | [BenchmarkCategory(nameof(AddRemoveRelations))] 5 | public abstract class AddRemoveRelations 6 | { 7 | [Params(Constants.EntityCount)] 8 | public int Entities { get; set; } 9 | 10 | [Params(1, 100)] 11 | public int Relations { get; set; } 12 | 13 | [Benchmark] 14 | public virtual void Run() 15 | { 16 | if (Relations == 1) { 17 | AddRemove1Relation(); 18 | return; 19 | } 20 | AddRemove10Relations(); 21 | } 22 | 23 | protected abstract void AddRemove1Relation(); 24 | protected abstract void AddRemove10Relations(); 25 | } 26 | -------------------------------------------------------------------------------- /src/_Category/ChildEntitiesAddRemove.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | // ReSharper disable once CheckNamespace 4 | [BenchmarkCategory(nameof(ChildEntitiesAddRemove))] 5 | public abstract class ChildEntitiesAddRemove 6 | { 7 | [Params(Constants.EntityCount)] 8 | public int Entities { get; set; } 9 | 10 | public abstract void Run(); 11 | } 12 | -------------------------------------------------------------------------------- /src/_Category/CommandBufferAddRemove.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | // ReSharper disable once CheckNamespace 4 | [BenchmarkCategory(nameof(CommandBufferAddRemove))] 5 | public abstract class CommandBufferAddRemove 6 | { 7 | [Params(Constants.EntityCount)] 8 | public int Entities { get; set; } 9 | 10 | public abstract void Run(); 11 | } 12 | -------------------------------------------------------------------------------- /src/_Category/ComponentEvents.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | // ReSharper disable once CheckNamespace 4 | [BenchmarkCategory(nameof(ComponentEvents))] 5 | public abstract class ComponentEvents 6 | { 7 | [Params(Constants.EntityCount)] 8 | public int Entities { get; set; } 9 | 10 | public abstract void Run(); 11 | } 12 | -------------------------------------------------------------------------------- /src/_Category/CreateEntity.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | // ReSharper disable once CheckNamespace 4 | [WarmupCount(5000)] // so fast implementation to execute JIT compiler 5 | [BenchmarkCategory(nameof(CreateEntity))] 6 | public abstract class CreateEntity 7 | { 8 | [Params(Constants.CreateEntityCount)] 9 | public int Entities { get; set; } 10 | 11 | [Params(1, 3)] 12 | public int Components { get; set; } 13 | 14 | [Benchmark] 15 | public virtual void Run() 16 | { 17 | switch (Components) { 18 | case 1: CreateEntity1Component(); return; 19 | case 3: CreateEntity3Components(); return; 20 | } 21 | } 22 | 23 | protected abstract void CreateEntity1Component(); 24 | protected abstract void CreateEntity3Components(); 25 | } 26 | 27 | [WarmupCount(5000)] // so fast implementation to execute JIT compiler 28 | [BenchmarkCategory(nameof(CreateBulk))] 29 | public abstract class CreateBulk 30 | { 31 | [Params(Constants.CreateBulkCount)] 32 | public int Entities { get; set; } 33 | 34 | [Params(1, 3)] 35 | public int Components { get; set; } 36 | 37 | [Benchmark] 38 | public virtual void Run() 39 | { 40 | switch (Components) { 41 | case 1: CreateEntity1Component(); return; 42 | case 3: CreateEntity3Components(); return; 43 | } 44 | } 45 | 46 | protected abstract void CreateEntity1Component(); 47 | protected abstract void CreateEntity3Components(); 48 | } 49 | -------------------------------------------------------------------------------- /src/_Category/CreateWorld.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | // ReSharper disable once CheckNamespace 4 | [BenchmarkCategory(nameof(CreateWorld))] 5 | public abstract class CreateWorld 6 | { 7 | public abstract void Run(); 8 | } 9 | -------------------------------------------------------------------------------- /src/_Category/DeleteEntity.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | // ReSharper disable once CheckNamespace 4 | [BenchmarkCategory(nameof(DeleteEntity))] 5 | public abstract class DeleteEntity 6 | { 7 | [Params(Constants.DeleteEntityCount)] 8 | public int Entities { get; set; } 9 | 10 | // all BenchUtils.AddComponents() add 5 componments 11 | [Params(5)] 12 | public int Components { get; set; } 13 | 14 | public abstract void Run(); 15 | } 16 | -------------------------------------------------------------------------------- /src/_Category/GetSetComponents.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | // ReSharper disable once CheckNamespace 4 | [BenchmarkCategory(nameof(GetSetComponents))] 5 | public abstract class GetSetComponents 6 | { 7 | [Params(Constants.EntityCount)] 8 | public int Entities { get; set; } 9 | 10 | [Params(1, 5)] 11 | public int Components { get; set; } 12 | 13 | [Benchmark] 14 | public virtual void Run() 15 | { 16 | switch (Components) { 17 | case 1: Run1Component(); return; 18 | case 5: Run5Components(); return; 19 | } 20 | } 21 | 22 | protected abstract void Run1Component(); 23 | protected abstract void Run5Components(); 24 | } 25 | -------------------------------------------------------------------------------- /src/_Category/QueryComponents.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | // ReSharper disable once CheckNamespace 4 | [BenchmarkCategory(nameof(QueryComponents))] 5 | public abstract class QueryComponents 6 | { 7 | [Params(100, 100_000)] 8 | public int Entities { get; set; } 9 | 10 | [Params(1, 5)] 11 | public int Components { get; set; } 12 | 13 | [Benchmark] 14 | public virtual void Run() 15 | { 16 | switch (Components) { 17 | case 1: Run1Component(); return; 18 | case 5: Run5Components(); return; 19 | } 20 | } 21 | 22 | protected abstract void Run1Component(); 23 | protected abstract void Run5Components(); 24 | } 25 | 26 | [BenchmarkCategory(nameof(QueryFragmented))] 27 | public abstract class QueryFragmented 28 | { 29 | [Params(Constants.FragmentationCount)] 30 | public int Entities { get; set; } 31 | 32 | [Benchmark] 33 | public abstract void Run(); 34 | } 35 | 36 | -------------------------------------------------------------------------------- /src/_Category/Search.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | // ReSharper disable once CheckNamespace 4 | [BenchmarkCategory(nameof(SearchComponentField))] 5 | public abstract class SearchComponentField 6 | { 7 | [Params(Constants.SearchSetSize)] 8 | public int Entities { get; set; } 9 | 10 | public abstract void Run(); 11 | } 12 | 13 | [BenchmarkCategory(nameof(SearchRange))] 14 | public abstract class SearchRange // aka range query 15 | { 16 | [Params(Constants.SearchSetSize)] 17 | public int Entities { get; set; } 18 | 19 | public abstract void Run(); 20 | } 21 | --------------------------------------------------------------------------------