Skip to content

ext/standard: handle html entities empty string before processing #19220

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: master
Choose a base branch
from

Conversation

xepozz
Copy link
Contributor

@xepozz xepozz commented Jul 23, 2025

https://gist.github.com/xepozz/8a7cbb52dd2087634d2ddaa90a21acbe

master

❯ ./sapi/cli/php test.php
Result: 0.12489318847656
Result: 0.025581121444702
Result: 0.024285078048706

branch

❯ ./sapi/cli/php test.php
Result: 0.11230993270874
Result: 0.012480974197388
Result: 0.01120400428772

all the tests were made with local php build with enabled debug and so on

performance increases because of eliminating memory allocation, I hope memory usage is also reduced

Copy link
Member

@nielsdos nielsdos left a comment

Choose a reason for hiding this comment

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

Not sure if it's worth optimizing this edge case, anyway here's some feedback

@iluuu1994
Copy link
Member

all the tests were made with local php build with enabled debug and so on

Also note that benchmarks should be done with debug disabled.

@xepozz
Copy link
Contributor Author

xepozz commented Jul 23, 2025

./configure --enable-opcache --with-iconv=$(brew --prefix libiconv) --enable-zend-test

master

❯ ./sapi/cli/php test.php
Result: 0.031224966049194
Result: 0.0051758289337158
Result: 0.0046060085296631
Result: 0.021102905273438

branch

❯ ./sapi/cli/php test.php
Result: 0.027465105056763
Result: 0.002708911895752
Result: 0.0021250247955322
Result: 0.021250009536743

@xepozz
Copy link
Contributor Author

xepozz commented Jul 23, 2025

Not sure if it's worth optimizing this edge case, anyway here's some feedback

Anyway, many functions accept empty string and return modified one.
It would be better to save piece of memory if it's possible, length check is a free check, compared to memory allocation.
I'd add such checks in all the rest functions, should I?

@xepozz
Copy link
Contributor Author

xepozz commented Jul 23, 2025

Result: 0.031224966049194
Result: 0.0051758289337158

The first call generates deprecation error because the function argument is null.
Is the performance degradation caused by the deprecations?

@@ -1320,6 +1320,10 @@ static void php_html_entities(INTERNAL_FUNCTION_PARAMETERS, int all)
Z_PARAM_BOOL(double_encode);
ZEND_PARSE_PARAMETERS_END();

if (EXPECTED(ZSTR_LEN(str) == 0)) {
Copy link
Member

Choose a reason for hiding this comment

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

This most certainly is not expected. You should not just benchmark the case of passing an empty string, but also non-empty strings of varying lengths.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

what do you mean? as you can see the new benchmark is hitting non empty string as well
https://gist.github.com/xepozz/8a7cbb52dd2087634d2ddaa90a21acbe#file-htmlspecialchars-php-L25

Copy link
Contributor Author

Choose a reason for hiding this comment

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

should I replace it with UNEXPECTED(len > 0)?

Copy link
Member

Choose a reason for hiding this comment

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

No, instead, this should not have a branch hint at all.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

hmm, is there a guide how it works?

Copy link
Member

Choose a reason for hiding this comment

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

No guide. But UNEXPECTED should only be used for situations that are actually unexpected: it can move the code to the cold text section and pessimize optimizations. I only use it for error conditions. EXPECTED is less aggressive, but still inappropriate here as calling it with length 0 is an edge case.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So,
UNEXPECTED is preferable for error (exceptions?) branch: call strlen() with null/int/resource
EXPECTED is preferable for most popular branch: call strlen() with non empty string
for the rest just don't write any hints?

@TimWolla
Copy link
Member

Also, please use hyperfine (or perf) for benchmarking. microtime() within the PHP binary is not appropriate. See: https://tideways.com/profiler/blog/how-we-use-hyperfine-to-measure-php-engine-performance

@nielsdos
Copy link
Member

Is the performance degradation caused by the deprecations?

Emitting deprecations, warnings, ... causes a performance degradation indeed.

@xepozz
Copy link
Contributor Author

xepozz commented Jul 24, 2025

Also, please use hyperfine (or perf) for benchmarking. microtime() within the PHP binary is not appropriate. See: https://tideways.com/profiler/blog/how-we-use-hyperfine-to-measure-php-engine-performance

>  hyperfine --warmup 2 './sapi/cli/php test.php' 'php test.php'
Benchmark 1: ./sapi/cli/php test.php
  Time (mean ± σ):      59.7 ms ±   0.5 ms    [User: 56.3 ms, System: 2.4 ms]
  Range (min … max):    58.7 ms …  61.5 ms    47 runs
 
Benchmark 2: php test.php
  Time (mean ± σ):     180.6 ms ±   1.3 ms    [User: 166.2 ms, System: 10.7 ms]
  Range (min … max):   179.2 ms … 184.3 ms    16 runs
 
Summary
  ./sapi/cli/php test.php ran
    3.03 ± 0.03 times faster than php test.php

@xepozz
Copy link
Contributor Author

xepozz commented Jul 24, 2025

Is the performance degradation caused by the deprecations?

Emitting deprecations, warnings, ... causes a performance degradation indeed.

Sure. I thought performance won't be affected much if I disable deprecation warning.

@TimWolla
Copy link
Member

> hyperfine --warmup 2 './sapi/cli/php test.php' 'php test.php'

Is this an apples-to-apples comparison? The bare php indicates that this is your system PHP, possibly a PHP 8.4? You must compare the base commit of the PR against the latest commit of the PR, otherwise the results are just going to be useless. The script should also only test the specific test case you are interested in and not include any unrelated code (e.g. the microtime() calls).

You should test:

  1. htmlspecialchars("");
  2. htmlspecialchars(str_repeat('x', 16));
  3. htmlspecialchars(str_repeat('x', 4096));

And for each of the cases you should test the base commit against the PR.

For benchmarks it also helps if you include the CPU you test this on, since relative performance often is CPU-dependent (e.g. due to cache sizes).

@xepozz
Copy link
Contributor Author

xepozz commented Jul 25, 2025

branch

❯ hyperfine './sapi/cli/php html.php' -r 10000
Benchmark 1: ./sapi/cli/php html.php
  Time (mean ± σ):       5.4 ms ±   0.8 ms    [User: 3.8 ms, System: 1.6 ms]
  Range (min … max):     4.4 ms …  21.8 ms    10000 runs
 
  Warning: Command took less than 5 ms to complete. Note that the results might be inaccurate because hyperfine can not calibrate the shell startup time much more precise than this limit. You can try to use the `-N`/`--shell=none` option to disable the shell completely.
  Warning: The first benchmarking run for this command was significantly slower than the rest (16.4 ms). This could be caused by (filesystem) caches that were not filled until after the first run. You should consider using the '--warmup' option to fill those caches before the actual benchmark. Alternatively, use the '--prepare' option to clear the caches before each timing run.

master

❯ hyperfine './sapi/cli/php html.php' -r 10000
Benchmark 1: ./sapi/cli/php html.php
  Time (mean ± σ):       6.6 ms ±   1.0 ms    [User: 3.9 ms, System: 1.7 ms]
  Range (min … max):     5.5 ms …  58.9 ms    10000 runs
 
  Warning: Statistical outliers were detected. Consider re-running this benchmark on a quiet system without any interferences from other programs. It might help to use the '--warmup' or '--prepare' options.

I don't know why it should work, because the time is different every run. I've tried without manual --runs flag, with 1 000 and 10 000 run. Everytime I get different time both on master and the branch.
But anyway, you may use the results if it help you to make a decision.

master: first 3 runs
branch: last 3 runs
image

@nielsdos
Copy link
Member

nielsdos commented Jul 25, 2025

@xepozz You're likely not doing enough iterations in your benchmark PHP code, which means that your run is mostly measuring startup time.

@xepozz
Copy link
Contributor Author

xepozz commented Jul 25, 2025

@nielsdos ok

<?php

error_reporting(E_ALL ^ E_DEPRECATED);

for($i=0;$i<=10_000;$i++) {
	htmlspecialchars(null);
	htmlspecialchars("");
	htmlspecialchars(str_repeat('x', 16));
	htmlspecialchars(str_repeat('x', 4096));
}
hyperfine './sapi/cli/php html.php' -r 100 

master: first 3 runs
branch: last 3 runs
image

is it enough?

@nielsdos
Copy link
Member

You have to measure each of the 4 inputs separately, and compare them on master vs this PR. That's how you benchmark that there are improvements for the empty string and no degradations for other cases.

@xepozz
Copy link
Contributor Author

xepozz commented Jul 25, 2025

Well, there are results:

3 runs on each branch:

master
❯ hyperfine './sapi/cli/php html_null.php' './sapi/cli/php html_empty.php' './sapi/cli/php html_x16.php' './sapi/cli/php html_x4096.php' -r 100
Benchmark 1: ./sapi/cli/php html_null.php
  Time (mean ± σ):       9.5 ms ±   2.2 ms    [User: 6.7 ms, System: 1.8 ms]
  Range (min … max):     8.6 ms …  29.8 ms    100 runs
 
  Warning: The first benchmarking run for this command was significantly slower than the rest (29.8 ms). This could be caused by (filesystem) caches that were not filled until after the first run. You should consider using the '--warmup' option to fill those caches before the actual benchmark. Alternatively, use the '--prepare' option to clear the caches before each timing run.
 
Benchmark 2: ./sapi/cli/php html_empty.php
  Time (mean ± σ):       6.9 ms ±   0.5 ms    [User: 4.3 ms, System: 1.8 ms]
  Range (min … max):     6.1 ms …   8.8 ms    100 runs
 
Benchmark 3: ./sapi/cli/php html_x16.php
  Time (mean ± σ):       8.3 ms ±   1.6 ms    [User: 5.5 ms, System: 1.8 ms]
  Range (min … max):     7.1 ms …  20.1 ms    100 runs
 
  Warning: Statistical outliers were detected. Consider re-running this benchmark on a quiet system without any interferences from other programs. It might help to use the '--warmup' or '--prepare' options.
 
Benchmark 4: ./sapi/cli/php html_x4096.php
  Time (mean ± σ):     231.9 ms ±   1.0 ms    [User: 228.1 ms, System: 2.4 ms]
  Range (min … max):   230.9 ms … 237.6 ms    100 runs
 
  Warning: Statistical outliers were detected. Consider re-running this benchmark on a quiet system without any interferences from other programs. It might help to use the '--warmup' or '--prepare' options.
 
Summary
  ./sapi/cli/php html_empty.php ran
    1.20 ± 0.25 times faster than ./sapi/cli/php html_x16.php
    1.38 ± 0.33 times faster than ./sapi/cli/php html_null.php
   33.78 ± 2.68 times faster than ./sapi/cli/php html_x4096.php
❯ hyperfine './sapi/cli/php html_null.php' './sapi/cli/php html_empty.php' './sapi/cli/php html_x16.php' './sapi/cli/php html_x4096.php' -r 100
Benchmark 1: ./sapi/cli/php html_null.php
  Time (mean ± σ):       9.4 ms ±   1.3 ms    [User: 6.7 ms, System: 1.8 ms]
  Range (min … max):     8.3 ms …  19.4 ms    100 runs
 
  Warning: Statistical outliers were detected. Consider re-running this benchmark on a quiet system without any interferences from other programs. It might help to use the '--warmup' or '--prepare' options.
 
Benchmark 2: ./sapi/cli/php html_empty.php
  Time (mean ± σ):       6.7 ms ±   0.6 ms    [User: 4.3 ms, System: 1.7 ms]
  Range (min … max):     6.0 ms …   9.8 ms    100 runs
 
Benchmark 3: ./sapi/cli/php html_x16.php
  Time (mean ± σ):      14.9 ms ±  16.9 ms    [User: 5.7 ms, System: 2.1 ms]
  Range (min … max):     7.0 ms … 100.5 ms    100 runs
 
  Warning: Statistical outliers were detected. Consider re-running this benchmark on a quiet system without any interferences from other programs. It might help to use the '--warmup' or '--prepare' options.
 
Benchmark 4: ./sapi/cli/php html_x4096.php
  Time (mean ± σ):     233.5 ms ±   4.7 ms    [User: 228.6 ms, System: 2.5 ms]
  Range (min … max):   230.7 ms … 264.2 ms    100 runs
 
  Warning: The first benchmarking run for this command was significantly slower than the rest (264.2 ms). This could be caused by (filesystem) caches that were not filled until after the first run. You should consider using the '--warmup' option to fill those caches before the actual benchmark. Alternatively, use the '--prepare' option to clear the caches before each timing run.
 
Summary
  ./sapi/cli/php html_empty.php ran
    1.40 ± 0.23 times faster than ./sapi/cli/php html_null.php
    2.21 ± 2.52 times faster than ./sapi/cli/php html_x16.php
   34.76 ± 3.26 times faster than ./sapi/cli/php html_x4096.php
❯ hyperfine './sapi/cli/php html_null.php' './sapi/cli/php html_empty.php' './sapi/cli/php html_x16.php' './sapi/cli/php html_x4096.php' -r 100
Benchmark 1: ./sapi/cli/php html_null.php
  Time (mean ± σ):       9.2 ms ±   0.8 ms    [User: 6.7 ms, System: 1.8 ms]
  Range (min … max):     8.5 ms …  16.1 ms    100 runs
 
  Warning: Statistical outliers were detected. Consider re-running this benchmark on a quiet system without any interferences from other programs. It might help to use the '--warmup' or '--prepare' options.
 
Benchmark 2: ./sapi/cli/php html_empty.php
  Time (mean ± σ):       6.6 ms ±   0.4 ms    [User: 4.3 ms, System: 1.7 ms]
  Range (min … max):     5.8 ms …   8.0 ms    100 runs
 
Benchmark 3: ./sapi/cli/php html_x16.php
  Time (mean ± σ):       7.8 ms ±   0.4 ms    [User: 5.4 ms, System: 1.7 ms]
  Range (min … max):     7.1 ms …   8.9 ms    100 runs
 
Benchmark 4: ./sapi/cli/php html_x4096.php
  Time (mean ± σ):     231.6 ms ±   0.5 ms    [User: 228.1 ms, System: 2.3 ms]
  Range (min … max):   230.3 ms … 233.2 ms    100 runs
 
Summary
  ./sapi/cli/php html_empty.php ran
    1.18 ± 0.09 times faster than ./sapi/cli/php html_x16.php
    1.39 ± 0.15 times faster than ./sapi/cli/php html_null.php
   34.92 ± 2.14 times faster than ./sapi/cli/php html_x4096.php
branch
❯ hyperfine './sapi/cli/php html_null.php' './sapi/cli/php html_empty.php' './sapi/cli/php html_x16.php' './sapi/cli/php html_x4096.php' -r 100
Benchmark 1: ./sapi/cli/php html_null.php
  Time (mean ± σ):       9.1 ms ±   0.5 ms    [User: 6.5 ms, System: 1.8 ms]
  Range (min … max):     8.2 ms …  12.1 ms    100 runs
 
Benchmark 2: ./sapi/cli/php html_empty.php
  Time (mean ± σ):       6.6 ms ±   0.4 ms    [User: 4.1 ms, System: 1.7 ms]
  Range (min … max):     5.9 ms …   7.8 ms    100 runs
 
Benchmark 3: ./sapi/cli/php html_x16.php
  Time (mean ± σ):       9.1 ms ±   1.1 ms    [User: 5.5 ms, System: 2.0 ms]
  Range (min … max):     7.8 ms …  16.2 ms    100 runs
 
  Warning: Statistical outliers were detected. Consider re-running this benchmark on a quiet system without any interferences from other programs. It might help to use the '--warmup' or '--prepare' options.
 
Benchmark 4: ./sapi/cli/php html_x4096.php
  Time (mean ± σ):     232.0 ms ±   0.9 ms    [User: 228.1 ms, System: 2.4 ms]
  Range (min … max):   231.0 ms … 237.6 ms    100 runs
 
  Warning: Statistical outliers were detected. Consider re-running this benchmark on a quiet system without any interferences from other programs. It might help to use the '--warmup' or '--prepare' options.
 
Summary
  ./sapi/cli/php html_empty.php ran
    1.38 ± 0.18 times faster than ./sapi/cli/php html_x16.php
    1.39 ± 0.11 times faster than ./sapi/cli/php html_null.php
   35.22 ± 2.12 times faster than ./sapi/cli/php html_x4096.php
❯ hyperfine './sapi/cli/php html_null.php' './sapi/cli/php html_empty.php' './sapi/cli/php html_x16.php' './sapi/cli/php html_x4096.php' -r 100
Benchmark 1: ./sapi/cli/php html_null.php
  Time (mean ± σ):       9.4 ms ±   0.7 ms    [User: 6.5 ms, System: 1.8 ms]
  Range (min … max):     8.6 ms …  13.6 ms    100 runs
 
  Warning: The first benchmarking run for this command was significantly slower than the rest (13.6 ms). This could be caused by (filesystem) caches that were not filled until after the first run. You should consider using the '--warmup' option to fill those caches before the actual benchmark. Alternatively, use the '--prepare' option to clear the caches before each timing run.
 
Benchmark 2: ./sapi/cli/php html_empty.php
  Time (mean ± σ):       6.9 ms ±   0.5 ms    [User: 4.1 ms, System: 1.8 ms]
  Range (min … max):     6.2 ms …   8.9 ms    100 runs
 
Benchmark 3: ./sapi/cli/php html_x16.php
  Time (mean ± σ):       8.2 ms ±   0.7 ms    [User: 5.4 ms, System: 1.8 ms]
  Range (min … max):     7.4 ms …  13.5 ms    100 runs
 
  Warning: The first benchmarking run for this command was significantly slower than the rest (13.5 ms). This could be caused by (filesystem) caches that were not filled until after the first run. You should consider using the '--warmup' option to fill those caches before the actual benchmark. Alternatively, use the '--prepare' option to clear the caches before each timing run.
 
Benchmark 4: ./sapi/cli/php html_x4096.php
  Time (mean ± σ):     232.4 ms ±   2.5 ms    [User: 228.1 ms, System: 2.5 ms]
  Range (min … max):   230.9 ms … 256.4 ms    100 runs
 
  Warning: Statistical outliers were detected. Consider re-running this benchmark on a quiet system without any interferences from other programs. It might help to use the '--warmup' or '--prepare' options.
 
Summary
  ./sapi/cli/php html_empty.php ran
    1.19 ± 0.14 times faster than ./sapi/cli/php html_x16.php
    1.36 ± 0.14 times faster than ./sapi/cli/php html_null.php
   33.70 ± 2.53 times faster than ./sapi/cli/php html_x4096.php
❯ hyperfine './sapi/cli/php html_null.php' './sapi/cli/php html_empty.php' './sapi/cli/php html_x16.php' './sapi/cli/php html_x4096.php' -r 100
Benchmark 1: ./sapi/cli/php html_null.php
  Time (mean ± σ):       9.1 ms ±   0.4 ms    [User: 6.5 ms, System: 1.8 ms]
  Range (min … max):     8.1 ms …  10.2 ms    100 runs
 
Benchmark 2: ./sapi/cli/php html_empty.php
  Time (mean ± σ):       6.6 ms ±   0.5 ms    [User: 4.1 ms, System: 1.8 ms]
  Range (min … max):     5.8 ms …   8.5 ms    100 runs
 
Benchmark 3: ./sapi/cli/php html_x16.php
  Time (mean ± σ):       8.0 ms ±   0.6 ms    [User: 5.4 ms, System: 1.8 ms]
  Range (min … max):     7.1 ms …  11.1 ms    100 runs
 
Benchmark 4: ./sapi/cli/php html_x4096.php
  Time (mean ± σ):     231.9 ms ±   1.0 ms    [User: 228.1 ms, System: 2.4 ms]
  Range (min … max):   230.8 ms … 237.9 ms    100 runs
 
  Warning: Statistical outliers were detected. Consider re-running this benchmark on a quiet system without any interferences from other programs. It might help to use the '--warmup' or '--prepare' options.
 
Summary
  ./sapi/cli/php html_empty.php ran
    1.21 ± 0.13 times faster than ./sapi/cli/php html_x16.php
    1.37 ± 0.12 times faster than ./sapi/cli/php html_null.php
   35.12 ± 2.64 times faster than ./sapi/cli/php html_x4096.php

Summary:

master

  1. Summary
    ./sapi/cli/php html_empty.php ran
    1.20 ± 0.25 times faster than ./sapi/cli/php html_x16.php
    1.38 ± 0.33 times faster than ./sapi/cli/php html_null.php
    33.78 ± 2.68 times faster than ./sapi/cli/php html_x4096.php
  2. Summary
    ./sapi/cli/php html_empty.php ran
    1.40 ± 0.23 times faster than ./sapi/cli/php html_null.php
    2.21 ± 2.52 times faster than ./sapi/cli/php html_x16.php
    34.76 ± 3.26 times faster than ./sapi/cli/php html_x4096.php
  3. Summary
    ./sapi/cli/php html_empty.php ran
    1.18 ± 0.09 times faster than ./sapi/cli/php html_x16.php
    1.39 ± 0.15 times faster than ./sapi/cli/php html_null.php
    34.92 ± 2.14 times faster than ./sapi/cli/php html_x4096.php

branch:

  1. Summary
    ./sapi/cli/php html_empty.php ran
    1.38 ± 0.18 times faster than ./sapi/cli/php html_x16.php
    1.39 ± 0.11 times faster than ./sapi/cli/php html_null.php
    35.22 ± 2.12 times faster than ./sapi/cli/php html_x4096.php
  2. Summary
    ./sapi/cli/php html_empty.php ran
    1.19 ± 0.14 times faster than ./sapi/cli/php html_x16.php
    1.36 ± 0.14 times faster than ./sapi/cli/php html_null.php
    33.70 ± 2.53 times faster than ./sapi/cli/php html_x4096.php
  3. Summary
    ./sapi/cli/php html_empty.php ran
    1.21 ± 0.13 times faster than ./sapi/cli/php html_x16.php
    1.37 ± 0.12 times faster than ./sapi/cli/php html_null.php
    35.12 ± 2.64 times faster than ./sapi/cli/php html_x4096.php
Source

html_null.php

<?php

error_reporting(E_ALL ^ E_DEPRECATED);

$x=null;
for($i=0;$i<=10_000;$i++) {
	htmlspecialchars($x);
}

html_empty.php

<?php

error_reporting(E_ALL ^ E_DEPRECATED);

$x="";
for($i=0;$i<=10_000;$i++) {
	htmlspecialchars($x);
}

html_x16.php

<?php

error_reporting(E_ALL ^ E_DEPRECATED);

$x=str_repeat('x', 16);
for($i=0;$i<=10_000;$i++) {
	htmlspecialchars($x);
}

html_x4096.php

<?php

error_reporting(E_ALL ^ E_DEPRECATED);

$x=str_repeat('x', 4096);
for($i=0;$i<=10_000;$i++) {
	htmlspecialchars($x);
}

command:

hyperfine './sapi/cli/php html_null.php' './sapi/cli/php html_empty.php' './sapi/cli/php html_x16.php' './sapi/cli/php html_x4096.php' -r 100

@bukka
Copy link
Member

bukka commented Jul 25, 2025

btw this is sort of duplicate of #18126 that already contain this change (well if my suggestion is applied there).

I think it would be probably worth it to pick that one up. @ArtUkrainskiy might be too busy but it would be good to get it can bring some other improvements. Although if your strategy is to do it piece by piece, that's of course even better as we can see impact of each improvement.

@bukka
Copy link
Member

bukka commented Jul 25, 2025

That PR needs some refactoring though (see my comments there) so I'm fine to merge this on its own unless someone objects.

@xepozz
Copy link
Contributor Author

xepozz commented Jul 25, 2025

btw this is sort of duplicate of #18126 that already contain this change (well if my suggestion is applied there).

I think it would be probably worth it to pick that one up. @ArtUkrainskiy might be too busy but it would be good to get it can bring some other improvements. Although if your strategy is to do it piece by piece, that's of course even better as we can see impact of each improvement.

There were some internal discussions about slower call of htmlspecialchars() with null and empty string.
I looked at the implementation and found a minor thing with unnecessary memory allocation.

So I don't plan any other improvements unless I find others.

@bukka
Copy link
Member

bukka commented Jul 25, 2025

The benchmark numbers do not make much sense to me though. Why master 2. Summary is a bigger difference than branch 2. Summary?

@xepozz
Copy link
Contributor Author

xepozz commented Jul 25, 2025

The benchmark numbers do not make much sense to me though. Why master 2. Summary is a bigger difference than branch 2. Summary?

You may see similar picture in my previous replies. There are just BIG deviation between runs.
I test it on MacBook M1 PRO 16GB

@nielsdos
Copy link
Member

nielsdos commented Jul 25, 2025

I benchmarked on my i7-4790, just tested the empty string and "X"*16, 2000000 iterations in PHP.
Didn't test passing null as that's coerced anyway to "", so that doesn't matter for the actual performance of htmlentities.

Benchmark Mean + stddev (master) Mean + stddev (PR) Conclusion
"" 70.4 ms ± 1.4 ms 27.0 ms ± 0.5 ms new is 2.61 ± 0.07 times faster
"X"*16 245.6 ms ± 2.1 ms 246.5 ms ± 4.4 ms old is 1.00 ± 0.02 times faster

So PR seems worth it, performance difference of "X"*16 seems to be noise. Testing on "X"*4096 is not particularly useful as the cost of the check is upfront before the actual logic loop.

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

Successfully merging this pull request may close these issues.

5 participants