Перед вами первая статья из серии "Как использовать KPHP в open source?" В рамках этой серии мы будем разбирать разные аспекты работы с KPHP, расширяя ту информацию, что вы можете найти в официальной документации. В сегодняшнем выпуске: На данный момент использовать KPHP для настоящих "боевых" задач может быть проблематично по нескольким причинам: Наша команда старается сделать так, чтобы каждый из этих аспектов со временем становился менее ярко выраженным. Мы хотим сделать возможным эффективное и удобное использование KPHP за пределами ВК: в компилятор и рантайм добавляются недостающие фичи, а статьи, вроде этой, могут помочь закрыть пробелы в документации. Сперва нам нужно установить KPHP.  Если мы разрабатываем в директории ~/kphp-uuid, то эту директорию нужно будет пробросить в контейнер при его запуске. Внутри контейнера вам будет доступна команда kphp. По ходу статьи нам понадобится утилита ktest внутри контейнера. Я предлагаю положить бинарник ktest в директорию ~/kphp-uuid, которую мы уже подключаем к контейнеру. Далее по ходу статьи я буду делать вид, что kphp у вас установлен локально, хотя, скорее всего, вы будете его использовать из Docker-контейнера. Подготовим директорию, в которой мы будем работать: Любой уважающий себя [K]PHP пакет хочет дружить с composer, поэтому нам нужен composer.json в самом корне: Как можно догадаться по описанию, наш пакет будет предоставлять функции создания UUID. Основной наш класс мы разместим в файле src/UUID.php: Библиотека выложена на packagist, поэтому её можно установить в своих PHP/KPHP программах. Теперь создадим отдельный проект, в котором установим kphp-uuid и запустим наш hello world, печатающий сгенерированный UUIDv4. В самом корне положим наш main.php: Соберём исполняемый файл: По умолчанию результат компиляции размещается в ./kphp_out. Мы собирали с режимом cli, поэтому созданный бинарник можно найти в ./kphp_out/cli. Запустим его: Вы так же можете запускать этот main файл как обычный PHP-скрипт: Вернёмся к нашему коду в kphp-uuid. Теперь, когда мы научились писать и запускать простой код на KPHP, мы можем приступить к главной теме этой статьи — тестированию. Так как код на KPHP легко запускать через php, естественной идеей является тестирование KPHP-кода через phpunit. Это действительно рабочая стратегия, поэтому начнём мы именно с неё. Тесты мы будем размещать внутри директории tests. Следуя правилам хорошего тона, тестовый класс для UUID мы разместим вtests/UUIDTest.php: Теперь мы можем запустить тесты привычным для нас образом: Мы протестировали наш KPHP-код как обычный PHP-код. Чаще всего этого достаточно, ведь в большинстве случаев результаты будут идентичными. Однако, есть несколько преимуществ запуска тестов на настоящем KPHP: ktest — это утилита командной строки для тестирования корректности и производительности KPHP программ. Проще всего её установить через composer: Команда ktest сразу же станет доступной: В наш пакет kphp-uuid нужно будет установить вспомогательный пакет kphpunit: Использовать его напрямую нам не нужно. Этот пакет требуется для работы создаваемого утилитой ktest тестирующего кода. Теперь мы готовы запустить те же самые тесты на KPHP: Как это работает: Формат вывода у ktest phpunit очень близок к обычному phpunit. Допустим, вы решили написать немного более оптимизированную версию метода v4(). В ней вы используете меньше вызовов mt_rand. Получилось что-то такое: Тесты добавлены, вроде бы работает нормально. Но насколько мы реально ускорили код? Команда ktest bench позволяет найти ответ на этот вопрос. Тесты производительности (бенчмарки) пишутся в похожем на тесты стиле: создаём особый класс, описываем там методы. В своём примере я размещу бенчмарки в директории benchmarks в корне проекта. Содержимое файла benchmarks/BenchmarkUUID.php: Обратите внимание: Запустим бенчмарки: Вы можете использовать статическое (и не только) состояние класса внутри кода бенчмарков. Например, тестовые данные можно держать внутри static поля класса. Код бенчмарка должен выполнять именно ту операцию, которую вы хотите протестировать, ровно один раз (вам не нужно самим добавлять циклы). Старайтесь минимизировать использование хелпер-методов. Если вам нужно подготовить входные данные для бенчмарков, делайте это в конструкторе, а не внутри метода-бенчмарка. Настройка своей машины для стабильной работы бенчмарков — это тема для отдельного рассказа. Но я раскрою один простой лайфхак. Для Intel процессоров вы захотите выключить Turbo Boost. На некоторых Линуксах это делается, например, вот так: Если разброс времени исполнения выше, чем 3-5%, то проблема либо в плохо настроенном окружении, либо в самом коде бенчмарка. Желательно достигать разброса в 1-2%. Если вы сравниваете результаты бенчмарков, делать это корректно только если все наборы результатов были получены на одной и той же машине (либо если они были идентично настроены и имеют одинаковое железо). Корректно: запустить бенчмарк для PHP и KPHP на своей машине, сравнить числа. Некорректно: найти в интернете результаты для PHP/KPHP и сравнить их с вашими результатами. ktest bench будет запускать тестируемый метод много раз, пытаясь подобрать правильное значение повторений N. Однако даже это не даёт нам необходимой точности. Если вы знакомы с бенчмарками в Go (go test -bench), то вы, наверное, уже догадались, о чём сейчас пойдёт речь. Нам нужно собрать ещё больше результатов, выявить статистическую значимость и оценить погрешности. После этого нам нужно правильно соотнести результаты v4 и v4fast. Чтобы получить больше сэмплов, ktest bench стоит запускать с параметром count: Есть такая замечательная утилита benchstat, которая позволяет анализировать и сравнивать результаты бенчмарков. benchstat встроен в ktest, поэтому мы можем очень легко оценить разброс результатов: Разброс в 2-3% — это неплохой результат, с этим можно работать. benchstat может помочь нам сравнить V4 и V4Fast между собой, но нам придётся немного поработать ручками для этого. Как-то так: Теперь у вас есть два файла похожей структуры: old.txt и new.txt. Мы можем их сравнить: Видим, что fast версия (new) на 29.27% быстрее. Низкий p=0.008 говорит о том, что результаты достаточно стабильны. В целом, я рекомендую использовать -count со значением выше, чем 5. Например, неплохо работает значение 10. Чем более "шумный" ваш бенчмарк, тем больше сэмплов вам может захотеться. Я считаю правилом хорошего тона прикладывать вот такую информацию со сравнением производительности в формате benchstat в каждом коммите, который внедряет какую-то оптимизацию. Эту традицию я перенял когда работал над golang/go. ktest умеет запускать бенчмарки через PHP: Если мы соберём одни результаты в файлик php.txt, а другие в kphp.txt, то через benchstat их можно будет между собой сравнить. К счастью, подкоманда bench-vs-php сделает это всё за нас: Общие рекомендации: Самый простой вариант тестов интеграции в GitHub actions — запуск в PHP-only режиме. Подразумевается, что KPHP тесты будут запускаться через отдельную команду, локально на машине разработчика (где KPHP точно должен быть установлен). Для упрощения запуска тестов, создадим Makefile в корне проекта: Работая с репозиторием локально, мы запускаем все тесты: В GitHub Actions мы будем запускать make ci-tests. Создадим файл .github/workflows/php.yml: Теперь при каждом пуше и создании Pull Request у нас будут запускаться тесты.  Не забудьте добавить build status badge в свой README: В моём случае $user/$repo будет quasilyte/kphp-uuid. Весь исходный код kphp-uuid вместе с Makefile и прочими интеграциями можно найти тут: github.com/quasilyte/kphp-uuid. Полезные ресурсы: Making developer tools & video games