Мемоизация: объединение параметров или выполнение хеша md5?

Я добавляю памятку к нескольким функциям. Эти функции принимают 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.P.S. На данный момент комментарий анхзета кажется лучшим решением, учитывая, что мои строки практически уникальны для начала: crc32($paramString), Небольшая память и очень быстрая функция вычисления контрольной суммы.


Тестирование производительности crc32 ()

Ниже приведен тестовый скрипт, который заполняет 4 массива 1 миллионом key => value пары каждый. values из всех 4 массивов идентичны. keys также идентичны, за исключением того, что для первых 2 массивов сцепленные строковые ключи сначала имеют 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 байтов для md5()х 16


ЗаключениеТак как основная нагрузка на мои функции заключается в повторяющихся вызовах базы данных, и поскольку эти функции вызываются примерно в 50-200 раз за запрос, я лично считаю, что ~ 135-540 микросекунд дополнительного вычислительного времени стоит сэкономить ~ 1150-4600 байт памяти.

Если кто-то не согласен с моими тестами и / или заключением, я хотел бы знать.

4

Решение

Вот мой наивный тест производительности для хеширования md5-crc32-sha1-native на машине AMD 2×2.3 ГГц с PHP7:

function probe($label, $times, $callback) {
$mem = memory_get_usage();
$start = microtime(true);
$array = $callback($times);
$time = microtime(true) - $start;
$mem = sprintf('%.3f', (memory_get_usage() - $mem) / 1024 / 1024);
return "$label:  $time s, $mem MB";
}

$times = 1000000;

$run1 = probe('String key', $times, function ($times) {
$a = [];
while ($times-- > 0) {
$a["pagemanagement" . "#" . "staticblocktype" . "#" . $times . "#" . 1] = "test " . $times;
}
return $a;
});

$run2 = probe('CRC32 key', $times, function ($times) {
$a = [];
while ($times-- > 0) {
$a[crc32("pagemanagement" . "#" . "staticblocktype" . "#" . $times . "#" . 1)] = "test " . $times;
}
return $a;
});

$run3 = probe('MD5 key', $times, function ($times) {
$a = [];
while ($times-- > 0) {
$a[md5("pagemanagement" . "#" . "staticblocktype" . "#" . $times . "#" . 1)] = "test " . $times;
}
return $a;
});

$run4 = probe('SHA1 key', $times, function ($times) {
$a = [];
while ($times-- > 0) {
$a[sha1("pagemanagement" . "#" . "staticblocktype" . "#" . $times . "#" . 1)] = "test " . $times;
}
return $a;
});

echo join("<br/>\n", [
$run1,
$run2,
$run3,
$run4,
]);

Ключ строки: 1,2421879768372 с, 111,923 МБ
Ключ CRC32: 1,3447260856628 с, 58,517 МБ
Ключ MD5: 2,1748039722443 с, 111,923 МБ
Ключ SHA1: 2,2480459213257 с, 119,552 МБ

Похоже, что MD5 немного медленнее, чем crc32, в то время как crc32 очевидно имеет меньшую нагрузку на память.

Вот Вы можете найти тот же тест (но в 10 раз меньше итераций, поскольку ограничения памяти сервера для процесса тестирования составляют 64 МБ) для PHP5.5 + -PHP7 & ххвм версии.


редактировать: Добавлен примерный тест распределения памяти (также обновлена ​​демонстрационная ссылка). Похоже crc32 занимает примерно в 1,5-2 раза меньше памяти на предлагаемом тестовом наборе.

редактировать: Добавлен тест sha1. Выглядит еще медленнее и тяжелее, чем md5.

Примечание. Порядок перемешивания тестов ничего не меняет, поэтому никакой разогрев / выделение памяти не влияет на результаты.

2

Другие решения

Когда вы храните это в массиве:

$cache[$paramString] = $value;  // or
$cache[crc32($paramString)] = $value;

PHP собирается создать хеш из ключа, который он хранит как длинную без знака. Он также будет хранить фактическую $ paramString вместе с другими необходимыми данными. Итак, я не вижу, что вы действительно что-то получаете от использования crc32 () или md5 (), тем более что $ paramString обычно не будет таким большим.

На этой странице много деталей: https://nikic.github.io/2011/12/12/How-big-are-PHP-arrays-really-Hint-BIG.html

1

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

Так что только по этой причине вы должны использовать хеш.

Вы можете использовать sha1 (), который, несмотря на то, что он более сложный, чем md5 (), на самом деле работает намного быстрее (по крайней мере, в последний раз, когда я проверял).

Но это пахнет преждевременной оптимизацией для меня.

-1
По вопросам рекламы [email protected]