Commit 9977966
committed
minor symfony#61239 [OptionsResolver] Optimize splitOutsideParenthesis() - 5.9x faster (bendavies)
This PR was squashed before being merged into the 7.4 branch.
Discussion
----------
[OptionsResolver] Optimize splitOutsideParenthesis() - 5.9x faster
| Q | A
| ------------- | ---
| Branch? | 7.4
| Bug fix? | no
| New feature? | no <!-- if yes, also update src/**/CHANGELOG.md -->
| Deprecations? | no <!-- if yes, also update UPGRADE-*.md and src/**/CHANGELOG.md -->
| Issues | Fix symfony#59354 <!-- prefix each issue number with "Fix #"; no need to create an issue if none exists, explain below -->
| License | MIT
This PR optimises the `splitOutsideParenthesis` method in `OptionsResolver.php`, achieving a 2.91x performance improvement.
I discovered this method as a performance hotspot while benchmarking a large Symfony form with many fields. Profiling revealed that `splitOutsideParenthesis` was consuming a significant portion of the form processing time.
The `splitOutsideParenthesis` method (introduced in [PR symfony#59354](symfony#59354)) is called frequently during options resolution and has several performance bottlenecks:
1. Character-by-character string concatenation creates new string objects on each iteration (particularly inefficient in PHP due to copy-on-write behavior)
2. All input strings are processed the same way, regardless of complexity - no fast path for simple types
3. Multiple conditional checks per character
## Test Methodology
Here's how all performance measurements were conducted:
- **Benchmark tool**: hyperfine (10 runs with 1 warmup run)
- **Test iterations**: 100,000 iterations per test case
- **Test data**: 16 different type patterns:
- Simple types: `string`, `int`, `bool`, `array`
- Union types: `string|int`, `string|int|bool`, `string|int|bool|array`
- Parentheses types: `string|(int|bool)`, `(string|int)|bool`
- Nested types: `string|(int|(bool|float))`, `(string|int)|(bool|float)`
- Array types: `string[]`, `int[]`
- Class types: `MyClass`, `\\Namespace\\Class`
- Complex union: `string|int|bool|array|object|resource|callable`
Each optimisation was tested in isolation to measure its individual impact, then all optimisations were combined for the final benchmark.
## Optimisations
### 1. Fast Path for Simple Types (No Pipes)
Most type declarations are simple types like `string`, `int`, `MyClass`, etc. without any union types.
**Implementation:**
```php
if (!\str_contains($type, '|')) {
return [$type];
}
```
### 2. Fast Path for Union Types (No Parentheses)
Common union types like `string|int|bool` don't need complex parsing - PHP's `explode()` is much faster.
**Implementation:**
```php
if (!\str_contains($type, '(') && !\str_contains($type, ')')) {
return \explode('|', $type);
}
```
### 3. Eliminate String Concatenation
String concatenation in loops creates memory overhead. Using `substr()` avoids creating intermediate strings.
**Implementation:**
```php
// Instead of: $currentPart .= $char;
// Use: $parts[] = \substr($type, $start, $i - $start);
```
### 4. Switch Statement Optimisation
Eliminates Multiple conditional checks per character.
**Implementation:**
```php
switch ($char) {
case '(':
++$parenthesisLevel;
break;
case ')':
--$parenthesisLevel;
break;
case '|':
// ...
}
```
## Benchmark Results
### Individual Optimisation Impact
Testing each optimisation in isolation:
```bash
hyperfine --warmup 1 --runs 10 \
--sort=command \
--reference 'php test_original.php' \
'php test_opt1_fast_path_simple.php' \
'php test_opt2_fast_path_union.php' \
'php test_opt3_no_string_concat.php' \
'php test_opt4_switch_statement.php'
```
```
Relative speed comparison
1.00 php test_original.php
1.23 ± 0.02 php test_opt1_fast_path_simple.php
1.95 ± 0.04 php test_opt2_fast_path_union.php
1.13 ± 0.03 php test_opt3_no_string_concat.php
1.35 ± 0.03 php test_opt4_switch_statement.php
```
### Combined Optimisation Impact
Combining all optimisations:
```bash
hyperfine --warmup 1 --runs 10 \
--sort=command \
--reference 'php test_original.php' \
'php test_optimised.php'
```
```
Relative speed comparison
1.00 php test_original.php
2.91 ± 0.03 php test_optimised.php
```
Commits
-------
b568fef [OptionsResolver] Optimize splitOutsideParenthesis() - 5.9x fasterFile tree
2 files changed
+162
-27
lines changed- src/Symfony/Component/OptionsResolver
- Tests
2 files changed
+162
-27
lines changedLines changed: 23 additions & 27 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1215 | 1215 | | |
1216 | 1216 | | |
1217 | 1217 | | |
1218 | | - | |
1219 | | - | |
1220 | | - | |
1221 | | - | |
1222 | | - | |
1223 | | - | |
1224 | | - | |
1225 | | - | |
1226 | | - | |
1227 | | - | |
1228 | | - | |
1229 | | - | |
1230 | | - | |
1231 | | - | |
1232 | | - | |
1233 | | - | |
1234 | | - | |
1235 | | - | |
1236 | | - | |
1237 | | - | |
1238 | | - | |
1239 | | - | |
1240 | | - | |
1241 | | - | |
1242 | | - | |
1243 | | - | |
1244 | | - | |
| 1218 | + | |
| 1219 | + | |
| 1220 | + | |
| 1221 | + | |
| 1222 | + | |
| 1223 | + | |
| 1224 | + | |
| 1225 | + | |
| 1226 | + | |
| 1227 | + | |
| 1228 | + | |
| 1229 | + | |
| 1230 | + | |
| 1231 | + | |
| 1232 | + | |
| 1233 | + | |
| 1234 | + | |
| 1235 | + | |
| 1236 | + | |
| 1237 | + | |
| 1238 | + | |
| 1239 | + | |
| 1240 | + | |
1245 | 1241 | | |
1246 | 1242 | | |
1247 | 1243 | | |
| |||
Lines changed: 139 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2044 | 2044 | | |
2045 | 2045 | | |
2046 | 2046 | | |
| 2047 | + | |
| 2048 | + | |
| 2049 | + | |
| 2050 | + | |
| 2051 | + | |
| 2052 | + | |
| 2053 | + | |
| 2054 | + | |
| 2055 | + | |
| 2056 | + | |
| 2057 | + | |
| 2058 | + | |
| 2059 | + | |
| 2060 | + | |
| 2061 | + | |
| 2062 | + | |
| 2063 | + | |
| 2064 | + | |
| 2065 | + | |
| 2066 | + | |
| 2067 | + | |
| 2068 | + | |
| 2069 | + | |
| 2070 | + | |
| 2071 | + | |
| 2072 | + | |
| 2073 | + | |
| 2074 | + | |
| 2075 | + | |
| 2076 | + | |
| 2077 | + | |
| 2078 | + | |
| 2079 | + | |
| 2080 | + | |
| 2081 | + | |
| 2082 | + | |
| 2083 | + | |
| 2084 | + | |
| 2085 | + | |
| 2086 | + | |
| 2087 | + | |
| 2088 | + | |
| 2089 | + | |
| 2090 | + | |
| 2091 | + | |
| 2092 | + | |
| 2093 | + | |
| 2094 | + | |
| 2095 | + | |
| 2096 | + | |
| 2097 | + | |
| 2098 | + | |
| 2099 | + | |
| 2100 | + | |
| 2101 | + | |
| 2102 | + | |
| 2103 | + | |
| 2104 | + | |
| 2105 | + | |
| 2106 | + | |
| 2107 | + | |
| 2108 | + | |
| 2109 | + | |
| 2110 | + | |
| 2111 | + | |
| 2112 | + | |
| 2113 | + | |
| 2114 | + | |
| 2115 | + | |
| 2116 | + | |
| 2117 | + | |
| 2118 | + | |
| 2119 | + | |
| 2120 | + | |
| 2121 | + | |
| 2122 | + | |
| 2123 | + | |
| 2124 | + | |
| 2125 | + | |
| 2126 | + | |
| 2127 | + | |
| 2128 | + | |
| 2129 | + | |
| 2130 | + | |
| 2131 | + | |
| 2132 | + | |
| 2133 | + | |
| 2134 | + | |
| 2135 | + | |
| 2136 | + | |
| 2137 | + | |
| 2138 | + | |
| 2139 | + | |
| 2140 | + | |
| 2141 | + | |
| 2142 | + | |
| 2143 | + | |
| 2144 | + | |
| 2145 | + | |
| 2146 | + | |
| 2147 | + | |
| 2148 | + | |
| 2149 | + | |
| 2150 | + | |
| 2151 | + | |
| 2152 | + | |
| 2153 | + | |
| 2154 | + | |
| 2155 | + | |
| 2156 | + | |
| 2157 | + | |
| 2158 | + | |
| 2159 | + | |
| 2160 | + | |
| 2161 | + | |
| 2162 | + | |
| 2163 | + | |
| 2164 | + | |
| 2165 | + | |
| 2166 | + | |
| 2167 | + | |
| 2168 | + | |
| 2169 | + | |
| 2170 | + | |
| 2171 | + | |
| 2172 | + | |
| 2173 | + | |
| 2174 | + | |
| 2175 | + | |
| 2176 | + | |
| 2177 | + | |
| 2178 | + | |
| 2179 | + | |
| 2180 | + | |
| 2181 | + | |
| 2182 | + | |
| 2183 | + | |
| 2184 | + | |
| 2185 | + | |
2047 | 2186 | | |
2048 | 2187 | | |
2049 | 2188 | | |
| |||
0 commit comments