php – Memoization:连接params或执行md5哈希?

我正在为几个函数添加memoization.这些函数需要2-3个字符串参数(对象名称),可选的int参数(记录ID)和布尔参数(包括已删除的记录).保证每个参数组合产生唯一的结果(因此值得缓存).

我想知道连接给定参数($param1.$param2.$param3等)并将其用作数组键,或者使用相同的连接字符串并使用md5哈希作为键是否更快.在99%的情况下,连接参数字符串的长度在20-32个字符之间(平均大约27个),而md5散列总是32个字符.
编辑:md5哈希只有16个字节,而不是32.谢谢Mjh.

我倾向于第一种选择,因为它:

>节省了执行md5哈希的成本
>它通常会节省几个字节的内存(27个平均值比32个哈希值)(Mjh指出这不是真的:md5只有16个字节),并且
>因为md5哈希只是另一个字符串,所以比较较短的字符串通常会更快

我怀疑的唯一原因是绝大多数的memoization函数似乎都使用(md5)哈希,所以我想知道我是否遗漏了一些东西.

提前致谢.

附:我忘了提到:我用#字符分隔各个参数,这在任何参数中都不会自然出现.

P.P.S.到目前为止,ankhzet的评论似乎是最好的解决方案,因为我的字符串实际上是独一无二的:crc32($paramString).内存占用少,校验和计算功能非常快.

测试crc32()性能

下面是一个测试脚本,用100万个密钥填充4个数组=>每个价值对.所有4个数组的值都是相同的.键也是相同的,除了对于前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”似乎具有初始化开销),对于“测试2”我留下3.6255409717560,对于“测试4”留下0.9160522619883.这是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微秒是值得的~115-4600字节的内存.

如果有人不同意我的测试和/或结论,我很想知道.

最佳答案 这是我使用PHP7在AMD 2×2.3 GHz机器上进行md5-crc32-sha1-native哈希的天真性能测试:

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,
    ]);

String key: 1.2421879768372 s, 111.923 MB
CRC32 key: 1.3447260856628 s, 58.517 MB
MD5 key: 2.1748039722443 s, 111.923 MB
SHA1 key: 2.2480459213257 s, 119.552 MB

看起来MD5比crc32有点冷,而crc32显然有更少的内存开销.

Here你可以找到相同的测试(但x10次迭代,因为测试过程的服务器内存限制是64MB),PHP5.5 -PHP7& hhvm版本.

编辑:添加粗略内存分配测试(演示链接也更新).看起来crc32在建议的测试集上占用的内存少了1.5-2倍.

编辑:添加了sha1测试. md5看起来更慢,更潇洒.

注意:改组测试命令没有任何改变,所以,没有热身/内存分配会严重影响结果.

点赞