Skip to content

Commit 22ced65

Browse files
committed
feat(queries): Task fn replaces ForeachChannel
This simplified API uses ~40% less memory and reduces ~55% the number of allocations.
1 parent ebb6e56 commit 22ced65

File tree

4 files changed

+834
-24
lines changed

4 files changed

+834
-24
lines changed

README.md

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,8 @@ for result := range query.Foreach(nil) {
112112
transformData(result.A)
113113
}
114114
```
115-
The Foreach function receives a function to pre-filter the results, and returns an iterator.
115+
116+
DEPRECATED: The Foreach function receives a function to pre-filter the results, and returns an iterator.
116117

117118
For faster performances, you can use concurrency with the function ForeachChannel:
118119
```go
@@ -143,6 +144,21 @@ func runWorkers(workersNumber int, worker func(int)) {
143144
}
144145
```
145146

147+
The Task function replaces the previous ForeachChannel function: it gives a simpler API with smaller memory footprint, for similar performances.
148+
149+
It is useful for executing code on all queried entities, by dispatching the work across a defined number of workers.
150+
Internally, this function leverages the iterator typically obtained through the Foreach function.
151+
152+
The number of workers should be considered based on the task's complexity:
153+
dispatching has a minmal overhead that might outweigh the benefits if the entity count to worker count ratio is not appropriate.
154+
```go
155+
query := volt.CreateQuery2[transformComponent, meshComponent](world, volt.QueryConfiguration{OptionalComponents: []volt.OptionalComponent{meshComponentId}})
156+
157+
query.Task(4, nil, func(result volt.QueryResult2[transformComponent, meshComponent]) {
158+
transformData(result.A)
159+
})
160+
```
161+
146162
Queries exist for 1 to 8 Components.
147163

148164
You can also get the number of entities, without looping on each:
@@ -255,21 +271,22 @@ goarch: amd64
255271
pkg: benchmark
256272
cpu: AMD Ryzen 7 5800X 8-Core Processor
257273

258-
| Benchmark | Iterations | ns/op | B/op | Allocs/op |
259-
|---------------------------------|------------|-----------|------------|-----------|
260-
| BenchmarkCreateEntityArche-16 | 171 | 7138387 | 11096954 | 61 |
261-
| BenchmarkIterateArche-16 | 2798 | 429744 | 354 | 4 |
262-
| BenchmarkAddArche-16 | 253 | 4673362 | 122153 | 100000 |
263-
| BenchmarkRemoveArche-16 | 247 | 4840772 | 100000 | 100000 |
264-
| BenchmarkCreateEntityUECS-16 | 27 | 38852089 | 49119503 | 200146 |
265-
| BenchmarkIterateUECS-16 | 4892 | 235333 | 128 | 3 |
266-
| BenchmarkAddUECS-16 | 28 | 38982533 | 4721942 | 100005 |
267-
| BenchmarkRemoveUECS-16 | 30 | 40290316 | 3336712 | 100000 |
268-
| BenchmarkCreateEntityVolt-16 | 63 | 18836136 | 35181458 | 100101 |
269-
| BenchmarkIterateVolt-16 | 3619 | 337764 | 256 | 8 |
270-
| BenchmarkIterateConcurrentlyVolt-16 | 9164 | 121653 | 3324 | 91 |
271-
| BenchmarkAddVolt-16 | 103 | 11379690 | 4313182 | 300000 |
272-
| BenchmarkRemoveVolt-16 | 146 | 7647252 | 400001 | 100000 |
274+
| Benchmark | Iterations | ns/op | B/op | Allocs/op |
275+
|--------------------------------------------------|-------------|-----------|------------|-----------|
276+
| BenchmarkCreateEntityArche-16 | 171 | 7138387 | 11096954 | 61 |
277+
| BenchmarkIterateArche-16 | 2798 | 429744 | 354 | 4 |
278+
| BenchmarkAddArche-16 | 253 | 4673362 | 122153 | 100000 |
279+
| BenchmarkRemoveArche-16 | 247 | 4840772 | 100000 | 100000 |
280+
| BenchmarkCreateEntityUECS-16 | 27 | 38852089 | 49119503 | 200146 |
281+
| BenchmarkIterateUECS-16 | 4892 | 235333 | 128 | 3 |
282+
| BenchmarkAddUECS-16 | 28 | 38982533 | 4721942 | 100005 |
283+
| BenchmarkRemoveUECS-16 | 30 | 40290316 | 3336712 | 100000 |
284+
| BenchmarkCreateEntityVolt-16 | 63 | 18836136 | 35181458 | 100101 |
285+
| BenchmarkIterateVolt-16 | 3619 | 337764 | 256 | 8 |
286+
| (DEPRECATED) BenchmarkIterateConcurrentlyVolt-16 | 9164 | 121653 | 3324 | 91 |
287+
| BenchmarkTaskVolt-16 | 9859 | 119525 | 1847 | 38 |
288+
| BenchmarkAddVolt-16 | 103 | 11379690 | 4313182 | 300000 |
289+
| BenchmarkRemoveVolt-16 | 146 | 7647252 | 400001 | 100000 |
273290

274291
These results show a few things:
275292
- Arche is the fastest tool for writes operations. In our game development though we would rather lean towards fastest read operations, because the games loops will read way more often than write.

benchmark/volt_test.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,27 @@ func BenchmarkIterateConcurrentlyVolt(b *testing.B) {
7676
b.ReportAllocs()
7777
}
7878

79+
func BenchmarkTaskVolt(b *testing.B) {
80+
world := volt.CreateWorld(ENTITIES_COUNT)
81+
volt.RegisterComponent[testTransform](world, &volt.ComponentConfig[testTransform]{})
82+
volt.RegisterComponent[testTag](world, &volt.ComponentConfig[testTag]{})
83+
84+
for i := 0; i < ENTITIES_COUNT; i++ {
85+
id := world.CreateEntity()
86+
volt.AddComponent[testTransform](world, id, testTransform{})
87+
volt.AddComponent[testTag](world, id, testTag{})
88+
}
89+
90+
for b.Loop() {
91+
query := volt.CreateQuery2[testTransform, testTag](world, volt.QueryConfiguration{})
92+
query.Task(WORKERS, nil, func(result volt.QueryResult2[testTransform, testTag]) {
93+
transformData(result.A)
94+
})
95+
}
96+
97+
b.ReportAllocs()
98+
}
99+
79100
func BenchmarkAddVolt(b *testing.B) {
80101
b.StopTimer()
81102

0 commit comments

Comments
 (0)