Skip to content

Performance of preg_match varies depending on how subject string is initialized #24380

Closed as not planned

Description

@rr-it

Description

The following code:

<?php

/*
 * Run pinned to specific CPU core:
 * taskset -c 3 php perf-regex-strings.php
 */

$pattern = '/[&]/u'; // This will not produce any match in test subject.

$textA = 'weengahhoghecho1eeSheeG5el9cie5b';
$textB = sprintf('%s', 'weengahhoghecho1eeSheeG5el9cie5b');
$textC = 'weengahhoghecho1eeSheeG5el9cie5' . 'b';

$textD_1 = 'weengahhoghecho1eeSheeG5el9cie5';
$textD = $textD_1 . 'b';

$tests = ['A', 'A', 'B', 'C', 'D', 'A', 'B', 'C', 'D'];

foreach ($tests as $test) {
    $subject = '';
    switch ($test) {
        case 'A':
            $subject = $textA;
            break;
        case 'B':
            $subject = $textB;
            break;
        case 'C':
            $subject = $textC;
            break;
        case 'D':
            $subject = $textD;
            break;
    }

    $start = microtime(true);

    for ($i = 0; $i < 1000000; $i++) {
        preg_match($pattern, $subject);
    }

    $diff = sprintf('%.6f sec.', microtime(true) - $start);
    echo "Execution time $test: $diff\n";

    gc_collect_cycles();
}

See:
https://3v4l.org/uFPrD#v8.5.11

Resulted in this output:

Execution time A: […]
Execution time A: 0.052051 sec.
Execution time B: 0.054563 sec.
Execution time C: 0.052694 sec.
Execution time D: 0.030555 sec.
Execution time A: 0.052350 sec.
Execution time B: 0.052309 sec.
Execution time C: 0.052223 sec.
Execution time D: 0.030175 sec.

But I expected this output instead:

All execution times should nearly be identical.

$textD is somehow fastest for PHP 8.4 and 8.5

$textB and $textD are fastest for PHP 8.2 and PHP 8.3

PHP Version

PHP 8.2, 8.3, 8.4, 8.5 on Debian.

Operating System

No response

Activity

  1. lazerg commented on Oct 17, 2026

    @lazerg
    Contributor

    This is expected. With /u, preg_match() checks that the subject is valid UTF-8, then marks the string so later calls skip the check, but it can't mark interned strings (php_pcre.c). Only D is built at runtime; A and C are compile-time literals, and so is B since 8.4 (#14546).

  2. ndossche commented on Oct 17, 2026

    @ndossche
    Member

    Indeed, depending on how something is compiled or how something is initialized, you can get different performance characteristics.
    Passing fixed compile-time (and thus interned) strings to preg_match to check its performance isn't realistic anyway, microbenchmarks can be deceiving.
    Some common use cases have fast paths, so you can always find cases where functions behave differently because of what's commonly found in real-world code.
    Closing as not a bug.

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions