MIT License

技术解读

以下内容由 GitRead 中文社区 编辑团队基于项目 README 与官方资料整理, 结合中文开发者的常见使用场景补充技术选型建议。具体以项目官方文档为准。

Friflo.Engine.ECS 技术分析

项目背景与痛点

在游戏和高性能模拟应用中,Entity Component System(ECS)架构日益受欢迎。这种架构能够显著提升数据密集型应用程序的性能,特别是在缓存友好的数据布局和支持高效的并行计算方面。friflo/Friflo.Engine.ECS项目正是一款C#实现的高性能ECS引擎,专门针对这类高性能场景进行优化。它直接解决了传统对象模型在处理大规模实体时的性能瓶颈,使得开发者可以专注开发便携且高效的系统。

核心技术揭秘

Friflo.Engine.ECS 项目的内部实现基于了高度优化的数据结构和算法,确保数据访问的高效性。在 Ecosystem 部署中,实体(Entities)作为数据容器使用,而组件(Components)则存储在独立的数据结构中,以数组形式存取,从而确保缓存局部性最大化。系统(Systems)以并行或串行模式更新组件状态,借助C#语言特性进行高度优化。

graph TD subgraph "Friflo Engine ECS架构" A[用户调用API] --> B{"初始化检查"} B -->|"通过验证"| C[系统运行] C --> D[数据更新] D --> E{"新消息到达"} E -->|"有新状态"| F[执行系统] F --> G[状态同步] E -->|"无新状态"| H[继续监听] H -->|循环| G end

引擎在组件更新和状态同步中的机制实现了弱耦合,利用数据流的设计减少了不必要的数据复制,通过事件驱动模型高效调度系统。这些关键设计使得 Friflo.Engine.ECS 在性能上有着显著的优势。

功能亮点与差异

与同类竞品相比,friflo/Friflo.Engine.ECS在数据高效处理和并行运算中表现尤为突出,其独特之处明显在于优化和专门模块的使用,如:

  • 缓存优化的组件分组:通过逻辑和物理上的分组来增强缓存遍历效率。
  • 轻量级事件系统:维持了对事件驱动的设计支持,而不增加瓶颈。
  • 系统调度灵活性:提供多种调度策略,适应不同性能和需求场景。

这使得它非常适用于游戏和实时模拟等对性能要求极高的使用场景。

应用场景与落地建议

该引擎最适合的应用场景是高性能图形和模拟系统,比如实时Strategy游戏或模拟训练系统,它也适合作为底层框架支持复杂的实时内容创作和编辑工具。在复杂的生产线上使用时,需确保系统的稳定性,并为数据容量和性能要求留出足够的余地,尤其是在实体数量和组件复杂度特别高的情况时需要慎重部署。

综合评价

friflo/Friflo.Engine.ECS 在数据处理的高效性和灵活性上显示了其优异的特性,特别适合处理大规模数据的应用环境。相比于原始数据驱动的OOP模式,它提供了一个更有效的架构,其组件系统模式是现代高性能应用的理想选择。不过,需要注意的是,对于初学者来说,ECS架构需要适应新的思维模式和开发范式,这可能需要一定时间的成本。因此,评价总结为:一个成熟、适配性能优化需求的高性能C# ECS引擎。

关于

GitHub 原始描述(英文)

High-performance C# ECS

描述取自 GitHub 官方 API,保持仓库作者书写的原文。

简要分析

热度分
962
价值分
504
活跃状态
活跃

语言占比

C#
GLSL
Wolfram Language

Release

README

friflo ECS   splash

nuget codecov CI-Engine Demos C# API Discord Docs

friflo ECS

Friflo ECS is a high performance C# ECS with focus on simplicity and reliability.
The library is designed for easy integration and a smooth developer experience.
Among various C# ECS projects, it is currently the only one fully implemented in Managed C#.
By this no use of unsafe code preventing crashes due to memory corruption.

What is ECS - Entity Component System? An ECS is a software architecture pattern. See [ECS ⋅ Wikipedia](https://en.wikipedia.org/wiki/Entity_component_system). It is often used in the Gaming industry - e.g. Minecraft - and used for high performant data processing. An ECS provide two strengths:
  1. It enables writing highly decoupled code. Data is stored in Components which are assigned to objects - aka Entities - at runtime.
    Code decoupling is accomplished by dividing implementation in pure data structures (Component types) - and code (Systems) to process them.

  2. It enables high performant system execution by storing components in continuous memory to leverage CPU caches L1, L2 & L3.
    It improves CPU branch prediction by minimizing conditional branches when processing components in tight loops.

Highlights

  • 🔥 Top Performance - C/C++/Rust speed and memory efficiency. Various benchmarks below.
  • 🧩 Bare Essentials - Provide only ECS core concepts: components, tags, relations and systems. Thats it!
  • 🎯 Simple - Clean & Simple API. No boilerplate. Check the Documentation with many examples.
  • 🛡️ Reliable - Built for production. Fully Managed C# - No unsafe code. High test coverage.
  • 🙏 Community - Helpful and welcoming support on Discord and Github.

Features

🚀 New in v3.5.0
Map components to enums and Map tags to enums.
Introduced new pattern to process events on adding/removing components or tags more performant and concise.

🚀 New in v3.6.0
New feature: Query Generator
Generates high-performance query code and reduces boilerplate code to a minimum.

  • High performant / type-safe queries
  • Efficient multithreaded queries
  • Fast batch / bulk operations
  • Command buffers aka deferred operations
  • Clone / Copy entities
  • Entity hierarchy aka scene tree
  • Relationships and relations
  • Component Search in O(1)
  • Entity / component events
  • Allocation free enumerators
  • Systems and groups
  • JSON Serialization
  • SIMD Support
  • Type-safe Add/Remove/Get component. No potential memory corruption like in Arch.
  • Single library. No exclusive feature combinations such as events, assertions or debug/release.
  • Zero allocations after buffers are large enough.
  • Automatic component type registration
  • Supports .NET Standard 2.1 .NET 5 .NET 6 .NET 7 .NET 8 .NET 9 .NET 10 WASM / WebAssembly, Unity (Mono, AOT/IL2CPP, WebGL), Godot, MonoGame
  • Native AOT
  • CLS Compliant API - supporting all .NET languages: C#, F#, VB.NET, …
  • Debug Tools: Watch entities, components, tags, relations, query results, systems, …
    Screenshot - Watch ad-hoc query result in debugger
  • Fully Managed C#. No unsafe code, native dll bindings and access violations.
    Behavior of access violation bugs using unsafe code

Get package on nuget or use the dotnet CLI.

dotnet add package Friflo.Engine.ECS

Projects using friflo ECS

Vanguard Tides


Godot Steam
A survivors-like game exploiting the strength of ECS controlling a very high amount of AI units.
A Godot MultiMesh is used to enable reliable 2000 FPS.
It uses a single MultiMeshInstance2D node with a single material and shader.

Louis Adventure


Unity WASM Android     Play in Browser
Tech Demo of a 2D platformer with focus on movement mechanics.
ECS is used for core game logic, component-base abilities, physics / collisions & triggers,
state machines / behavior logic, gameplay events and UI integration. more …

RaygueLike Challenge


raylib WASM     Play in Browser · GitHub
Project is hosted on GitHub and supports Desktop and WASM.
Rendering of 3D environment and 2D sprites is implemented with Raylib.
Interesting note: It combines behavior trees with ECS. more …

Horse Runner DX


MonoGame Nintendo Switch Steam Deck Steam
Quote from developer: “Just wanted to let you know that Friflo ECS 2.0.0 works like a charm in my little game.
I use it for basically everything (landscape segments, vegetation, players, animations, collisions and even the floating dust particles are entities).
After some optimization there is no object allocation during gameplay - the allocation graph just stays flat - no garbage collection.”

Want to add a project?
Create a GitHub Discussion or message on Discord.

Demos

MonoGame Demo is available as WASM / WebAssembly app. Try Demo in your browser.
Demo projects on GitHub below.

MonoGame Unity Godot

Desktop Demo performance: Godot 202 FPS, Unity 100 FPS at 65536 entities.
All example Demos - Windows, macOS & Linux - available as projects for MonoGame, Unity and Godot.
See Demos · GitHub


New features in 3.0.0

  • Index / Search used to search entities with specific component values in O(1). E.g

    • To lookup entities having a TileComponent with a specific tile id.
    • To lookup network entities with a NetComponent using a custom identifier like a Guid,long or a string.
  • Relationships to create links / connections between entities. Used for:

    • Attack systems
    • Path finding / Route tracing
    • Model social networks. E.g friendship, alliances or rivalries
    • Build any type of a directed graph using entities as nodes and links or relations as edges
  • Relations to add multiple “components” of the same type to a single entity. E.g.

    • Inventory systems
    • To model one-to-many data structures

Big shout out to fennecs and flecs for the challenge to improve the feature set and performance of this project!


⏩ Examples

This section contains two typical use cases when using an ECS.
More examples describing use of various features are at friflo ECS - Documentation.

🚀 Hello World

The hello world examples demonstrates the creation of a world, some entities with components
and their movement using a simple ForEachEntity() call.

public struct Velocity : IComponent { public Vector3 value; }

public static void HelloWorld()
{
    var world = new EntityStore();
    for (int n = 0; n < 10; n++) {
        world.CreateEntity(new Position(n, 0, 0), new Velocity{ value = new Vector3(0, n, 0)});
    }
    var query = world.Query<Position, Velocity>();
    query.ForEachEntity((ref Position position, ref Velocity velocity, Entity entity) => {
        position.value += velocity.value;
    });
}

In case of moving (updating) thousands or millions of entities an optimized approach can be used.
See: Enumerate Query Chunks, Parallel Query Job and Query Vectorization - SIMD.
All query optimizations are using the same query but with different enumeration techniques.


⚙️ Systems

Systems are new in Friflo.Engine.ECS v2.0.0

Systems in ECS are typically queries.
So you can still use the world.Query<Position, Velocity>() shown in the “Hello World” example.

Using Systems is optional but they have some significant advantages.

System features

  • Enable chaining multiple decoupled QuerySystem classes in a SystemGroup.
    Each group provide a CommandBuffer.

  • A system can have state - fields or properties - which can be used as parameters in OnUpdate().
    The system state can be serialized to JSON.

  • Systems can be enabled/disabled or removed.
    The order of systems in a group can be changed.

  • Systems have performance monitoring build-in to measure execution times and memory allocations.
    If enabled systems detected as bottleneck can be optimized.
    A perf log (see example below) provide a clear overview of all systems their amount of entities and impact on performance.

  • Multiple worlds can be added to a single SystemRoot instance.
    root.Update() will execute every system on all worlds.

public static void HelloSystem()
{
    var world = new EntityStore();
    for (int n = 0; n < 10; n++) {
        world.CreateEntity(new Position(n, 0, 0), new Velocity(), new Scale3());
    }
    var root = new SystemRoot(world) {
        new MoveSystem(),
    //  new PulseSystem(),
    //  new ... multiple systems can be added. The execution order still remains clear.
    };
    root.Update(default);
}
        
class MoveSystem : QuerySystem<Position, Velocity>
{
    protected override void OnUpdate() {
        Query.ForEachEntity((ref Position position, ref Velocity velocity, Entity entity) => {
            position.value += velocity.value;
        });
    }
}

A valuable strength of an ECS is establishing a clear and decoupled code structure.
Adding the PulseSystem below to the SystemRoot above is trivial.
This system uses a foreach (var entity in Query.Entities) as an alternative to Query.ForEachEntity((...) => {...})
to iterate the query result.

struct Pulsating : ITag { }

class PulseSystem : QuerySystem<Scale3>
{
    float frequency = 4f;
    
    public PulseSystem() => Filter.AnyTags(Tags.Get<Pulsating>());
    
    protected override void OnUpdate() {
        foreach (var entity in Query.Entities) {
            ref var scale = ref entity.GetComponent<Scale3>().value;
            scale = Vector3.One * (1 + 0.2f * MathF.Sin(frequency * Tick.time));
        }
    }
}

⏱ System monitoring

System performance monitoring is disabled by default.
To enable monitoring call:

root.SetMonitorPerf(true);

When enabled system monitoring captures

  • Number of system executions.
  • System execution duration in ms.
  • Memory heap allocations per system in bytes.
  • The number of entities matching a query system.
Realtime monitoring

In a game editor like Unity system monitoring is available in the ECS System Set component.

Screenshot: ECS System Set component in Play mode
Log monitoring

The performance statistics available at SystemPerf.
To get performance statistics on console use:

root.Update(default);
Console.WriteLine(root.GetPerfLog());

The log result will look like:

stores: 1                  on   last ms    sum ms   updates  last mem   sum mem  entities
---------------------      --  --------  --------  --------  --------  --------  --------
Systems [2]                 +     0.076     3.322        10       128      1392
| ScaleSystem               +     0.038     2.088        10        64       696     10000
| PositionSystem            +     0.038     1.222        10        64       696     10000
on                  + enabled  - disabled
last ms, sum ms     last/sum system execution time in ms
updates             number of executions
last mem, sum mem   last/sum allocated bytes
entities            number of entities matching a QuerySystem

🏁 ECS Benchmarks

ECS.CSharp.Benchmark - Common use-cases

Created a new GitHub repository ECS.CSharp.Benchmark - Common use-cases.
It compares the performance of multiple ECS projects with simple benchmarks.
So they can be used as a guide to migrate form one ECS to another.
See discussion of reddit announcement Post.

Performance Ratio Overview - see C# ECS Benchmark

friflo Flecs.NET TinyEcs Arch fennecs Leopotam DefaultEcs Morpeh
Ratio 1.00 2.55 3.42 6.96 19.02 2.57 3.81 21.09

ECS.CSharp.Benchmark

Performance comparison using popular ECS C# benchmark on GitHub.
Two benchmarks - subset of GitHub ⋅ Ecs.CSharp.Benchmark + PR #38 running on a Mac Mini M2.

See Benchmark results.


You Like this project?
Leave a ⭐ at  friflo ECS


License

This project is licensed under MIT.

Friflo.Engine.ECS
Copyright © 2026   Ullrich Praetz - https://github.com/friflo

评论

暂无评论