Skip to content

Skip output buffering for pure text slots#163

Open
SanderMuller wants to merge 1 commit intolivewire:mainfrom
SanderMuller:autoresearch/text-slot-fast-path
Open

Skip output buffering for pure text slots#163
SanderMuller wants to merge 1 commit intolivewire:mainfrom
SanderMuller:autoresearch/text-slot-fast-path

Conversation

@SanderMuller
Copy link
Copy Markdown
Contributor

When a slot contains only plain text (no PHP blocks), use a literal string instead of ob_start/ob_get_clean + trim. Eliminates three function calls per text slot per render.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 29, 2026

Benchmark Result: Slot

Attempt Blade Blaze Improvement
#1 * 142.14ms 9.46ms 93.3%
#2 146.85ms 9.43ms 93.6%
#3 * 151.24ms 9.52ms 93.7%
#4 146.93ms 9.50ms 93.5%
#5 145.90ms 9.55ms 93.5%
#6 143.80ms 9.36ms 93.5%
#7 146.81ms 9.46ms 93.6%
#8 147.62ms 9.42ms 93.6%
#9 146.85ms 9.35ms 93.6%
#10 148.11ms 9.41ms 93.6%
Snapshot 150.69ms 10.35ms 93.1%
Result 146.85ms (-2.5%) 9.43ms (-8.9%) 93.6% (+0.5%)

Median of 10 attempts (* = outlier, excluded from result), 5000 iterations x 10 rounds, 20.75s 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 345.87ms 16.22ms 95.3%
#2 347.59ms 16.28ms 95.3%
#3 351.52ms 16.14ms 95.4%
#4 349.15ms 16.25ms 95.3%
#5 353.13ms 15.89ms 95.5%
#6 349.80ms 16.45ms 95.3%
#7 349.44ms 16.31ms 95.3%
#8 349.99ms 16.32ms 95.3%
#9 345.13ms 15.89ms 95.4%
#10 352.08ms 16.15ms 95.4%
Snapshot 353.36ms 16.16ms 95.4%
Result 349.62ms (~) 16.24ms (~) 95.4% (~)

Median of 10 attempts, 5000 iterations x 10 rounds, 46.04s 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: Named Slots

Attempt Blade Blaze Improvement
#1 237.98ms 13.13ms 94.5%
#2 236.94ms 13.11ms 94.5%
#3 * 227.37ms 13.39ms 94.1%
#4 233.23ms 13.06ms 94.4%
#5 230.68ms 13.04ms 94.3%
#6 232.40ms 13.05ms 94.4%
#7 233.16ms 13.05ms 94.4%
#8 233.64ms 13.06ms 94.4%
#9 227.61ms 13.06ms 94.3%
#10 229.47ms 13.12ms 94.3%
Snapshot 232.04ms 16.24ms 93%
Result 233.16ms (~) 13.06ms (-19.6%) 94.4% (+1.4%)

Median of 10 attempts (* = outlier, excluded from result), 5000 iterations x 10 rounds, 31.57s 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

What if there are Blade directives / components in the slot?

@SanderMuller SanderMuller force-pushed the autoresearch/text-slot-fast-path branch 3 times, most recently from 868a3f2 to 2d37ca9 Compare March 29, 2026 17:34
When a slot contains only plain text (no PHP blocks, Blade echo syntax,
or directives), use a literal string instead of ob_start/ob_get_clean
+ trim. Eliminates three function calls per text slot per render.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@SanderMuller SanderMuller force-pushed the autoresearch/text-slot-fast-path branch from 2d37ca9 to 37e0e2d Compare March 29, 2026 17:37
@SanderMuller
Copy link
Copy Markdown
Contributor Author

/benchmark slot

@SanderMuller
Copy link
Copy Markdown
Contributor Author

/benchmark named-slots

@SanderMuller
Copy link
Copy Markdown
Contributor Author

What if there are Blade directives / components in the slot?

You were right. I've added 2 tests that failed and now pass. Not the happiest about the several str_contains checks, though. Do you have a better idea? The performance gains on slot and named-slots seem pretty great

{
// Fast path: if content is plain text (no PHP, Blade syntax, or directives),
// use a literal string instead of output buffering.
if (! $this->manager->isFolding() && ! str_contains($content, '<?') && ! str_contains($content, '{{') && ! str_contains($content, '{!!') && ! str_contains($content, '@')) {
Copy link
Copy Markdown
Contributor

@ghabriel25 ghabriel25 Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe extract as a function for readability? Like

Edited

protected function containsAll(string $haystack, array $needles): bool
{
    return array_reduce($needles, fn($a, $n) => $a && str_contains($haystack, $n), true);
}

protected function containsAny(string $haystack, array $needles): bool
{
    return array_reduce($needles, fn($a, $n) => $a || str_contains($haystack, $n), false);
}

Reference: https://www.php.net/manual/en/function.str-contains.php#128796

@ganyicz
Copy link
Copy Markdown
Collaborator

ganyicz commented Mar 29, 2026

We do these types of checks in multiple places in the codebase, maybe there's a way to extract that globally somehow?

But overall I'm not super confident by this. I see we're escaping ' and \\, what if the slot content already has escaped escapes \\\\? The string checks are naive, having an email inside a slot breaks the optimization.

While the gains are tempting I'm not sure if this is the best way to achieve this. We're planning to integrate Forte for full static analysis which would allow us to check if the content is truly static. I think we hold off until then.

Thoughts?

@SanderMuller
Copy link
Copy Markdown
Contributor Author

We do these types of checks in multiple places in the codebase, maybe there's a way to extract that globally somehow?

But overall I'm not super confident by this. I see we're escaping ' and \\, what if the slot content already has escaped escapes \\\\? The string checks are naive, having an email inside a slot breaks the optimization.

While the gains are tempting I'm not sure if this is the best way to achieve this. We're planning to integrate Forte for full static analysis which would allow us to check if the content is truly static. I think we hold off until then.

Thoughts?

It currently does handle the escaped escapes properly.

Email does indeed break the optimization, but I don't think that's that bad.

We could however delay it until Forte integration, that could be a more solid implementation. It would also slow down compilation speed, significant for cold boots. Any thought about hooking into php artisan view:cache or something similar once the compilation is slower?

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.

3 participants