Ads
Comments 58
Получается, что на небольших объёмах правильным решением чаще будет добавление железа


Очень важное и правильное замечание. Многие крупные компании забывают об этом и твердят об оптимизации. Именно поэтому иногда натыкаешься на разработчиков, которые до сих пор двойные кавычки в одинарные переводят ради увеличения скорости.

На своем примере в этом убедился. Когда у вас нет зоопарка серверов проще докупить мощности, особенно в наше время облаков.
Именно поэтому иногда натыкаешься на разработчиков, которые до сих пор двойные кавычки в одинарные переводят ради увеличения скорости.


Зачастую это делается для того, чтобы потом не огрести проблем. Ну и потом уже по привычке не ставят их там, где это просто не нужно.

А если рассматривать с точки зрения производительности — никаких отличий по скорости в двойных и одинарных кавычках нет (если без интерполяции). Более того, даже если использовать конкатенацию — это так же соберётся в один php-опокод. А если это будет ещё и константа, то соберётся на этапе раннего статического связывания.

TL:DR; За этот год уже 3 вопроса на Тостере было, где используют двойные кавычки, а внутрь случайно прилетает управляющая конструкция. А вопрошающие просто не понимают «почему не работает».
Это немного другая тема, как мне кажется.

В любом случае, микрооптимизации работают только если у вас действительно крупный проект. В остальном надо либо искать узкое место (а зачастую оно есть), либо просто наращивать мощность.
Понятно что микрооптимизации — это бред сивой кобылы в 99.9% случаях, но если не мешает никому, то почему нет?

С другой стороны, если мы выяснили, что между одинарными и двойными кавычками отличий по скорости в константных выражениях нет (можно самостоятельно убедиться в этом, воспользовавшись vld), то не думали вы, что опытные разработчики могут по какой-либо иной причине не использовать двойные кавычки?

Ну вот на вскидку:
1) Интерполяция нечитаема без подсветки и статического анализа кода (хотя кто в своём уме будет работать в nano — я не знаю).
2) Интерполяция работает в PHP через одно место (только с переменными, никаких выражений).
3) Сложнее рефакторить, нежели конкатенацию.

Примеры все, конечно, высосаны из пальца, но если посмотреть на это как на комплекс проблем, против решений, где проблем никаких нет, кроме пары лишних символов (ну или функций, для любителей sprintf), то кажется, что это всё не просто так. Что думаете?
Если не мешает и есть время — я только «за».

Стоп. Я не говорил, что не нужно использовать двойные кавычки :)
Я лишь говорил, что не нужно везде заменять двойные на одинарные с целью сделать мегабыстрыйсуперхайлоад сайт. Не получится.
А применять их по делу — да они для этого и были созданы, почему бы и нет.

зачастую цель не сделать "оптимизацию", а привести к единому стилю.

Под интерполяцией понимается: " string {$var1} string {$var2}"?

Так вот, конкатенация медленее и, по моим тестам, как бы не на 30%
Под интерполяцией понимается: " string {$var1} string {$var2}"?

Не «понимается». Это общеизвестный термин: www.google.com/search?q=интерполяция+строк Так что да

Так вот, конкатенация медленее и, по моим тестам, как бы не на 30%


Ответ: Зависит от того, в какой момент собираются эти инструкции.

Заголовок спойлера
На этапе позднего динамического связывания картина такая:
Заголовок спойлера
<?php
$var = 42;

echo 'some' . $var . 'any';

/**
 * line     #* E I O op                           fetch          ext  return  operands
 * -------------------------------------------------------------------------------------
 *    2     0  E >   ASSIGN                                                   !0, 42
 *    4     1        CONCAT                                           ~2      'some', !0
 *          2        CONCAT                                           ~3      ~2, 'any'
 *          3        ECHO                                                     ~3
 *    5     4      > RETURN                                                   1
 */

<?php
$var = 42;

echo "some${var}any";

/**a
 * line     #* E I O op                           fetch          ext  return  operands
 * -------------------------------------------------------------------------------------
 *    2     0  E >   ASSIGN                                                   !0, 42
 *    4     1        ROPE_INIT                                     3  ~3      'some'
 *          2        ROPE_ADD                                      1  ~3      ~3, !0
 *          3        ROPE_END                                      2  ~2      ~3, 'any'
 *          4        ECHO                                                     ~2
 *    5     5      > RETURN                                                   1
 */



И тут просто всё зависит от скорости выполнения инструкций. Но делаем ход конём и переносим конкатенацию на этап раннего статического связывания. И вуаля!
Заголовок спойлера
<?php
class Wtf
{
    public const VAR = 42;
    public const CONCAT = 'some' . self::VAR . 'any';
}

echo Wtf::CONCAT;

/**
 * line     #* E I O op                           fetch          ext  return  operands
 * -------------------------------------------------------------------------------------
 *    2     0  E >   NOP
 *    8     1        ECHO                                                     'some42any'
 *   10     2      > RETURN                                                   1
 */



Тут даже самому рассеянному станет очевидно, что конкатенация будет на порядки быстрее, потому что интерполяция умеет только в позднее связывание и просто не умеет работать на уровне этапа компиляции в инструкции, а только во время интерпретации.

Причём это относится не только к константам, но и полям, объявленным на раннем этапе:
Заголовок спойлера
class Wtf2
{
    private $some = 'asdasd' . 42 . 'asdasd';
}



Они тоже содержат только одну инструкцию фетча переменной, а константное значение уже заранее помещается в таблицу символов интерпретатора.
вот так, много лет используешь что-то, а потом выясняется, что у него аж определение есть и весь мир его знает кроме тебя (самоирония)

Но делаем ход конём и переносим конкатенацию на этап раннего статического связывания

То, что константа будет намного быстрее (фактически мгновенно) — это очевидная вещь, которую даже не стоит упоминать!

Тем не менее, как часто в практических задачах удается перенести то не знаю что на уровень констант.

P.S. На моем, совершенно синтетическом тесте (1 млн. итераций)

"Some string contains {$x} and {$y} with {$z} string"; 1.7375 sec
'Some string contains ' . $x . ' and ' . $y . ' with ' . $z .' string' 2.2438 sec
sprintf("Some string contains %s and %s with %s", $x, $y, $z) 1.9559 sec

а не рассматривали переход на более быстрый язык? 50к рпс как бы не так много…
UFO landed and left these words here
UFO landed and left these words here
UFO landed and left these words here
yaroslavche, да, в том конкурсе нода показала себя хуже. Я кроме решения на php писал также решение на ноде и надеялся побить лучший результат среди решиний на ноде, но к сожалению мне не удалось добиться и близко похожих результатов, потому что периодически срабатывал сборщик мусора, создавалась пауза на какую-то долю секунды, но этого хватало, чтобы после сборки мусора нода была уже не в состоянии справиться с нагрузкой. Все топовые решения тогда отключали сборку мусора, а в ноде отключить можно было только перекомпилировав саму ноду. Управляющие конструкции для сборки мусора позволяли её только отложить ненадолго, но не отключить.
Конечно это были экстремальные тесты и в реальности почти никто не отключает сборку мусора в пхп и ноде. Мои "helloword тесты" показали, что нода сравнима по скорости с пхп. Сейчас для достижения таких результатов нужно использовать swoole/workerman вместо php-fpm, а в php 8 так будет работать «из коробки».
люблю go, пишу на нем много, но писать на нем сложную, поддерживаемую бизнес логику — сложно, а ООП PHP 7 тут как нельзя кстати. Лучший рецепт — правильно сочетать сервисы на разных языках, что я думаю они и так делают

А вы представьте сколько потребуется человеко-часов, чтоб переписать всю кодовую базу такого проекта. Придется нанять гору программистов Go, снять/расширить помещение, платить им. А пока они будут творить, текущих код должен кто то поддерживать.

я не говорю все сразу переписать, начать можно с самых нагруженных эндпоинтов, переписывать постепенно
вы так говорите как будто сервера бесплатные, с приложением явно что-то не так, 600 серверов для 50к рпс, да мой ноутбук может рендерить 400к рпс
конечно приложение сложнее, чем hello world, но это бекенд который ходит в базу и рендерит json, основной упор должен быть на базе, но тут почему-то напрягаются сервера приложения
«просто ходить и рендерить» — это когда проект совсем новый. Потом появляются новые фичи, требующие обратной совместимости в АПИ (+ кучки слоёв абстракции). Потом появляется хайлоад (это когда физически невозможно уже иметь нормализованную БД и приходится опять всё подпорками нашпиговывать)… Потом…

Ну, короче, много всего может быть. Можно попросить, конечно, скинуть бадушников код, чтобы разобраться, но не думаю что они это сделают :)

Ну и в конце концов есть функционал «под капотом», о котором непосвящённый просто не догадывается. Всякий ML, файрволлы от спамеров и прочее-прочее.
Вы хотите сказать, что там ML на php? тогда я ничему не удивлюсь…
Я хочу сказать, что у баду есть функционал «под капотом», о котором непосвящённый просто не догадывается. Кажется именно это я и написал, вроде бы… =)

Да и не стоит, хм, сарказмировать (а есть такое слово? о__0). Есть как бы и биндинги на tensorflow, например. Есть куча всяких библиотек под ML. Да и PHP будет побыстрее того же классического Python и большинства других интерпретаторов.
Бывает и ML на PHP, ничего такого. Но только использование обученной модели (биндинги или ре-имплементация), а не само обучение, разумеется.

Да, мы используем обученные модели из PHP.
Благодаря Pinba за их потреблением легко следить — сейчас это сотые доли процента кластера.

lu4e3ar было бы интересно почитать об этом. Вы вроде ещё не рассказывали на хабре о том как используете обученные модели из PHP.
Я вижу три варианта: php-opencv (сыро), php-tensorflow (нужен php 7.3 RC) и php-ml (медленно).
Но я думаю, вы используете что-то другое. Было бы интересно узнать что именно :)

Это как в анекдоте про секретаршу:


Устраивается девушка на работу. «Я печатаю 2000 знаков в минуту!» (в сторону) «Такая фигня получается!»
Не забывайте, что на каждом реквесте:

— авторизуется пользователь
— подтягивается его сессия
— проверяется на флуд запросами
— отправляются единицы/десятки запросов в различные сервисы для учета статистики работы различных сервисов, апи, и так далее
— выполняется собственно бизнес-логика, которая может представлять из себя походы в сотни разных сервисов, с разными проверками и обработкой краевых случаев, которые нигде, кроме как в хайлоаде себя бы не проявляли
— и т.д.

Статику можно отдавать 50к qps и с одной машины, а вот для запросов в апи вам нужно не только те 600 (вероятно, всё же чуть меньше) серверов, но и как минимум столько же серверов с MySQL, довольно много серверов для сервисов (уверен, тоже как минимум сотни, если не тысячи), и так далее. Сравнение с ноутбуком здесь выглядит немного неуместным :). Вы можете написать сервис, который работает с вашего ноутбука и обслуживает большой QPS, только делать он скорее всего будет что-то очень простое и вряд ли кому-то очень нужное :).

кстати, интересно было бы почитать ваши мысли от php в баду и php в вк =)

И тем не менее 50krps на 300 серваков это по 200rps на серв. Понимаю, что так нельзя размазывать, но цифры и впрямь какие-то дикие для сегодняшнего железа. Может вам не из php надо выжимать последние соки, а вот это зарезать «походы в сотни разных сервисов»?
лет 10 назад прикручивал к PHP zend acselerator, точно уже не помню как пишется.
Кэширует скрипты целиком в виде бинарника и при обращении к скрипту не трати время на его копиляцию.
Тогда на некторых скриптах скорость удвоилась. Но основная проблема была в БД поэтому про оптимизацию PHP потом забыл.
Как можно видеть в статье, PHP с тех пор продвинулся довольно хорошо, например добавилось кеширование константных массивов (и, по идее, не только массивов, раз уж на то пошло), и это дает колоссальную экономию ресурсов, если это правильно использовать.
Пора начинать наливать пиковый трафик на сервера простаивающих в этот момент зон. Там судя по графикам до 70-80% ресурсов можно утилизировать.
Для того чтобы увеличить мощность в полтора раза, нам нужно добавить 300 серверов.


Хмм. Насколько знаю, добавление нового сервера не дает прироста в 100%, там формула вроде: 1 / (1 — n), где n — это количество серверов. То есть, в какой-та момент времени мы дойдем до такой степени, что новой сервер не даст производительности.
Часто бывают задачи линейно масштабируемые, есть накладные расходы на блокировки и синхронизации, но они логарифм от количества серверов.
UFO landed and left these words here

Переход на какой-нибудь swoole нереален, а вот промежуточное решение типа roadrunner как раз может быть очень привлекательным. Хотелось бы только конкретных цифр, в чем выиграли в чем проиграли.

Именно. Переписать реальный проект на Swoole — это дикий оверхед. Перепрыгнуть на RoadRunner довольно просто а возможный выигрыш в скорости — на порядок вверх

RoadRunner не пробовали.
У нас есть места, на которых подобный инструмент может дать прирост и на которых не сложно поэкспериментировать, — возможно в ближайшем будущем попробуем.


В любом случае, целиком на него переписать наш код очень сложно, выигрыш не будет сопоставим с затратами. С нуля это сделать проще, но даже в таком случае нужно постоянно держать в голове, что ничего не должно оседать в глобальном скоупе, кеши должны инвалидироваться за пределами реквеста и прочее. Для конкретных задач это может быть не проблемой, но в общем случае понижается гибкость разработки и повышается вероятность допустить ошибку.

Выйграли в бутлоаде, проиграли немного по памяти. Плюс возможность кастомизировать приложение на уровне апп сервера. У нас прирост до 40 раз.
Есть независимый синтеческий тест Ларавел от китайцев: https://www.ctolib.com/topics-132962.html

RoadRunner просто заменяет веб/апп-сервер вроде связки Nginx / PHP-FPM. Если кодовая база ориентируется на PSR-7, ничего менять в ней не придется
Очень интересная статья! Автору спасибо! Возникло 2 вопроса
1. Почему php-cgi? Разве mod_php не быстрее?.. Мой опыт настройки серверов под битрикс показывает, что php-cgi — самый медленный вариант.
2. В коментариях увидел, что на проекте используются обученные модели, упоминание opencv и т.д. А зачем? Ну т.е. зачем нужны нейронки и библиотеки компьюетрного зрения в Вашем случае?
  1. php-cgi (в контексте этой статьи) — это название воркеров PHP-FPM.
  2. У нас нет нейронок и компьютерного зрения на PHP. Из PHP мы используем более "классические" варианты ML. Если как-то попытаться обобщить все задачи, то по сути они сводятся к тому, чтобы иметь какое-то предсказание и в зависимости от этого что-то разное пользователю показывать или предлагать.
Понял, спасибо! Понимаю, что вроде оффтоп, но может есть еще какая ссылка на материалы по ML «для самых маленьких чайников»? :-)
Ну и за PHP-профайлеры отдельное спасибо! Очень вовремя — как раз надо понять где и что не так на проекте написали.
Вы упомянули xhprof, но это расширение имеют последнюю версию от 2013 года. Вы пользуетесь каким форком для PHP>=7.0 или упомянули его для галочки?
Only those users with full accounts are able to leave comments. , please.