我正在为几个函数添加memoization。这些函数需要2-3个字符串参数(对象名称),可选的int参数(记录ID)和布尔参数(包括已删除的记录)。保证每个参数组合都能产生一个独特的结果(因此值得缓存)。
我想知道连接给定参数($param1 . $param2 . $param3
等)并将其用作数组键,或者使用相同的连接字符串并使用md5哈希作为键是否更快。在99%的情况下,连接参数字符串的长度在20-32个字符之间(平均大约27个),而md5哈希值总是32个字符。
编辑 :md5哈希只有16个字节,而不是32.谢谢Mjh。
我倾向于第一个选项,因为它:
我怀疑的唯一原因是绝大多数的memoization函数似乎都使用(md5)哈希,所以我想知道我是否遗漏了一些东西。
提前致谢。
P.S。我忘了提及:我将个别参数与#
字符分开,这在任何参数中都不会自然出现。
P.P.S。到目前为止,ankhzet的评论似乎是最佳解决方案,因为我的字符串在开始时几乎是唯一的:crc32($paramString)
。内存占用少,校验和计算功能非常快。
下面是一个测试脚本,它填充4个阵列,每个阵列有100万key => value
对。所有4个阵列中的values
都是相同的。 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字节的内存。
如果有人不同意我的测试和/或结论,我很想知道。
答案 0 :(得分:2)
这是我使用PHP7在AMD 2x2.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,
]);
字符串键:1.2421879768372 s,111.923 MB
CRC32密钥:1.3447260856628 s,58.517 MB
MD5密钥:2.1748039722443 s,111.923 MB
SHA1密钥:2.2480459213257 s,119.552 MB
看起来MD5比crc32稍微宽松一点,而crc32显然有更少的内存开销。
对于PHP5.5 + -PHP7&amp; hhvm版本。 编辑:添加了粗略的内存分配测试(演示链接也已更新)。看起来crc32
在建议的测试集上的内存大约减少了1.5-2倍。
修改:添加了sha1测试。看起来更慢,更讨厌md5。
注意:改组测试命令没有任何改变,因此,没有热身/内存分配会对结果产生重大影响。
答案 1 :(得分:1)
将其存储在数组中时:
$cache[$paramString] = $value; // or
$cache[crc32($paramString)] = $value;
PHP将从密钥创建一个哈希值,它将它存储为unsigned long。它还将存储实际的$ paramString以及所需的其他数据。所以,我没有看到你真正从做crc32()或md5()获得任何东西,特别是因为$ paramString通常不会那么大。
此页面包含许多详细信息:https://nikic.github.io/2011/12/12/How-big-are-PHP-arrays-really-Hint-BIG.html
答案 2 :(得分:-1)
连接值只有在对象的toString方法不隐藏任何影响函数行为的特征时才会起作用,并且函数不会实例化依赖于任何隐藏特征的任何东西。但由于我们讨论的是面向对象,我们无法确定连接字符串的大小是否可预测。
因此仅仅因为这个原因你应该使用哈希。
您可以考虑使用sha1(),尽管它比md5()实际运行速度更快(至少在我最后一次检查时)。
但这闻起来像是对我过早优化。