Давайте тестанём не предмет скорости ещё какую-нибудь чепуху.
Вот, допустим, константы. Одно из применений констант, это обозначение «магических» чисел. Например:
class Compressor { const GZIP = 1; const BZIP = 2; const RAR = 3; const HUERAR = 4; public function compress($str, $type) { // ... } } |
То есть у нас есть некое множество значений (типов сжатия) и для обозначения каждого мы придумываем от балды какое-то число. А, чтобы с числами не запутаться, придумываем поверх него константу. И используем:
$compressed = $compressor->compress($plain, Compressor::GZIP); |
С незапамятных пор так ведётся. Ещё Страуструп молодым был.
Однако, иногда посещает мысль, что PHP, это не Си и со строками он работает намного веселее. И почему бы не написать крамольное:
$compressed = $compressor->compress($plain, 'gzip'); |
Зачем нам какие-то числа и константы, когда можно просто и ясно человеческим языком указать что мы хотим?
Это удобно и прекрасно, но закрадывается мысль — православно ли это? Не убьют ли строки нашу бедную производительность? Проверим.
Допустим, мы что-то парсим. Например, HTML или BB-коды или шаблонизатор в очередной раз изобретаем. Первым делом разбираем текст на токены и хотим видеть результат примерно в таком виде:
$tokensBB = [ [ 'type': 'text', 'value': 'I am text', ], [ 'type': 'IMG', 'value': 'SRC="смишные_котеки.jpg"', ], ]; |
Формат понятный, но, о ужас: мало того, что тип указан строкой, а не числовой константой, так ещё и ассоциативные массивы используются. Конец и скорости и памяти.
Напишем тест: https://github.com/vasa-c/utils/blob/master/php/const-string/keys.php.
Создаём список из 10 000 токенов, а потом всех их перебираем по нескольку раз и в зависимости от типа каждого токена выполняем различные действия. В качестве обработки отдельного токена — простая арифметическая операция. Всего типов токенов — 5.
Замеряем, как скорость создания, так и скорость перебора, а также прирост памяти после создания списка.
Результат:
PHP: 5.5.1 Count: 10000 Create: 0.031732082366943 Mem: 2 825 972 Process: 0.2093300819397
Итак, на моём дохлом нетбуке перебор происходит со скоростью 500 тысяч токенов в секунду (на сервере — около 5 млн.). Но нам, как всегда, мало, поэтому займёмся жёсткой оптимизацией.
Оптимизированный вариант: https://github.com/vasa-c/utils/blob/master/php/const-string/numbers.php.
Каждый токен записываем теперь: [тип числом, значение]
. И от ассоциативных ключей избавились и типы числом записываем. Даже именованные константы использовать не будем — только числа, только хардкор!
Работать с таким форматом одно мучение, но теперь то всё должно залетать:
PHP: 5.5.1 Count: 10000 Create: 0.031095027923584 Mem: 3 105 960 Process: 0.19729590415955
А не летает. Вообще не летает.
Выводы
Выводы всё те же.
1. Не надо оптимизировать то, что не надо.
2. Если всё таки надо — не стоит строить свои оптимизации на непонятно откуда взятых предпосылках, предрассудках и где-то краем уха подслушанных слухов.
Си — кристально чистый язык. И там очевидно, почему сравнение чисел оптимальнее, чем сравнение строк. И ясно, почему доступ к массиву по порядковому индексу быстрее, чем поиск по хэш-таблице.
А что наворочали создатели похапе, питона и всего подобного у них внутри, можно узнать только по исходникам. И как с этим делать микрооптимизацию тоже. Оно вам сильно надо?
> Это удобно и прекрасно
Пожалуй насчет удобства не соглашусь. Если сделана константа в классе то тогда любая православная IDE мне по CTRL+SPACE подскажет варианты типа для передачи. А если передается строка то сиди и думай чего там вообще можеть быть jpg, JPG или jpeg…
dallone, 19.08.2013, 12:26
dallone, ладно, предлагаю сойтись на мнении, что в зависимости от ситуации может быть удобен свой вариант :)
vasa_c, 19.08.2013, 12:44
Я использую константы, причем иногда вполне и строковые. Типа
const GZIP = ‘gzip’;
Т.к. ‘gzip’ это просто строка, а Compressor::GZIP это структурированная инфа понятная для авторефакторинга
так же как если в метод передавать имя класса, то лучше сделать что-то вроде \Some\App\Class::getClassname() (php < 5.5, т.к. про \Some\App\Class::class я в курсе :) ) чем передать 'Some\App\Class'
SunChaser, 19.08.2013, 13:01
SunChaser, ну это-то вообще ересь, похуже описанной :)
Мало того, что строки, так поверх них ещё и константы.
vasa_c, 21.08.2013, 15:41
Приходится вертеться, раз в PHP нет Enumeration и перегрузки операторов
SunChaser, 22.08.2013, 11:31
Используйте константы, потому что иначе в методе compress() придётся делать дополнительные преобразования, чтобы переданные ‘gZip’ или ‘GZIP’ было тоже самое, что и ‘gzip’. А также, чтобы человеку, который заглянет или будет использовать ваш код, было сразу понятно, взглянув на класс, с какими архивами он может работать. Оптимизация нужна там, где она нужна, в данном случае — оно излишне и только вредит читабельности и понятливости кода.
Denis, 13.09.2013, 10:50
Denis, ну здесь то я как раз и пытался сказать, что никакая оптимизация здесь не нужна.
И против констант ничего нет. Только в константу можно положить человекопонятную строку:
const GZIP = ‘gzip’;
а не непонятное магическое число, только ради той же «оптимизации».
vasa_c, 13.09.2013, 10:56