Три часа ночи. Вношу последние правки в код и собираюсь завершать работу, но вдруг, посмотрев на огромный класс, который я правил и в котором отсутствовали типы данных (а IDE ругалась на это), я их быстренько проставил. А там, где тип возвращаемой переменной был неизвестен, стали работать автодополнения. Появился вопрос, а для чего? Разве только для удобства?
Для проверки того как типизация данных влияет, без особого фанатизма был сделан бенчмарк тест, под спойлером фрагмент кода.
Функции бенчмарка
// Тест 1: сложение
function addWithoutTypes($a, $b)
{
return $a + $b;
}
function addWithTypes(int $a, int $b): int
{
return $a + $b;
}
// Тест 2: работа с массивами
function processArrayWithoutTypes($arr)
{
$sum = 0;
foreach ($arr as $val) {
$sum += $val * 2;
}
return $sum;
}
function processArrayWithTypes(array $arr): int
{
$sum = 0;
foreach ($arr as $val) {
$sum += $val * 2;
}
return $sum;
}
// Тест 3: работа с объектами
class Calculator
{
public function multiplyWithoutTypes($a, $b)
{
return $a * $b;
}
public function multiplyWithTypes(float $a, float $b): float
{
return $a * $b;
}
}
// Тест 4: чуть сложные вычисления
function complexWithoutTypes($x, $y, $z)
{
$a = $x * $y;
$b = $y + $z;
$c = $a / ($b ?: 1);
return sqrt($c * $c + $x);
}
function complexWithTypes(float $x, float $y, float $z): float
{
$a = $x * $y;
$b = $y + $z;
$c = $a / ($b ?: 1);
return sqrt($c * $c + $x);
}Тестовый стенд:
MacBook Pro 2018 x86_64 Intel i7-8559U (8) @ 2.70GHz;
PHP 8.4.6, к сожалению не помню метод установки, кажется компилировал из сорцев;
Каждый тест выполняется 10 миллионов итераций для получения статистически значимых результатов, предварительно прогревшись;
5 конфигураций: без оптимизаций, только OPcache, OPcache + JIT (режимы 1205, 1235, 1255);
Тесты прогонял несколько раз для достоверности результата.
Настройки opcache являются стандартными, потому прикладывать я их сюда не буду.
Результаты меня удивили, первым пошёл тест без opcache и без jit
Тест без opcache и jit
=== PHP Configuration ===
OPcache Enabled: No
JIT Enabled: No
=== Running Benchmarks (10,000,000 iterations) ===
Test 1: Simple Addition
---------------------------------------------------------------------------
addWithoutTypes(5, 10) | 0.9140s | 91.40 ns/op
addWithTypes(5, 10) | 0.9100s | 91.00 ns/op
→ Result: WITH types is 0.44% faster
Test 2: Array Processing
---------------------------------------------------------------------------
processArrayWithoutTypes(array) | 14.5909s | 1459.09 ns/op
processArrayWithTypes(array) | 14.3597s | 1435.97 ns/op
→ Result: WITH types is 1.58% faster
Test 3: Class Methods
---------------------------------------------------------------------------
Calculator->multiplyWithoutTypes() | 0.7837s | 78.37 ns/op
Calculator->multiplyWithTypes() | 0.7714s | 77.14 ns/op
→ Result: WITH types is 1.56% faster
Test 4: Complex Calculations
---------------------------------------------------------------------------
complexWithoutTypes() | 1.4055s | 140.55 ns/op
complexWithTypes() | 1.4713s | 147.13 ns/op
→ Result: WITHOUT types is 4.68% faster
=== Summary ===
Total time WITHOUT types: 17.6941s
Total time WITH types: 17.5124s
Overall difference: -1.03%
Неожиданным был тот момент, что на простых операциях появился прирост в скорости просто из-за указания типов данных, чего не сказать о более сложных операциях, происходит некий overhead который только мешает работе.
Далее прогнал включив opcache
Тест с opcache, но без jit
=== PHP Configuration ===
OPcache Enabled: Yes
JIT Enabled: No
=== Running Benchmarks (10,000,000 iterations) ===
Test 1: Simple Addition
---------------------------------------------------------------------------
addWithoutTypes(5, 10) | 0.9073s | 90.73 ns/op
addWithTypes(5, 10) | 0.8647s | 86.47 ns/op
→ Result: WITH types is 4.69% faster
Test 2: Array Processing
---------------------------------------------------------------------------
processArrayWithoutTypes(array) | 14.5617s | 1456.17 ns/op
processArrayWithTypes(array) | 15.0966s | 1509.66 ns/op
→ Result: WITHOUT types is 3.67% faster
Test 3: Class Methods
---------------------------------------------------------------------------
Calculator->multiplyWithoutTypes() | 0.7711s | 77.11 ns/op
Calculator->multiplyWithTypes() | 0.8225s | 82.25 ns/op
→ Result: WITHOUT types is 6.67% faster
Test 4: Complex Calculations
---------------------------------------------------------------------------
complexWithoutTypes() | 1.5212s | 152.12 ns/op
complexWithTypes() | 1.2378s | 123.78 ns/op
→ Result: WITH types is 18.63% faster
=== Summary ===
Total time WITHOUT types: 17.7613s
Total time WITH types: 18.0216s
Overall difference: 1.47%
Видим что картина немного поменялась методы где используются структуры проиграли при типизации данных, opcache никакой магии не сделал, да и не его это область деятельности т.к. его задача кешировать байт-код, а типы вносят в него проверки которые выполняет интерпретатор.
Переходим к JIT, для начала немного о его конфиге.
opcache.jit = 1255
││││
││││
│││└─ O: Optimization level (уровень оптимизации)
││└── T: Trigger (триггер компиляции)
│└─── R: Register allocation (распределение регистров)
└──── C: CPU-specific optimization (оптимизации для CPU)
Позиция 1: C - CPU-specific optimization
Значение |
Описание |
0 |
Отключены CPU-специфичные оптимизации |
1 |
Включены оптимизации под конкретный CPU (AVX, SSE и т.д.) |
Позиция 2: R - Register allocation
Значение |
Описание |
0 |
Не использовать регистры процессора |
1 |
Линейное сканирование для выделения регистров |
2 |
Глобальное выделение регистров |
Позиция 3: T - Trigger (когда компилировать)
Значение |
Описание |
0 |
Компилировать весь скрипт сразу |
1 |
Компилировать функцию при первом выполнении |
2 |
Компилирование на основании профилирования |
3 |
Профилирование на лету и компиляц��я |
4 |
Компилирование при наличии |
5 |
Трассировка горячих путей |
Позиция 4: O - Optimization level
Значение |
Описание |
0 |
Без оптимизаций |
1 |
Минимальные оптимизации |
2 |
Выборочные оптимизации |
3 |
Стандартные оптимизации |
4 |
Агрессивные оптимизации |
5 |
Максимальные оптимизации |
На просторах сети были найдены популярные настройки, их и протестировал:
1 2 0 5
│ │ │ └─ 5: Максимальные оптимизации
│ │ └─── 0: Компилировать весь скрипт
│ └───── 2: Глобальное выделение регистров
└─────── 1: CPU-оптимизации включены
1 2 3 5
│ │ │ └─ 5: Максимальные оптимизации
│ │ └─── 3: Компилировать при первом запуске
│ └───── 2: Глобальное выделение регистров
└─────── 1: CPU-оптимизации включены
1 2 5 5
│ │ │ └─ 5: Максимальные оптимизации
│ │ └─── 5: Tracing - трассировка горячих путей
│ └───── 2: Глобальное выделение регистров
└─────── 1: CPU-оптимизации включены
А теперь к результатам теста:
Тест opcache jit 1205
=== PHP Configuration ===
OPcache Enabled: Yes
JIT Enabled: Yes (1205)
=== Running Benchmarks (10,000,000 iterations) ===
Test 1: Simple Addition
---------------------------------------------------------------------------
addWithoutTypes(5, 10) | 0.8090s | 80.90 ns/op
addWithTypes(5, 10) | 0.7151s | 71.51 ns/op
→ Result: WITH types is 11.60% faster
Test 2: Array Processing
---------------------------------------------------------------------------
processArrayWithoutTypes(array) | 7.3388s | 733.88 ns/op
processArrayWithTypes(array) | 4.5771s | 457.71 ns/op
→ Result: WITH types is 37.63% faster
Test 3: Class Methods
---------------------------------------------------------------------------
Calculator->multiplyWithoutTypes() | 0.7012s | 70.12 ns/op
Calculator->multiplyWithTypes() | 0.6463s | 64.63 ns/op
→ Result: WITH types is 7.83% faster
Test 4: Complex Calculations
---------------------------------------------------------------------------
complexWithoutTypes() | 1.0326s | 103.26 ns/op
complexWithTypes() | 0.9278s | 92.78 ns/op
→ Result: WITH types is 10.15% faster
=== Summary ===
Total time WITHOUT types: 9.8816s
Total time WITH types: 6.8664s
Overall difference: -30.51%Тест opcache jit 1235
=== PHP Configuration ===
OPcache Enabled: Yes
JIT Enabled: Yes (1235)
=== Running Benchmarks (10,000,000 iterations) ===
Test 1: Simple Addition
---------------------------------------------------------------------------
addWithoutTypes(5, 10) | 0.7993s | 79.93 ns/op
addWithTypes(5, 10) | 0.7152s | 71.52 ns/op
→ Result: WITH types is 10.53% faster
Test 2: Array Processing
---------------------------------------------------------------------------
processArrayWithoutTypes(array) | 7.8423s | 784.23 ns/op
processArrayWithTypes(array) | 4.8758s | 487.58 ns/op
→ Result: WITH types is 37.83% faster
Test 3: Class Methods
---------------------------------------------------------------------------
Calculator->multiplyWithoutTypes() | 0.6806s | 68.06 ns/op
Calculator->multiplyWithTypes() | 0.6428s | 64.28 ns/op
→ Result: WITH types is 5.56% faster
Test 4: Complex Calculations
---------------------------------------------------------------------------
complexWithoutTypes() | 1.0316s | 103.16 ns/op
complexWithTypes() | 0.9201s | 92.01 ns/op
→ Result: WITH types is 10.81% faster
=== Summary ===
Total time WITHOUT types: 10.3538s
Total time WITH types: 7.1539s
Overall difference: -30.91%
Тест opcache jit 1255
=== PHP Configuration ===
OPcache Enabled: Yes
JIT Enabled: Yes (1255)
=== Running Benchmarks (10,000,000 iterations) ===
Test 1: Simple Addition
---------------------------------------------------------------------------
addWithoutTypes(5, 10) | 0.7502s | 75.02 ns/op
addWithTypes(5, 10) | 0.6914s | 69.14 ns/op
→ Result: WITH types is 7.84% faster
Test 2: Array Processing
---------------------------------------------------------------------------
processArrayWithoutTypes(array) | 3.7918s | 379.18 ns/op
processArrayWithTypes(array) | 3.5890s | 358.90 ns/op
→ Result: WITH types is 5.35% faster
Test 3: Class Methods
---------------------------------------------------------------------------
Calculator->multiplyWithoutTypes() | 0.7375s | 73.75 ns/op
Calculator->multiplyWithTypes() | 0.6369s | 63.69 ns/op
→ Result: WITH types is 13.65% faster
Test 4: Complex Calculations
---------------------------------------------------------------------------
complexWithoutTypes() | 1.0218s | 102.18 ns/op
complexWithTypes() | 0.9487s | 94.87 ns/op
→ Result: WITH types is 7.15% faster
=== Summary ===
Total time WITHOUT types: 6.3013s
Total time WITH types: 5.8659s
Overall difference: -6.91%Результат очень порадовал, типизация колоссально ускоряет время выполнения кода. Дополнительно JIT ускорил работу функций которые не были типизированны за счет предугадывая типов и спекулятивных оптимизаций, это когда JIT генерирует код для определённых типов данных, но с проверкой типизации.
Для наглядности привожу графики, так как в текстовом формате воспринимать такое кол-во чисел занятие не из приятных

И так, результат меня сильно порадовал, типизация данных это не просто подсказка для IDE какие буковки дописать, а невероятно полезный инструмент который не только ускоряет работу кода, но и так же процесс самой разработки. Обращать внимание на типы данных я стал после того как погрузился в мир Rust и понял на сколько это важно.
Интересно было узнать, что типизация данных влияет на производительность при отключённом JIT, иногда даже в лучшую сторону, значит внутри интерпретатора довольно много завязано на типах данных.
Более комплексные тесты на фреймворках и в качестве веб приложений, возможно, когда-то и проведу, но на данном этапе и так вывод очевиден, типизация данных - обязательна.
Комментарии (2)

Lainhard
14.11.2025 03:38Появился вопрос, а для чего? Разве только для удобства?
Да. В этом вся суть. В удобстве, а если точнее: в "неудобстве выстрелить себе в лицо".
d3d11
В PHP много чего нужно еще, и типизация в этом числе.