Skip to content

Commit 72a012f

Browse files
committed
Add rule optimization for simple component queries
1 parent 7698acf commit 72a012f

5 files changed

Lines changed: 368 additions & 12 deletions

File tree

flecs.c

Lines changed: 184 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35571,8 +35571,9 @@ typedef struct ecs_rule_var_t {
3557135571
/* -- Instruction kinds -- */
3557235572
typedef enum {
3557335573
EcsRuleAnd, /* And operator: find or match id against variable source */
35574+
EcsRuleAndId, /* And operator for fixed id (no wildcards/variables) */
3557435575
EcsRuleWith, /* Match id against fixed or variable source */
35575-
EcsRuleAndAny, /* And operation with support for matching Any src/id */
35576+
EcsRuleAndAny, /* And operator with support for matching Any src/id */
3557635577
EcsRuleTrav, /* Support for transitive/reflexive queries */
3557735578
EcsRuleIdsRight, /* Find ids in use that match (R, *) wildcard */
3557835579
EcsRuleIdsLeft, /* Find ids in use that match (*, T) wildcard */
@@ -35584,6 +35585,7 @@ typedef enum {
3558435585
EcsRuleSetVars, /* Populate it.sources from variables */
3558535586
EcsRuleSetThis, /* Populate This entity variable */
3558635587
EcsRuleSetFixed, /* Set fixed source entity ids */
35588+
EcsRuleSetIds, /* Set fixed (component) ids */
3558735589
EcsRuleContain, /* Test if table contains entity */
3558835590
EcsRulePairEq, /* Test if both elements of pair are the same */
3558935591
EcsRuleSetCond, /* Set conditional value for EcsRuleJmpCondFalse */
@@ -35815,6 +35817,7 @@ void flecs_rule_trav_cache_fini(
3581535817
static bool flecs_rule_op_is_test[] = {
3581635818
[EcsRuleAnd] = true,
3581735819
[EcsRuleAndAny] = true,
35820+
[EcsRuleAndId] = true,
3581835821
[EcsRuleWith] = true,
3581935822
[EcsRuleTrav] = true,
3582035823
[EcsRuleContain] = true,
@@ -36799,6 +36802,44 @@ void flecs_rule_insert_pair_eq(
3679936802
flecs_rule_op_insert(&contains, ctx);
3680036803
}
3680136804

36805+
static
36806+
bool flecs_rule_term_fixed_id(
36807+
ecs_filter_t *filter,
36808+
ecs_term_t *term)
36809+
{
36810+
/* Transitive/inherited terms have variable ids */
36811+
if (term->flags & (EcsTermTransitive|EcsTermIdInherited)) {
36812+
return false;
36813+
}
36814+
36815+
/* Or terms can match different ids */
36816+
if (term->oper == EcsOr) {
36817+
return false;
36818+
}
36819+
if ((term != filter->terms) && term[-1].oper == EcsOr) {
36820+
return false;
36821+
}
36822+
36823+
/* Wildcards can assume different ids */
36824+
if (ecs_id_is_wildcard(term->id)) {
36825+
return false;
36826+
}
36827+
36828+
/* Any terms can have fixed ids, but they require special handling */
36829+
if (term->flags & (EcsTermMatchAny|EcsTermMatchAnySrc)) {
36830+
return false;
36831+
}
36832+
36833+
/* First terms that are Not or Optional require special handling */
36834+
if (term->oper == EcsNot || term->oper == EcsOptional) {
36835+
if (term == filter->terms) {
36836+
return false;
36837+
}
36838+
}
36839+
36840+
return true;
36841+
}
36842+
3680236843
static
3680336844
void flecs_rule_compile_term(
3680436845
ecs_world_t *world,
@@ -36824,11 +36865,19 @@ void flecs_rule_compile_term(
3682436865
ecs_assert(ecs_term_id_is_set(&term->second), ECS_INTERNAL_ERROR, NULL);
3682536866
op.kind = EcsRuleTrav;
3682636867
} else {
36827-
if (term->flags & EcsTermMatchAny || term->flags & EcsTermMatchAnySrc) {
36868+
if (term->flags & (EcsTermMatchAny|EcsTermMatchAnySrc)) {
3682836869
op.kind = EcsRuleAndAny;
3682936870
}
3683036871
}
3683136872

36873+
/* If term has fixed id, insert simpler instruction that skips dealing with
36874+
* wildcard terms and variables */
36875+
if (flecs_rule_term_fixed_id(&rule->filter, term)) {
36876+
if (op.kind == EcsRuleAnd) {
36877+
op.kind = EcsRuleAndId;
36878+
}
36879+
}
36880+
3683236881
/* Save write state at start of term so we can use it to reliably track
3683336882
* variables got written by this term. */
3683436883
ecs_write_flags_t cond_write_state = ctx->cond_written;
@@ -37038,6 +37087,19 @@ void flecs_rule_compile(
3703837087
}
3703937088
}
3704037089

37090+
/* If the rule contains terms with fixed ids (no wildcards, variables),
37091+
* insert instruction that initializes ecs_iter_t::ids. This allows for the
37092+
* insertion of simpler instructions later on. */
37093+
for (i = 0; i < count; i ++) {
37094+
ecs_term_t *term = &terms[i];
37095+
if (flecs_rule_term_fixed_id(filter, term)) {
37096+
ecs_rule_op_t set_ids = {0};
37097+
set_ids.kind = EcsRuleSetIds;
37098+
flecs_rule_op_insert(&set_ids, &ctx);
37099+
break;
37100+
}
37101+
}
37102+
3704137103
/* Compile query terms to instructions */
3704237104
for (i = 0; i < count; i ++) {
3704337105
ecs_term_t *term = &terms[i];
@@ -37164,6 +37226,7 @@ const char* flecs_rule_op_str(
3716437226
{
3716537227
switch(kind) {
3716637228
case EcsRuleAnd: return "and ";
37229+
case EcsRuleAndId: return "and_id ";
3716737230
case EcsRuleAndAny: return "andany ";
3716837231
case EcsRuleWith: return "with ";
3716937232
case EcsRuleTrav: return "trav ";
@@ -37177,6 +37240,7 @@ const char* flecs_rule_op_str(
3717737240
case EcsRuleSetVars: return "setvars ";
3717837241
case EcsRuleSetThis: return "setthis ";
3717937242
case EcsRuleSetFixed: return "setfix ";
37243+
case EcsRuleSetIds: return "setids ";
3718037244
case EcsRuleContain: return "contain ";
3718137245
case EcsRulePairEq: return "pair_eq ";
3718237246
case EcsRuleSetCond: return "setcond ";
@@ -38045,6 +38109,7 @@ bool flecs_rule_select_w_id(
3804538109
op_ctx->column = flecs_ito(int16_t, tr->column);
3804638110
op_ctx->remaining = flecs_ito(int16_t, tr->count - 1);
3804738111
table = tr->hdr.table;
38112+
flecs_rule_var_set_table(op, op->src.var, table, 0, 0, ctx);
3804838113
} else {
3804938114
tr = (ecs_table_record_t*)op_ctx->it.cur;
3805038115
ecs_assert(tr != NULL, ECS_INTERNAL_ERROR, NULL);
@@ -38053,8 +38118,6 @@ bool flecs_rule_select_w_id(
3805338118
op_ctx->remaining --;
3805438119
}
3805538120

38056-
ecs_var_id_t var_id = op->src.var;
38057-
flecs_rule_var_set_table(op, var_id, table, 0, 0, ctx);
3805838121
flecs_rule_set_match(op, table, op_ctx->column, ctx);
3805938122
return true;
3806038123
}
@@ -38102,7 +38165,6 @@ bool flecs_rule_with(
3810238165
return false;
3810338166
}
3810438167

38105-
op_ctx->idr = idr;
3810638168
op_ctx->column = flecs_ito(int16_t, tr->column);
3810738169
op_ctx->remaining = flecs_ito(int16_t, tr->count);
3810838170
} else {
@@ -38125,13 +38187,103 @@ bool flecs_rule_and(
3812538187
const ecs_rule_run_ctx_t *ctx)
3812638188
{
3812738189
uint64_t written = ctx->written[ctx->op_index];
38128-
if (written & (1 << op->src.var)) {
38190+
if (written & (1ull << op->src.var)) {
3812938191
return flecs_rule_with(op, redo, ctx);
3813038192
} else {
3813138193
return flecs_rule_select(op, redo, ctx);
3813238194
}
3813338195
}
3813438196

38197+
static
38198+
bool flecs_rule_select_id(
38199+
const ecs_rule_op_t *op,
38200+
bool redo,
38201+
const ecs_rule_run_ctx_t *ctx)
38202+
{
38203+
ecs_rule_and_ctx_t *op_ctx = flecs_op_ctx(ctx, and);
38204+
ecs_iter_t *it = ctx->it;
38205+
int8_t field = op->field_index;
38206+
ecs_assert(field != -1, ECS_INTERNAL_ERROR, NULL);
38207+
38208+
if (!redo) {
38209+
ecs_id_t id = it->ids[field];
38210+
ecs_id_record_t *idr = op_ctx->idr;
38211+
if (!idr || idr->id != id) {
38212+
idr = op_ctx->idr = flecs_id_record_get(ctx->world, id);
38213+
if (!idr) {
38214+
return false;
38215+
}
38216+
}
38217+
38218+
if (!flecs_table_cache_iter(&idr->cache, &op_ctx->it)) {
38219+
return false;
38220+
}
38221+
}
38222+
38223+
const ecs_table_record_t *tr = flecs_table_cache_next(
38224+
&op_ctx->it, ecs_table_record_t);
38225+
if (!tr) {
38226+
return false;
38227+
}
38228+
38229+
ecs_table_t *table = tr->hdr.table;
38230+
flecs_rule_var_set_table(op, op->src.var, table, 0, 0, ctx);
38231+
flecs_rule_it_set_column(it, field, tr->column);
38232+
return true;
38233+
}
38234+
38235+
static
38236+
bool flecs_rule_with_id(
38237+
const ecs_rule_op_t *op,
38238+
bool redo,
38239+
const ecs_rule_run_ctx_t *ctx)
38240+
{
38241+
if (redo) {
38242+
return false;
38243+
}
38244+
38245+
ecs_rule_and_ctx_t *op_ctx = flecs_op_ctx(ctx, and);
38246+
ecs_iter_t *it = ctx->it;
38247+
int8_t field = op->field_index;
38248+
ecs_assert(field != -1, ECS_INTERNAL_ERROR, NULL);
38249+
38250+
ecs_table_t *table = flecs_rule_get_table(op, &op->src, EcsRuleSrc, ctx);
38251+
if (!table) {
38252+
return false;
38253+
}
38254+
38255+
ecs_id_t id = it->ids[field];
38256+
ecs_id_record_t *idr = op_ctx->idr;
38257+
if (!idr || idr->id != id) {
38258+
idr = op_ctx->idr = flecs_id_record_get(ctx->world, id);
38259+
if (!idr) {
38260+
return false;
38261+
}
38262+
}
38263+
38264+
const ecs_table_record_t *tr = flecs_id_record_get_table(idr, table);
38265+
if (!tr) {
38266+
return false;
38267+
}
38268+
38269+
flecs_rule_it_set_column(it, field, tr->column);
38270+
return true;
38271+
}
38272+
38273+
static
38274+
bool flecs_rule_and_id(
38275+
const ecs_rule_op_t *op,
38276+
bool redo,
38277+
const ecs_rule_run_ctx_t *ctx)
38278+
{
38279+
uint64_t written = ctx->written[ctx->op_index];
38280+
if (written & (1 << op->src.var)) {
38281+
return flecs_rule_with_id(op, redo, ctx);
38282+
} else {
38283+
return flecs_rule_select_id(op, redo, ctx);
38284+
}
38285+
}
38286+
3813538287
static
3813638288
bool flecs_rule_and_any(
3813738289
const ecs_rule_op_t *op,
@@ -38768,6 +38920,30 @@ bool flecs_rule_setfixed(
3876838920
return true;
3876938921
}
3877038922

38923+
static
38924+
bool flecs_rule_setids(
38925+
const ecs_rule_op_t *op,
38926+
bool redo,
38927+
ecs_rule_run_ctx_t *ctx)
38928+
{
38929+
(void)op;
38930+
const ecs_rule_t *rule = ctx->rule;
38931+
const ecs_filter_t *filter = &rule->filter;
38932+
ecs_iter_t *it = ctx->it;
38933+
38934+
if (redo) {
38935+
return false;
38936+
}
38937+
38938+
int32_t i;
38939+
for (i = 0; i < filter->term_count; i ++) {
38940+
ecs_term_t *term = &filter->terms[i];
38941+
it->ids[term->field_index] = term->id;
38942+
}
38943+
38944+
return true;
38945+
}
38946+
3877138947
/* Check if entity is stored in table */
3877238948
static
3877338949
bool flecs_rule_contain(
@@ -38875,6 +39051,7 @@ bool flecs_rule_run(
3887539051
{
3887639052
switch(op->kind) {
3887739053
case EcsRuleAnd: return flecs_rule_and(op, redo, ctx);
39054+
case EcsRuleAndId: return flecs_rule_and_id(op, redo, ctx);
3887839055
case EcsRuleAndAny: return flecs_rule_and_any(op, redo, ctx);
3887939056
case EcsRuleWith: return flecs_rule_with(op, redo, ctx);
3888039057
case EcsRuleTrav: return flecs_rule_trav(op, redo, ctx);
@@ -38888,6 +39065,7 @@ bool flecs_rule_run(
3888839065
case EcsRuleSetVars: return flecs_rule_setvars(op, redo, ctx);
3888939066
case EcsRuleSetThis: return flecs_rule_setthis(op, redo, ctx);
3889039067
case EcsRuleSetFixed: return flecs_rule_setfixed(op, redo, ctx);
39068+
case EcsRuleSetIds: return flecs_rule_setids(op, redo, ctx);
3889139069
case EcsRuleContain: return flecs_rule_contain(op, redo, ctx);
3889239070
case EcsRulePairEq: return flecs_rule_pair_eq(op, redo, ctx);
3889339071
case EcsRuleJmpCondFalse: return flecs_rule_jmp_if_not(op, redo, ctx);

src/addons/rules/api.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const char* flecs_rule_op_str(
2424
{
2525
switch(kind) {
2626
case EcsRuleAnd: return "and ";
27+
case EcsRuleAndId: return "and_id ";
2728
case EcsRuleAndAny: return "andany ";
2829
case EcsRuleWith: return "with ";
2930
case EcsRuleTrav: return "trav ";
@@ -37,6 +38,7 @@ const char* flecs_rule_op_str(
3738
case EcsRuleSetVars: return "setvars ";
3839
case EcsRuleSetThis: return "setthis ";
3940
case EcsRuleSetFixed: return "setfix ";
41+
case EcsRuleSetIds: return "setids ";
4042
case EcsRuleContain: return "contain ";
4143
case EcsRulePairEq: return "pair_eq ";
4244
case EcsRuleSetCond: return "setcond ";

0 commit comments

Comments
 (0)