Okay, what the fox!
|
|
Brand new? Try the cookbook for a quick & tasty intro, or dive into the docs! Familiar with ECS architectures? Get an overview of new & unique concepts!
At the basic level, all you need is a 🧩component type, a number of small foxes 🦊entities, and a query to ⚙️iterate and modify components, occasionally passing in some uniform 💾data.
// Declare a component record. (we can also use most existing value & reference types)
record struct Velocity(Vector3 Value);
// Create a world. (fyi, World implements IDisposable)
var world = new fennecs.World();
// Spawn an entity into the world with a choice of components. (or add/remove them later)
var entity = world.Spawn().Add<Velocity>();
// Queries are cached & we use ultra-lightweight Stream Views to feed data to our code!
var stream = world.Query<Velocity>().Stream();
// Run code on all entities in the query. (exchange 'For' with 'Job' for parallel processing)
stream.For(
uniform: DeltaTime * 9.81f * Vector3.UnitZ,
action: (Vector3 uniform, ref Velocity velocity) =>
{
velocity.Value -= uniform;
}
);
By any measure, we're talking just a couple of lines to get this gravity feature up and running. Creating the world and query is the only setup – the real slam dunk is how cleanly we built the full actor/gravity logic with barely any ceremonial code in sight.
And there's more: all that simplicity doesn't force any performance trade-offs! You get to have your cake and eat it too with zero confusion or fluff!
Grab a cup of coffee to get started, try the Cookbook, view the Demos , and more!
Preliminary (WIP) benchmarks suggest you can expect to process over 2 million components per millisecond on a 2020 CPU without even customizing your logic.
Using Doraku's synthetic Ecs.CSharp.Benchmark, fennecs scores among the faster ECS in the benchmark suite.
(link goes to PR #36 to reproduce)
[!WARNING] These are synthetic benchmarks, using a BETA BUILD of fennecs. Real-world performance will vary wildly. If you need a production-ready ECS today, 9 out of 10 foxes endorse Friflo.Engine.ECS👍 and Flecs.NET👍
Another optimization pass for fennecs is on the Roadmap.
// Benchmark Process Environment Information:
// BenchmarkDotNet v0.13.12
// Runtime=.NET 8.0.5 (8.0.524.21615), X64 RyuJIT AVX2
// GC=Concurrent Workstation
// HardwareIntrinsics=AVX2,AES,BMI1,BMI2,FMA,LZCNT,PCLMUL,POPCNT VectorSize=256
// Job: ShortRun(IterationCount=3, LaunchCount=1, WarmupCount=3)
// [EntityCount=100_000]
ECS & Method | Duration (less=better) |
---|---|
🦊 fennecs | 1.458 ms |
FrifloEngineEcs | 1.926 ms |
LeopotamEcs | 4.991 ms |
LeopotamEcsLite | 4.994 ms |
Arch | 7.811 ms |
FlecsNet | 17.838 ms |
DefaultEcs | 19.818 ms |
TinyEcs | 24.458 ms |
HypEcs | 25.215 ms |
MonoGameExtended | 27.562 ms |
Myriad | 28.249 ms |
SveltoECS | 52.311 ms |
Morpeh_Stash | 64.930 ms |
RelEcs | 65.023 ms |
Morpeh_Direct | 131.363 ms |
// Benchmark Process Environment Information:
// BenchmarkDotNet v0.13.12
// Runtime=.NET 8.0.5 (8.0.524.21615), X64 RyuJIT AVX2
// GC=Concurrent Workstation
// HardwareIntrinsics=AVX2,AES,BMI1,BMI2,FMA,LZCNT,PCLMUL,POPCNT VectorSize=256
// Job: ShortRun(IterationCount=3, LaunchCount=1, WarmupCount=3)
// [EntityCount=100_000, EntityPadding=10]
ECS & Method | Duration (less=better) |
Comment |
---|---|---|
🦊 fennecs(AVX2) | 10.43 µs | optimized Stream<>.Raw using AVX2 Intrinsics |
🦊 fennecs(SSE2) | 11.41 µs | optimized Stream<>.Raw using SSE2 Intrinsics |
FrifloEngineEcs_MultiThread | 13.45 µs | |
FrifloEngineEcs_SIMD_MonoThread | 16.92 µs | |
TinyEcs_EachJob | 20.51 µs | |
Myriad_MultiThreadChunk | 20.73 µs | |
TinyEcs_Each | 40.84 µs | |
FrifloEngineEcs_MonoThread | 43.41 µs | |
HypEcs_MonoThread | 43.86 µs | |
🦊 fennecs(Raw) | 46.36 µs | straightforward loop over Stream<>.Raw |
HypEcs_MultiThread | 46.80 µs | |
Myriad_SingleThreadChunk | 48.56 µs | |
Arch_MonoThread | 51.08 µs | |
Myriad_SingleThread | 55.65 µs | |
🦊 fennecs(For) | 56.32 µs | your typical bread & butter fennecs workload |
Arch_MultiThread | 59.84 µs | |
FlecsNet_Iter | 77.47 µs | |
🦊 fennecs(Job) | 97.70 µs | unoptimized in beta, ineffective <1M entities |
DefaultEcs_MultiThread | 102.37 µs | |
Myriad_Delegate | 109.31 µs | |
Arch_MonoThread_SourceGenerated | 134.12 µs | |
DefaultEcs_MonoThread | 142.35 µs | |
LeopotamEcs | 181.76 µs | |
FlecsNet_Each | 212.61 µs | |
LeopotamEcsLite | 230.50 µs | |
Myriad_Enumerable | 245.76 µs | |
RelEcs | 250.93 µs | |
SveltoECS | 322.30 µs | EntityPadding=0, skips benchmark with 10 |
MonoGameExtended | 387.12 µs | |
Morpeh_Stash | 992.62 µs | |
Myriad_MultiThread | 1115.44 µs | |
Morpeh_Direct | 2465.25 µs |
So how does fennecs compare to other ECSs?
This library is a tiny, tiny ECS with a focus on good performance and great simplicity. But it cares enough to provide a few things you might not expect.
[!IMPORTANT] The idea of fennecs was to fill the gaps that the author felt working with various established Entity-Component Systems. This is why this matrix is clearly imbalanced, it's a shopping list of things that fennecs does well and was made to do well; and things it may aspire to do but compromised on in order to be able to achieve the others.
(TL;DR - Foxes are soft, choices are hard - Unity dumb, .NET 8 really sharp.)
Many thanks to Byteron (Aaron Winter) for creating HypEcs and RelEcs, the inspiring libraries that fennecs evolved from.
Neofox was created by Volpeon and is in the Creative Commons CC BY-NC-SA 4.0, the same license applies to all Neofox-derived works made for this documentation.