Skip to content

Reduce per-call overhead for self-closing components#162

Open
SanderMuller wants to merge 2 commits intolivewire:mainfrom
SanderMuller:autoresearch/self-closing-call-overhead
Open

Reduce per-call overhead for self-closing components#162
SanderMuller wants to merge 2 commits intolivewire:mainfrom
SanderMuller:autoresearch/self-closing-call-overhead

Conversation

@SanderMuller
Copy link
Copy Markdown
Contributor

@SanderMuller SanderMuller commented Mar 29, 2026

An optimization to the compiled caller output for self-closing components:

Use function_exists() guard instead of calling ensureRequired() on every render. After the first render, function_exists() is a C-level check that's faster than a PHP method call + hash table lookup.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 29, 2026

Benchmark Result: Attributes

Attempt Blade Blaze Improvement
#1 161.04ms 10.02ms 93.8%
#2 170.34ms 10.36ms 93.9%
#3 164.25ms 10.26ms 93.8%
#4 169.78ms 10.24ms 94%
#5 166.10ms 10.14ms 93.9%
#6 162.84ms 10.12ms 93.8%
#7 163.25ms 10.03ms 93.9%
#8 162.42ms 10.33ms 93.6%
#9 163.33ms 10.02ms 93.9%
#10 160.52ms 10.10ms 93.7%
Snapshot 164.87ms 10.37ms 93.7%
Result 163.29ms (~) 10.13ms (-2.3%) 93.8% (~)

Median of 10 attempts, 5000 iterations x 10 rounds, 22.98s total

To run a specific benchmark, comment /benchmark <name> where name is one of: attributes, aware, class, default, forwarding, merge, named-slots, no-attributes, slot

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 29, 2026

Benchmark Result: Default

Attempt Blade Blaze Improvement
#1 248.52ms 13.60ms 94.5%
#2 247.29ms 13.61ms 94.5%
#3 248.06ms 13.56ms 94.5%
#4 * 247.18ms 13.74ms 94.4%
#5 * 250.16ms 13.48ms 94.6%
#6 249.50ms 13.58ms 94.6%
#7 * 247.64ms 13.83ms 94.4%
#8 249.03ms 13.62ms 94.5%
#9 247.08ms 13.58ms 94.5%
#10 246.44ms 13.59ms 94.5%
Snapshot 248.07ms 13.74ms 94.5%
Result 248.06ms (~) 13.59ms (~) 94.5% (~)

Median of 10 attempts (* = outlier, excluded from result), 5000 iterations x 10 rounds, 33.28s total

To run a specific benchmark, comment /benchmark <name> where name is one of: attributes, aware, class, default, forwarding, merge, named-slots, no-attributes, slot

@ganyicz
Copy link
Copy Markdown
Collaborator

ganyicz commented Mar 29, 2026

Love the function_exists check. Not a big fan of the refactor, makes it difficult to read, any reasoning behind that?

And I'm almost certain removing pushData would break things, flux:delegate-component uses the data stack and could be used inside another component without slots. Can we double check this?

@SanderMuller
Copy link
Copy Markdown
Contributor Author

Love the function_exists check. Not a big fan of the refactor, makes it difficult to read, any reasoning behind that?

And I'm almost certain removing pushData would break things, flux:delegate-component uses the data stack and could be used inside another component without slots. Can we double check this?

Thanks for the feedback, I'm looking into it

@SanderMuller SanderMuller force-pushed the autoresearch/self-closing-call-overhead branch from cf0677a to ebd6577 Compare March 29, 2026 17:22
Use function_exists() guard instead of calling ensureRequired() on every
render. After the first render, function_exists() is a C-level check
that's faster than a PHP method call + hash table lookup.

Also adds a test verifying that self-closing components correctly
propagate data to @AWare descendants via the data stack.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@SanderMuller SanderMuller force-pushed the autoresearch/self-closing-call-overhead branch from ebd6577 to 4b96d2b Compare March 29, 2026 17:26
@SanderMuller
Copy link
Copy Markdown
Contributor Author

Love the function_exists check. Not a big fan of the refactor, makes it difficult to read, any reasoning behind that?

And I'm almost certain removing pushData would break things, flux:delegate-component uses the data stack and could be used inside another component without slots. Can we double check this?

You were right about the pushData breaking change. I've added a failing test to prevent this regression in the future. And I think the if/else is quite large here, so early return can be nice, however I agree that it makes the diff in this PR hard to read, so I've reverted it to the if/else structure, which indeed helps a lot to review this change :)

@ganyicz
Copy link
Copy Markdown
Collaborator

ganyicz commented Mar 29, 2026

Nice, thanks! Wondering if this shouldn't be inside ensureRequired instead so the resolve function can benefit from this as well? Also we can probably drop the $required cache in runtime now that we have this.

Move the function_exists() check into ensureRequired() so both the
compiled caller and resolve() benefit from the C-level guard. Drop
the $required hash table which is now redundant.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@SanderMuller SanderMuller force-pushed the autoresearch/self-closing-call-overhead branch from 1bc8c94 to d31efff Compare March 29, 2026 19:59
@SanderMuller
Copy link
Copy Markdown
Contributor Author

/benchmark attributes

@SanderMuller
Copy link
Copy Markdown
Contributor Author

Nice, thanks! Wondering if this shouldn't be inside ensureRequired instead so the resolve function can benefit from this as well? Also we can probably drop the $required cache in runtime now that we have this.

Yeah good idea, applied

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants