我正在为几个函数添加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看起来更慢,更潇洒.
注意:改组测试命令没有任何改变,所以,没有热身/内存分配会严重影响结果.