Skip to content
/ fennecs Public

... the tiny C# ECS that loves you back!

License

Notifications You must be signed in to change notification settings

outfox/fennecs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

fennecs logo fennecs logo

... the tiny, tiny, high-energy Entity-Component System!

dotnet add package fennecs
to use the beta versions, append --prerelease

a box of fennecs, 8-color pixel art

Okay, what the fox!
Another ECS?!

We know... oh, we know. 😩

But in a nutshell, fennecs is...

🐾 zero codegen
🐾 minimal boilerplate
🐾 archetype-based
🐾 intuitively relational
🐾 lithe and fast

Discord Nuget Downloads GitHub Actions Workflow Status Open issues

Quickstart

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;
    }
);

πŸ’’... when we said minimal boilerplate, we meant it.

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!


πŸ’‘Highlights / Design Goals

  • Modern C# 12 codebase, targeting .NET 8.
  • Full Unit Test coverage.
  • Powerfully intuitive ways to access data... fast!
  • Workloads can be easily parallelized across and within Archetypes
  • Expressive, queryable relations among Entities themselves & between Entities and Objects
  • No code generation and no reflection required.

πŸ“• DOCUMENTATION: fennecs.tech (official website)

Grab a cup of coffee to get started, try the Cookbook, view the Demos , and more!
coffee cup


⏩ Nimble: fennecs benchmarks

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: CreateEntityWithThreeComponents

// 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: SystemWithThreeComponents

// 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

πŸ₯Š Comparisons: Punching above our weight?

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.)

πŸ₯‡πŸ₯ˆπŸ₯‰ (click to expand) ECS Comparison Matrix

Here are some of the key properties where fennecs might be a better or worse choice than its peers. Our resident fennecs have worked with all of these ECSs, and we're happy to answer any questions you might have.

fennecs HypEcs Entitas Unity DOTS DefaultECS
Boilerplate-to-Feature Ratio 3-to-1 5-to-1 12-to-1 27-to-1 😱 7-to-1
Entity-Component Queries βœ… βœ… βœ… βœ… βœ…
Entity-Entity Relations βœ… βœ… ❌ ❌ βœ…
(Map/MultiMap)
Entity-Object-Relations βœ… 🟨
(System.Type only)
❌ ❌ ❌
Target Querying
(find all targets of specific relations)
βœ… ❌ ❌ ❌ βœ…
Wildcard Semantics
(match multiple relations in 1 query)
βœ… ❌ ❌ ❌ ❌
Journaling ❌ ❌ 🟨 βœ… ❌
Shared Components βœ…
(ref types only)
❌ ❌ 🟨
(restrictive)
βœ…
Mutable Shared Components βœ… ❌ ❌ ❌ βœ…
Reference Component Types βœ… ❌ ❌ ❌ ❌
Arbitrary Component Types βœ… βœ…
(value types only)
❌ ❌ βœ…
Structural Change Events 🟨
(planned)
❌ βœ… ☠️
(unreliable)
❌
Workload Scheduling ❌ ❌ ❌ βœ…
(highly static)
βœ…
No Code Generation Required βœ… βœ… ❌ ❌ 🟨
(roslyn addon)
Enqueue Structural Changes at Any Time βœ… βœ… βœ… 🟨
(restrictive)
🟨
Apply Structural Changes at Any Time ❌ ❌ βœ… ❌ ❌
Parallel Processing ⭐⭐ ⭐ ❌ ⭐⭐⭐ ⭐⭐
Singleton / Unique Components 🟨
(ref types only)
❌ βœ… 🟨
(per system)
βœ…

🧑 Acknowledgements

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.