@@ -8,6 +8,32 @@ constexpr uint32_t EntitiesPerArchetype = 1'000;
8
8
9
9
using TBenchmarkTypes = cnt::darray<ecs::Entity>;
10
10
11
+ struct Position {
12
+ float x, y, z;
13
+ };
14
+ struct Velocity {
15
+ float x, y, z;
16
+ };
17
+ struct Rotation {
18
+ float x, y, z, w;
19
+ };
20
+ struct Scale {
21
+ float x, y, z;
22
+ };
23
+ struct Direction {
24
+ float x, y, z;
25
+ };
26
+ struct Health {
27
+ int value;
28
+ int max;
29
+ };
30
+ struct IsEnemy {
31
+ bool value;
32
+ };
33
+ struct Dummy {
34
+ int value[24 ];
35
+ };
36
+
11
37
auto create_archetypes (ecs::World& w, uint32_t archetypes, uint32_t maxIdsPerArchetype) {
12
38
GAIA_ASSERT (archetypes > 0 );
13
39
GAIA_ASSERT (maxIdsPerArchetype > 0 );
@@ -85,6 +111,77 @@ void bench_query_each_iter(picobench::state& state, TQuery& query) {
85
111
}
86
112
}
87
113
114
+ template <uint32_t NViews, bool ViewWithIndex>
115
+ void bench_query_each_view (picobench::state& state, ecs::World& w) {
116
+ /* We want to benchmark the hot-path. In real-world scenarios queries are cached so cache them now */
117
+ auto q = w.query ().all <Position>();
118
+ if constexpr (NViews > 1 )
119
+ q.all <Velocity>();
120
+ if constexpr (NViews > 2 )
121
+ q.all <Scale>();
122
+ if constexpr (NViews > 3 )
123
+ q.all <Direction>();
124
+ if constexpr (NViews > 4 )
125
+ q.all <Health>();
126
+
127
+ [[maybe_unused]] bool isEmpty = q.empty ();
128
+ gaia::dont_optimize (isEmpty);
129
+
130
+ state.stop_timer ();
131
+ for (auto _: state) {
132
+ (void )_;
133
+
134
+ state.stop_timer ();
135
+ q.each ([&](ecs::Iter& it) {
136
+ GAIA_EACH (it) {
137
+ state.start_timer ();
138
+
139
+ if constexpr (ViewWithIndex) {
140
+ auto v1 = it.template view <Position>(0 );
141
+ gaia::dont_optimize (v1);
142
+ if constexpr (NViews > 2 ) {
143
+ auto v2 = it.template view <Velocity>(1 );
144
+ gaia::dont_optimize (v2);
145
+ }
146
+ if constexpr (NViews > 2 ) {
147
+ auto v3 = it.template view <Scale>(2 );
148
+ gaia::dont_optimize (v3);
149
+ }
150
+ if constexpr (NViews > 3 ) {
151
+ auto v4 = it.template view <Direction>(3 );
152
+ gaia::dont_optimize (v4);
153
+ }
154
+ if constexpr (NViews > 4 ) {
155
+ auto v5 = it.template view <Health>(4 );
156
+ gaia::dont_optimize (v5);
157
+ }
158
+ } else {
159
+ auto v1 = it.template view <Position>();
160
+ gaia::dont_optimize (v1);
161
+ if constexpr (NViews > 2 ) {
162
+ auto v2 = it.template view <Velocity>();
163
+ gaia::dont_optimize (v2);
164
+ }
165
+ if constexpr (NViews > 2 ) {
166
+ auto v3 = it.template view <Scale>();
167
+ gaia::dont_optimize (v3);
168
+ }
169
+ if constexpr (NViews > 3 ) {
170
+ auto v4 = it.template view <Direction>();
171
+ gaia::dont_optimize (v4);
172
+ }
173
+ if constexpr (NViews > 4 ) {
174
+ auto v5 = it.template view <Health>();
175
+ gaia::dont_optimize (v5);
176
+ }
177
+ }
178
+
179
+ state.stop_timer ();
180
+ }
181
+ });
182
+ }
183
+ }
184
+
88
185
template <bool UseCachedQuery, uint32_t QueryComponents>
89
186
void BM_BuildQuery (picobench::state& state) {
90
187
ecs::World w;
@@ -150,6 +247,88 @@ void BM_Each_Iter(picobench::state& state, uint32_t ArchetypeCount, uint32_t Max
150
247
bench_query_each_iter<IterKind>(state, query);
151
248
}
152
249
250
+ template <uint32_t NViews, bool ViewWithIndex>
251
+ void BM_Each_View (picobench::state& state) {
252
+ ecs::World w;
253
+
254
+ // Create some archetypes for a good measure
255
+ create_archetypes (w, 1000 , 10 );
256
+
257
+ // Register our components. The order is random so it does not match
258
+ // the order in queries.
259
+ {
260
+ (void )w.add <Scale>();
261
+ (void )w.add <Position>();
262
+ (void )w.add <Direction>();
263
+ (void )w.add <Health>();
264
+ (void )w.add <IsEnemy>();
265
+ (void )w.add <Velocity>();
266
+ (void )w.add <Rotation>();
267
+ }
268
+
269
+ // Create our native component archetypes
270
+ constexpr uint32_t NEntities = 1000 ;
271
+ {
272
+ auto e = w.add ();
273
+ w.build (e) //
274
+ .add <Position>()
275
+ .add <Velocity>();
276
+ w.copy_n (e, NEntities - 1 );
277
+ }
278
+ {
279
+ auto e = w.add ();
280
+ w.build (e) //
281
+ .add <Position>()
282
+ .add <Velocity>()
283
+ .add <Rotation>();
284
+ w.copy_n (e, NEntities - 1 );
285
+ }
286
+ {
287
+ auto e = w.add ();
288
+ w.build (e) //
289
+ .add <Position>()
290
+ .add <Velocity>()
291
+ .add <Rotation>()
292
+ .add <Scale>();
293
+ w.copy_n (e, NEntities - 1 );
294
+ }
295
+ {
296
+ auto e = w.add ();
297
+ w.build (e) //
298
+ .add <Position>()
299
+ .add <Velocity>()
300
+ .add <Rotation>()
301
+ .add <Scale>()
302
+ .add <Direction>();
303
+ w.copy_n (e, NEntities - 1 );
304
+ }
305
+ {
306
+ auto e = w.add ();
307
+ w.build (e) //
308
+ .add <Position>()
309
+ .add <Velocity>()
310
+ .add <Rotation>()
311
+ .add <Scale>()
312
+ .add <Direction>()
313
+ .add <Health>();
314
+ w.copy_n (e, NEntities - 1 );
315
+ }
316
+ {
317
+ auto e = w.add ();
318
+ w.build (e) //
319
+ .add <Position>()
320
+ .add <Velocity>()
321
+ .add <Rotation>()
322
+ .add <Scale>()
323
+ .add <Direction>()
324
+ .add <Health>()
325
+ .add <IsEnemy>();
326
+ w.copy_n (e, NEntities - 1 );
327
+ }
328
+
329
+ bench_query_each_view<NViews, ViewWithIndex>(state, w);
330
+ }
331
+
153
332
#define DEFINE_EACH_ITER (IterKind, ArchetypeCount, MaxIdsPerArchetype, QueryComponents ) \
154
333
void BM_Each_##IterKind##_##ArchetypeCount##_##QueryComponents(picobench::state& state) { \
155
334
BM_Each_Iter<true , QueryComponents, ecs::IterKind>(state, ArchetypeCount, MaxIdsPerArchetype); \
@@ -159,6 +338,11 @@ void BM_Each_Iter(picobench::state& state, uint32_t ArchetypeCount, uint32_t Max
159
338
BM_Each_Iter<false , QueryComponents, ecs::IterKind>(state, ArchetypeCount, MaxIdsPerArchetype); \
160
339
}
161
340
341
+ #define DEFINE_EACH_VIEW (NViews, ViewWithIndex ) \
342
+ void BM_Each_View_##NViews##_##ViewWithIndex(picobench::state& state) { \
343
+ BM_Each_View<NViews, ViewWithIndex>(state); \
344
+ }
345
+
162
346
DEFINE_EACH_ITER (IterAll, 1 , 1 , 1 )
163
347
DEFINE_EACH_ITER(Iter, 1 , 1 , 1 )
164
348
DEFINE_EACH_U_ITER(Iter, 1 , 1 , 1 )
@@ -179,6 +363,17 @@ DEFINE_EACH_U_ITER(Iter, 1000, 10, 3);
179
363
DEFINE_EACH_U_ITER (Iter, 1000 , 10 , 5 );
180
364
DEFINE_EACH_U_ITER (Iter, 1000 , 10 , 7 );
181
365
366
+ DEFINE_EACH_VIEW (1 , false );
367
+ DEFINE_EACH_VIEW (1 , true );
368
+ DEFINE_EACH_VIEW (2 , false );
369
+ DEFINE_EACH_VIEW (2 , true );
370
+ DEFINE_EACH_VIEW (3 , false );
371
+ DEFINE_EACH_VIEW (3 , true );
372
+ DEFINE_EACH_VIEW (4 , false );
373
+ DEFINE_EACH_VIEW (4 , true );
374
+ DEFINE_EACH_VIEW (5 , false );
375
+ DEFINE_EACH_VIEW (5 , true );
376
+
182
377
#define PICO_SETTINGS () iterations({8192 }).samples(3 )
183
378
#define PICO_SETTINGS_1 () iterations({8192 }).samples(1 )
184
379
#define PICO_SETTINGS_SANI () iterations({8 }).samples(1 )
@@ -260,6 +455,18 @@ int main(int argc, char* argv[]) {
260
455
PICOBENCH_REG (BM_BuildQuery_U_5).PICO_SETTINGS ().label (" (u) 5 comps" ); // uncached
261
456
PICOBENCH_REG (BM_BuildQuery_7).PICO_SETTINGS ().label (" 7 comps" );
262
457
PICOBENCH_REG (BM_BuildQuery_U_7).PICO_SETTINGS ().label (" (u) 7 comps" ); // uncached
458
+
459
+ PICOBENCH_SUITE_REG (" Iter view" );
460
+ PICOBENCH_REG (BM_Each_View_1_false).PICO_SETTINGS ().label (" view 1 comp" );
461
+ PICOBENCH_REG (BM_Each_View_1_true).PICO_SETTINGS ().label (" view 1 comp, idx" );
462
+ PICOBENCH_REG (BM_Each_View_2_false).PICO_SETTINGS ().label (" view 2 comp" );
463
+ PICOBENCH_REG (BM_Each_View_2_true).PICO_SETTINGS ().label (" view 2 comp, idx" );
464
+ PICOBENCH_REG (BM_Each_View_3_false).PICO_SETTINGS ().label (" view 3 comp" );
465
+ PICOBENCH_REG (BM_Each_View_3_true).PICO_SETTINGS ().label (" view 3 comp, idx" );
466
+ PICOBENCH_REG (BM_Each_View_4_false).PICO_SETTINGS ().label (" view 4 comp" );
467
+ PICOBENCH_REG (BM_Each_View_4_true).PICO_SETTINGS ().label (" view 4 comp, idx" );
468
+ PICOBENCH_REG (BM_Each_View_5_false).PICO_SETTINGS ().label (" view 5 comp" );
469
+ PICOBENCH_REG (BM_Each_View_5_true).PICO_SETTINGS ().label (" view 5 comp, idx" );
263
470
}
264
471
}
265
472
0 commit comments