Я добавляю запоминание к нескольким функциям. Эти функции принимают 2-3 строковых параметра (имена объектов), необязательный параметр int (идентификатор записи) и логический параметр (включая удаленные записи). Каждая комбинация параметров гарантированно дает уникальный результат (поэтому стоит кэшировать).
Мне интересно, будет ли быстрее объединить заданные параметры ($param1 . $param2 . $param3
и т. д.) и использовать их в качестве ключа массива или взять ту же объединенную строку и использовать хэш md5 в качестве ключа. Длина объединенной строки параметра составляет от 20 до 32 символов в 99% случаев (в среднем около 27), тогда как хэш md5 всегда составляет 32 символа.
Изменить: md5 хэш всего 16 байт, а не 32. Спасибо, Mjh.
Я склоняюсь к первому варианту, так как он:
- экономит мне стоимость выполнения хэша md5
- обычно это экономит несколько байтов памяти (27 в среднем против 32 хешированных) (Mjh указал, что это неправда: md5 занимает всего 16 байт), и
- поскольку хэш md5 — это просто еще одна строка, обычно быстрее сравнивать более короткую строку.
Единственная причина, по которой я сомневаюсь в этом, заключается в том, что подавляющее большинство функций запоминания, похоже, используют хэши (md5), поэтому мне интересно, не упустил ли я что-то.
Заранее спасибо.
P.S. Забыл упомянуть: я разделяю отдельные параметры символом #
, который естественным образом не встречается ни в одном из параметров.
P.P.S. На данный момент комментарий ankhzet кажется лучшим решением, учитывая, что мои строки изначально практически уникальны: crc32($paramString)
. Небольшой объем памяти и очень быстрая функция вычисления контрольной суммы.
Тестирование производительности crc32()
Ниже приведен тестовый скрипт, который заполняет 4 массива по 1 миллиону key => value
пар в каждом. values
всех 4 массивов идентичны. keys
также идентичны, за исключением того, что для первых двух массивов конкатенированные строковые ключи сначала обрабатываются crc32()
.
$test1Array = [];
$start1 = microtime(true);
for ($i = 0; $i < 1000000; $i++)
{
$test1Array[crc32("pagemanagement" . "#" . "staticblocktype" . "#" . $i . "#" . 1)] = "test " . $i;
}
$end1 = microtime(true);
$test2Array = [];
$start2 = microtime(true);
for ($j = 0; $j < 1000000; $j++)
{
$test2Array[crc32("pagemanagement" . "#" . "staticblocktype" . "#" . $i . "#" . 1)] = "test " . $j;
}
$end2 = microtime(true);
$test3Array = [];
$start3 = microtime(true);
for ($x = 0; $x < 1000000; $x++)
{
$test3Array["pagemanagement" . "#" . "staticblocktype" . "#" . $i . "#" . 1] = "test " . $x;
}
$end3 = microtime(true);
$test4Array = [];
$start4 = microtime(true);
for ($y = 0; $y < 1000000; $y++)
{
$test4Array["pagemanagement" . "#" . "staticblocktype" . "#" . $i . "#" . 1] = "test " . $y;
}
$end4 = microtime(true);
Результаты 3 тестов:
Тест 1: 3,9902291297913
Тест 2: 3,6312079429626
Тест 3: 0,91605305671692
Тест 4: 0,91405177116394
Тест 1: 3,9842278957367
Тест 2: 3,6172070503235
Тест 3: 0,91405200958252
Тест 4: 0,918053150177
Тест 1: 3,9842278957367
Тест 2: 3,6282079219818
Тест 3: 0,91205215454102
Тест 4: 0,91605186462402
Если я возьму среднее значение всех значений «Тест 2» и «Тест 4» (поскольку «Тест 1», похоже, имеет накладные расходы на инициализацию), у меня останется 3,6255409717560 для «Теста 2» и 0,9160522619883 для «Теста 4». Это разница в 2,7094887097677 и (2,7094887097677/1000000) = 0,0000027094887 или 2,72 микросекунды на вызов функции.
К сожалению, в данный момент я не могу легко рассчитать использование памяти, но сохранение 4-байтового значения crc32()
гарантированно займет значительно меньше памяти, чем средняя длина строки в 27 символов. Предполагая, что в лучшем случае 1 байт символов, это разница в 23 байта на кешированный результат.
Для полноты картины я также провел быстрый тест с md5()
:
Тест 1: 4.2855787277221
Тест 2: 3.8108838399251
Я действительно удивлен тем, насколько мала разница в производительности между md5()
и crc32()
. Конечно, crc32()
по-прежнему имеет то преимущество, что использует всего 4 байта вместо 16 у md5()
.
Вывод: поскольку основные накладные расходы моих функций связаны с повторными вызовами базы данных, и поскольку эти функции вызываются порядка 50-200 раз за запрос, я лично считаю, что ~135-540 микросекунд дополнительного вычислительного времени стоит сэкономить ~1150-4600 байт памяти.
Если кто-то не согласен с моими тестами и/или выводами, я хотел бы знать.
md5
занимает 16 байт, а не 32. Он занимает 32, когда мы представляем хеш в виде строки, но в двоичном формате это меньше. Использование чего-то вроде$memoized_index = md5(json_encode(func_get_args()), true);
не должно быть ни медленным, ни требовательным к памяти. По крайней мере, я бы так поступил. - person Mjh   schedule 17.03.2016crc
? Это намного быстрее, чем md5, с уникальными и короткими исходными строками коллизии имеют тот же шанс, что и md5 (но это можно легко проверить), и требует только 32/64-битного целого числа в качестве ключа... В противном случае я бы также предпочел простой конкатенация... - person ankhzet   schedule 17.03.2016crc32()
действительно кажется хорошим и быстрым решением, торгующим крошечным приростом скорости (путем вычисления контрольной суммы) для значительно меньшего использования памяти (путем замены строки из 25-30 символов на 4-байтовый целое число). - person Byson   schedule 17.03.2016json_encode()
, а вручную объединил бы свои параметры (моя функция все равно не работает с переменным числом аргументов).json_encode()
на мой вкус добавляет слишком много накладных расходов (стек вызовов функций, возвращающий гораздо больше строковых данных, чем требуется). - person Byson   schedule 17.03.20161.3896489143372, 1.1757810115814, 0.99707007408142, 0.98535180091858
. Похоже, что прямое использование хэша имеет ту же производительность, но вычисление crc32 примерно в 2-4 раза быстрее... Я попробую также с массивами целочисленных ключей через пару минут. - person ankhzet   schedule 18.03.2016md5()
. EDIT: когда я меняю приоритет процесса на высокий, я могу выжать из него ~ 3-4% дополнительной производительности. - person Byson   schedule 18.03.2016json_encode()
ранее. И да, в этом сценарии может быть полезно использоватьjson_encode()
,var_export()
или любую другую функцию в этом роде. - person Byson   schedule 24.03.2016php-fpm
. Вы уверены, что не будете повторять работу базы данных дважды? Базы данных чрезвычайно эффективны с их кешем, я был свидетелем сотен сайтов, где разработчики пытались быть умными в отношении производительности и делали свои сайты медленнее. - person Mjh   schedule 24.03.2016php-fpm
сохраняет соединения с базой данных открытыми, а не открывает и закрывает их при каждом запросе. Для последующих запросов вообще не требуется рукопожатие. Чего вы добьетесь, так это удвоения использования оперативной памяти для сомнительного прироста производительности. Извлечение записи из базы данных не настолько медленное, чтобы вам нужно было запоминать и создавать всю систему кэширования, которая сама по себе влечет за собой накладные расходы, которые НАМНОГО больше, чем накладные расходы на лексирование базы данных. - person Mjh   schedule 24.03.2016true
/false
. Сохранение этого заключения позволяет пропустить 1-2 обращения к базе данных, а также всю логику между ними и операторомreturn
. Добавление кэширования к некоторым функциям сделало их в 20-70 раз быстрее (профилировано с помощью XDebug). - person Byson   schedule 24.03.2016