https://github.com/kjkrol/goke
High-performance, zero-allocation ECS for Go. Cache-friendly data orchestrator with type-safe iterators.
https://github.com/kjkrol/goke
ebitengine ecs entity-component-system gamedev geme-engine go goalng high-performance simulation
Last synced: 9 days ago
JSON representation
High-performance, zero-allocation ECS for Go. Cache-friendly data orchestrator with type-safe iterators.
- Host: GitHub
- URL: https://github.com/kjkrol/goke
- Owner: kjkrol
- License: mit
- Created: 2026-01-10T12:17:49.000Z (5 months ago)
- Default Branch: main
- Last Pushed: 2026-06-07T21:58:32.000Z (10 days ago)
- Last Synced: 2026-06-07T23:22:57.711Z (10 days ago)
- Topics: ebitengine, ecs, entity-component-system, gamedev, geme-engine, go, goalng, high-performance, simulation
- Language: Go
- Homepage: https://github.com/kjkrol/goke/wiki
- Size: 3.22 MB
- Stars: 80
- Watchers: 1
- Forks: 1
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
- awesome-go - GOKe - Data-Oriented (DOD), archetype-based ECS engine utilizing an L1 cache-aligned chunked SoA layout for predictable, stepless memory growth and zero-allocation execution paths. (Game Development / Search and Analytic Databases)
- fucking-awesome-go - GOKe - Data-Oriented (DOD), archetype-based ECS engine utilizing an L1 cache-aligned chunked SoA layout for predictable, stepless memory growth and zero-allocation execution paths. (Game Development / Search and Analytic Databases)
README
# GOKe
**GOKe** is an ultra-lightweight, high-performance, and type-safe **SoA (Structure of Arrays)** [Entity Component System](https://en.wikipedia.org/wiki/Entity_component_system) (aka ECS) for [Go](https://go.dev/). It is engineered for maximum data throughput, leveraging modern **Go 1.23+ Iterators** and a Data-Oriented Design (DOD) architecture.
Installation
•
Usage
•
Architecture
•
Performance
•
Features
•
Roadmap
•
Benchmarks
•
Documentation
# 🚀 Use Cases: Why GOKe?
GOKe is primarily a **high-performance ECS for game development**, designed to manage massive entity counts while keeping the Go Garbage Collector (GC) completely silent. However, its core architecture a **data-oriented orchestrator** — makes it suitable for any scenario requiring cache-friendly iteration over millions of objects.
## 🎮 Gaming (Ebitengine & Frameworks)
GOKe is the perfect companion for **Ebitengine** or purely server-side game loops. Managing thousands of active objects (bullets, particles, NPCs) often hits CPU bottlenecks due to pointer chasing and GC pressure. GOKe solves this via:
* **Zero-Alloc Updates**: Update thousands of entities in a single tick without triggering the GC.
* **Decoupled Logic**: Keep your rendering logic in Ebitengine and your game state in GOKe's optimized archetypes, utilizing structures like **[Bucket Grid](https://github.com/kjkrol/gokg)**.
* **Deterministic Physics**: Run complex collision detection systems across all entities using `RunParallel`.
Stats: 2048 colliding AABBs | 120 TPS | 0.1-1 collisions/tick
Stats: 64 colliding AABBs | 120 TPS | 0.1-1 collisions/tick
Check out the full source code
## 🧬 Simulations & High-Throughput Data
Beyond gaming, GOKe shines in any domain where latency consistency is critical and object counts are in the millions.
* **Agent-Based Simulations**: Crowd dynamics, epidemiological models, or particle physics where $O(N)$ iteration speed is the bottleneck.
* **Real-time Telemetry**: Processing high-frequency data streams (e.g., IoT sensor fusion) where predictable memory access patterns prevent latency spikes.
* **Heavy Compute Pipelines**: Logic that requires transforming large datasets every frame (e.g., 16ms window) without allocation overhead.
## ⚖️ When NOT to use GOKe
To ensure GOKe is the right tool for your project, consider these trade-offs:
* **Small Data Sets:** If you only manage a few hundred objects, a simple slice of structs will be easier to maintain and fast enough.
* **Deep Hierarchies:** ECS is designed for flat, high-speed iteration. If your data is naturally a deep tree (like a UI DOM), a classic tree structure might be more intuitive.
* **High Structural Churn:** If you are adding/removing components from thousands of entities *every single frame*, the overhead of archetype migration might offset the iteration gains.
GOKe requires **Go 1.23** or newer.
```bash
go get github.com/kjkrol/goke
```
Core capabilities designed for predictable performance, cache locality, and zero-allocation cycles:
* **Type-Safe Generics**: Views (`NewView1[A]` ... `NewView10`) use Go generics to eliminate interface overhead, boxing, and runtime type assertions in the hot loop.
* **Go 1.23+ Range Iterators**: Uses native `iter.Seq` for standard `for range` loops. This allows the compiler to inline iteration logic directly, avoiding callback overhead.
* **Deferred Mutations**: Structural changes (Create/Remove/Add components) are buffered via a **Command Buffer** and applied at synchronization points to ensure thread safety without heavy locking.
* **Parallel Execution**: `RunParallel` distributes system execution across available CPU cores with deterministic synchronization, scaling linearly with hardware resources.
* **Zero-Alloc Hot Loop**: The architecture guarantees zero heap allocations during the update cycle (tick), preventing GC pauses during simulation.
* **Entity Blueprints**: Fast, template-based instantiation. Allows creating thousands of entities with identical component layouts using optimal memory copy operations.
> 💡 **See it in action**: Check the `cmd` directory for the concurrent dice game simulation demonstrating parallel systems and state management.
## ⚠️ Limitations
* **Maximum component types: 128 by default.** Defined by `MaxComponents` in [`internal/core/archetype_mask.go`](./internal/core/archetype_mask.go). The archetype mask is a fixed-size bitset (`[MaskSize]uint64`
with `MaskSize = 2`) for branch-free intersection checks. Raising this limit requires recompiling GOKe with edited constants (`MaskSize` and `MaxComponents`); it is **not** a runtime configuration.
# 💻 Usage Example
> 📘 **New to ECS?** Check out the [**Getting Started with GOKe**](https://github.com/kjkrol/goke/wiki/Getting-Started-with-GOKe) guide for a step-by-step deep dive into building your first simulation.
```go
package main
import (
"fmt"
"time"
"github.com/kjkrol/goke"
)
type Pos struct{ X, Y float32 }
type Vel struct{ X, Y float32 }
type Acc struct{ X, Y float32 }
func main() {
// Initialize the ECS world.
// The ECS instance acts as the central coordinator for entities and systems.
ecs := goke.New()
// Define component metadata.
// This binds Go types to internal descriptors, allowing the engine to
// pre-calculate memory layouts and manage data in contiguous arrays.
posDesc := goke.RegisterComponent[Pos](ecs)
_ = goke.RegisterComponent[Vel](ecs)
_ = goke.RegisterComponent[Acc](ecs)
// --- Type-Safe Entity Template (Blueprint) ---
// Blueprints place entities into the correct archetype immediately and
// reserve memory for all components in a single batch operation.
// Each yielded page exposes typed slices for direct, in-place initialization.
blueprint := goke.NewBlueprint3[Pos, Vel, Acc](ecs)
var entity goke.Entity
for page := range blueprint.Create(1) {
entity = page.Entity[0]
page.Comp1[0] = Pos{X: 0, Y: 0}
page.Comp2[0] = Vel{X: 1, Y: 1}
page.Comp3[0] = Acc{X: 0.1, Y: 0.1}
}
// Initialize view for Pos, Vel, and Acc components
view := goke.NewView3[Pos, Vel, Acc](ecs)
// Define the movement system using the functional registration pattern
movementSystem := goke.RegisterSystemFunc(ecs, func(schedule *goke.Schedule, d time.Duration) {
// SoA (Structure of Arrays) layout ensures CPU cache friendliness.
// View.All yields page-shaped slices over native memory — the inner
// loop is on the caller side for aggressive compiler inlining.
for page := range view.All() {
for i := range page.Entity {
pos, vel, acc := &page.Comp1[i], &page.Comp2[i], &page.Comp3[i]
vel.X += acc.X
vel.Y += acc.Y
pos.X += vel.X
pos.Y += vel.Y
}
}
})
// Configure the ECS's execution workflow and synchronization points
goke.Plan(ecs, func(ctx goke.ExecutionContext, d time.Duration) {
ctx.Run(movementSystem, d)
ctx.Sync() // Ensure all component updates are flushed and views are consistent
})
// Execute a single simulation step (standard 120 TPS)
goke.Tick(ecs, time.Second/120)
p := goke.GetComponent[Pos](ecs, entity, posDesc)
fmt.Printf("Final Position: {X: %.2f, Y: %.2f}\n", p.X, p.Y)
}
```
### Explore Examples
Check the [**examples/**](./examples) directory for complete, ready-to-run projects.
> ⚠️ **IMPORTANT**:
> **Setup Required**: To keep the core ECS engine lightweight and free of GUI dependencies, examples are managed as isolated modules. Before running them, you must initialize the workspace:
> ```bash
> make setup
> ```
* [**Mini Demo**](./examples/mini-demo/main.go) – The minimalist starter.
* [**Simple Demo**](./examples/simple-demo/main.go) – A slightly more advanced introduction to the ECS lifecycle.
* [**Parallel Demo**](./examples/parallel-demo/main.go) – **Advanced showcase**:
* Coordination of multiple systems.
* Concurrent execution using `RunParallel`.
* Handling structural changes via **Command Buffer** and explicit **Sync points**.
* [**Ebiten Demo**](./examples/ebiten-demo/main.go) – **Graphics Integration & Spatial Physics**:
* Real-time rendering using [Ebitengine](https://github.com/kjkrol/gokg).
* High-performance spatial management using [GOKg](https://github.com/kjkrol/gokg).
* Custom physics pipeline: **Velocity Inversion** is processed strictly before **Position Compensation** to ensure boundary stability.
* **Note**: Run `make` inside the example directory to fetch dependencies and start the demo.
# 🏗️ Core Architecture & "Mechanical Sympathy"
GOKe is an archetype-based ECS designed for deterministic performance. It shifts structural overhead (like offset calculations) to the initialization phase and uses a chunked SoA layout to maintain consistent throughput regardless of scale.
## Data-Oriented Memory Design
The storage layer is engineered to maximize cache hits and eliminate allocation spikes ("GC jitter").
* **Chunked SoA (Structure of Arrays)**: Instead of monolithic slices that require costly resizing (copying millions of elements) when capacity is exceeded, GOKe manages data in **fixed-size Memory Pages** (aligned to L1 Cache, e.g., 96KB).
* **Stable Growth**: Memory allocation is linear and "stepless". Adding the 1,000,001st entity simply allocates one small memory chunk, avoiding the massive latency spike of doubling a large array.
* **Cache Locality**: Inside each chunk, components are packed in a tight SoA layout (`[IDs...][CompA...][CompB...]`), ensuring high-efficiency hardware prefetching.
* **Generation-based Recycling**: Entities are tracked via 64-bit IDs (32-bit Index / 32-bit Generation). This prevents **entity aliasing**—stale references to destroyed entities are instantly recognized as invalid when their memory slot is reused.
* **Archetype Masks**: Supports rapid composition checks using fast, constant-time bitwise operations. This allows for complex queries over component types without iterating over unrelated data.
## High-Throughput Access & Iteration
GOKe bypasses traditional bottlenecks like reflection and map lookups in the execution phase.
* **Flat Cache View**: Views pre-calculate direct pointers to component columns within active chunks. This **eliminates map lookups** and pointer chasing inside the hot loop.
* **Zero-Overhead Iteration**: Powered by native `for range` over functions (`iter.Seq`), allowing the Go compiler to perform aggressive loop inlining directly over the memory pages.
* **Deterministic $O(1)$ Filter**: Querying specific entities via the **Centralized Record System** takes constant time regardless of the total entity count ($N$) by mapping IDs directly to `(ChunkIndex, RowIndex)` coordinates.
* **Hardware Prefetching Optimization**: View structures are optimized to keep the prefetcher strictly focused on the data stream, minimizing cache pollution during iteration.
## Execution Planning & Consistency
* **Deferred Commands**: State consistency is maintained via `Commands`. Structural changes (add/remove) are buffered and applied during explicit `Sync()` points to ensure memory safety and cache integrity.
* **Thread-Safe Concurrency**: Native support for `RunParallel` execution. GOKe provides the infrastructure for multi-core scaling, assuming the developer ensures disjoint component sets to avoid race conditions.
# ⏱️ Performance & Scalability
> The engine is engineered for extreme scalability and deterministic performance. By utilizing a **Centralized Record System** (dense array lookup) instead of traditional hash maps, we have effectively decoupled both structural changes and query performance from the total entity count ($N$).
GOKe delivers near-metal speeds by eliminating heap allocations and leveraging L1/L2 cache locality.
| Category | Operation | Performance (1024 Baseline) | Allocs | Technical Mechanism |
| :--- | :--- | :--- | :--- | :--- |
| **Throughput** | **Iteration (View.All)** | **0.35 - 2.06 ns/ent** | **0** | Linear SoA (0-10 components) |
| **Subset Query** | **Filter (per-entity)** | **3.20 - 11.33 ns/ent** | **0** | Per-entity record lookup + pointer math |
| **Structural** | **Batch Create (1024 ent.)** | **8 - 20 ns/ent** | 4 | Blueprint-based pages |
| **Structural** | **Migrate Component** | **36.08 ns/op** | **0** | Archetype Move (Insert) |
| **Structural** | **Add Tag** | **33.96 ns/op** | **0** | Archetype Move (Metadata) |
| **Structural** | **Remove Component** | **7.58 ns/op** | **0** | Swap-and-pop |
| **Structural** | **Remove Entity** | **2.83 ns/op** | **0** | Index Recycling |
| **Access** | **Get Component** | **4.67 ns/op** | **0** | Inlined Record Lookup |
> 📊 **Deep Dive**: For a full breakdown of hardware specs, stress tests, and $O(N)$ vs $O(1)$ scaling charts, see [**BENCHMARKS.md**](./BENCHMARKS.md).
> 🔬 **Cross-framework comparison**: Benchmarks against other Go ECS libraries (Arche, Donburi, Ento, etc.) are maintained in a dedicated project — [**go-ecs-benchmarks**](https://github.com/mlange-42/go-ecs-benchmarks) by [@mlange-42](https://github.com/mlange-42). ⚠️ Before drawing conclusions, verify which GOKe version (tag) is used in the comparison — published results may lag behind the main branch.
### Reproducing Results
Run the suite on your own hardware:
```bash
go test -bench=. ./... -benchmem
```
# 🗺️ Roadmap
Current development focus and planned improvements:
* **Ebitengine Integration:** Dedicated helpers for seamless state synchronization between GOKe systems and Ebitengine's loop — partially prototyped in the [ebiten-demo](./examples/ebiten-demo/main.go), with the goal of extracting it into a separate companion repository.
* **Entity Relations via Tags:** Extend the Tag system to model relationships between entities (parent-child, links, ownership, ...) — adding relational semantics on top of the existing archetype-mask machinery, without sacrificing the zero-allocation hot loop.
> 🛠️ **Live Feature Tracker**
> We manage our long-term goals through GitHub Issues. View all planned core engine expansions and functional capabilities here:
> [**Explore all Pending Features ↗**](https://github.com/kjkrol/goke/issues?q=state%3Aopen%20label%3Afeature)
# License
GOKe is licensed under the MIT License. See the LICENSE [file](./LICENSE) for more details.
# 📖 Documentation
* **API Reference**: Detailed documentation and examples are available on [**pkg.go.dev**](https://pkg.go.dev/github.com/kjkrol/goke).
* **Wiki & Guides**: For a step-by-step deep dive into building your first simulation, check the [**Getting Started with GOKe**](https://github.com/kjkrol/goke/wiki/Getting-Started-with-GOKe) guide.
* **Internal Mechanics**: For a technical breakdown of the engine's core, check the `doc.go` files within the `ecs` packages.