我正在进行科学研究,处理数百万个兆字节数组组合。
要想回答这个问题,您需要具备以下所有知识/经验
- HHVM如何在请求之间缓存RAM中的数据结构
- 如何告诉HHVM数据结构将是常量
- 如何声明数组索引和值类型
我需要处理整个数组,所以需要加载和处理大量数据。(局域网上几分钟内就有数百万次请求)。我完成请求的速度越快,我完成工作的速度就越快。如果HHVM必须在每个请求上加载这些数据,那么它将占完成请求时间的很大一部分(有时超过一半,这取决于我当时所做分析的复杂性)。
我找到了一种方法,可以让我将这些数据结构缓存在RAM中(无需从文件加载、解释代码、无故向数组推送数十万次、无需重复进行非序列化等),因此我消除了这种巨大的可测量延迟。
关于如何使更快,我有3个问题:
- 我现在这样做是不是造成了全局范围的惩罚
- 如何将数组声明为常量并告诉HHVM需要什么数据类型?如果我将数组声明为常量,是否有必要声明HHVM的类型
- 如果我使用3个独立的数据结构ImmVector、PackedArray或定义一个类,而不是使用嵌套数组,会更快吗
请记住,任何阻止HHVM在请求之间缓存RAM中数据结构的行为都应被视为不可接受。
Lookuptable35543.php
<?php
$data = [
["uuid (20 chars)", 5336, 7373],
["uuid (20 chars)", 5336, 7373],
#more lines as above
];
?>
其中一些文件的大小有很多MB,而且有很多Main.php
<?php
function main() {
require /path/to/Lookuptable35543.php;
#(Do stuff with $data)
}
?>
这运行得很好,因为Main.php在短时间内收到了数千个请求,HHVM将Lookuptable.php的数据结构保存在内存中。避免无意义的处理和IO,因为它只是放在RAM中,随时可以使用。(我有足够的RAM)
不幸的是,我知道如何让HHVM将查找表保存在RAM中的唯一方法是,我在我的查找####.php文件中的全局范围中设置$data(然后要求将查找文件放入数据处理文件Main.php中的函数中)?通过这种方式,HHVM不需要加载文件或重新执行代码来创建$data,因为它可以看到$data可以在编译时确定,并且在运行时永远不会更改。这是有效的,但我不知道$data是否存在于查找###.php文件的全局范围中会受到惩罚。(或者可能它根本不是全局的,因为它是main.php函数的require
d?)
如果我从Lookup.php中的一个函数返回$data,并像这样从Main.php调用该函数,会怎么样Main.php
HHVM JIT会是RAM中getData()的结果吗?不知怎的,我把函数和不可预测性联系在一起。。。但也许HHVM足够聪明,知道函数的结果可以在编译时确定,并且永远不会改变?
我无法将查找表放在Main.php中,因为我根据请求类型require
个不同的查找表。
- 有没有办法告诉HHVM,我的外部数组将始终有一个永远不变的整数索引,并且外部数组的值将始终是一个数组?也许我需要使用ImmVector?那么,有没有一种方法可以告诉HHVM,我的内部数组将始终是一个固定长度的字符串,后面跟着2个整数,始终没有额外的元素,内容永远不会改变
我宁愿不使用OO或创建类。如何声明类型、过程样式?如果一个类是绝对必要的,你能给出适合我上述要求的示例代码吗?
- 如果我不嵌套数组会更快吗?我刚刚意识到我可以有一个数组,其中包含整数索引和固定长度字符串的值。然后是第二个包含整数索引和整数值的数组,第三个包含整数指数和整数值
如果你不熟悉这种HHVM缓存技术,请不要浪费时间建议数据库、redis、APC、取消序列化等。最快的是HHVM只将我的各种$data变量保存在RAM中。即使是从ramdisk文件中取消$data的序列化也很慢,因为必须将整个数据结构解析为字符串,并为每个请求转换为内存中的数据结构。据我所知,APC也有同样的问题。我甚至不想复制$data。查找表是不可变的,只读的。它们必须在RAM中保持完全结构化。我目前的缓存解决方案(在这个问题的顶部)已经给了我巨大的收益,但根据我的3个问题,我认为可能还有更多的收益?
如果你想知道,我已经测量了各种数据加载或缓存方法的延迟。现在,我基本上想保留我的缓存情况,但让HHVM JIT对如何键入我的数据有最大的信心,这样它就可以节省不运行类型甚至绑定(数组大小)检查的时间。
编辑好的,所以还没有人能给我任何代码示例,所以我只是在尝试。
以下是我迄今为止的发现。
常量数组在HHVM中还不能工作。
const foo = ['uuid1',43,43];
引发一个关于HHVM仅支持具有标量值的常量的错误。向量与数组值:我还不知道它将如何执行。。。我预计它会比普通数组更好。这是有效的HH代码。
这是一个进步,因为HHVM应该能够以相同的方式缓存它,HHVM知道整个结构是恒定的,并且HHVM知道索引都是整数。
我对这种结构仍然不完全满意的是:考虑这个代码
for ($n=0;$n<count($iv);++$n) if ($x > $iv[$n][1]) dosomething();
HHVM会在每次循环迭代中对$if[$n][2]
执行类型检查吗?在我上面对$iv
的定义中,没有任何内容表明内部数组的第二个元素将是整数。我该如何改进?禁用类型检查器有什么用吗?这只是对外部类型检查器隐藏错误,还是阻止HHVM不断进行类型检查?(我认为这是第一件事)
也许我可以制作自己的用户定义类型来解决这个问题?
<?hh
#I don't know what mechanisms for UDT's exist, so this code is made-up
CreateUDT foo = <string,int,int>;
$iv = ImmVector<foo> {
['uuid1',425,244],
['uuid2',658,836]
};
print_r($iv);
我在Hack Collections Literal Syntax Vector<Foo>不幸的是,它可能还不能使用。
我是Facebook的一名软件工程师,负责HHVM。
对我来说,整个问题充满了过早优化的味道。你是否完成了分析并确定加载此数组实际上是你的应用程序的瓶颈?(不仅是微基准,还有它如何实际影响现实页面加载的性能、延迟、RPS等。
一般来说,HHVM非常擅长处理数组,因为它们在几乎每个代码路径中都非常热门,尤其是在像这样的常量数组中。对于您关于如何告知数组中事物的形状和类型的问题,HHVM可以自行解决,并且非常擅长在完全由常量组成的常量数组上这样做。(它思考数组的方式与你思考数组的方法不同,所以它可能会做得更好!)基本上,除非评测表明这实际上是一个热点——我对此持怀疑态度——否则我不会太担心
- 衡量每一个性能差异。不要过早优化——使用评测来指导。过早的优化阻碍了开发人员的工作效率,这可能是致命的
- 尽可能地把事情从顶层("伪主干")拿出来。返回
static
或常量数组的函数应该很好,并且通常会帮助HHVM更好地优化代码 - 如果您非常关心性能,请尽可能避免引用,尤其是
- 您可能应该研究回购授权模式,它可以帮助HHVM更好地优化很多事情——但特别是在这种情况下,回购授权模式可以做的更积极的内联可能是一个胜利
编辑,除此之外:
,因为必须将整个数据结构解析为字符串,并为每个请求转换为内存中的数据结构。据我所知,APC也有同样的问题
这正是我所说的过早优化的意思:你甚至没有尝试就拒绝了APC,即使它可能是一种更清洁的方式来做你想要的事情。事实证明,在大多数情况下,HHVM实际上可以优化APC中存储数组的序列化/反序列化,特别是如果它们是从未修改过的常量数组。如上所述,HHVM非常擅长优化许多常见模式。只需编写干净的代码,对其进行评测,并修复热点。
好的,我已经解决了我的第一个问题。
-
我没有任何全局范围的问题。我的需求是从函数main()内部完成的,所以就好像将lookuptable####.php中的代码插入到函数main()中一样。HHVM文档:"如果include发生在函数内部…"基本上,如果您要打开lookuptable####.php,那么代码看起来像是在全局范围内,但这不是从hhvm请求的文件。main.php是被请求的,因此在全局范围内没有代码。
-
我想我已经回答了我的第二个问题,它目前在我问题的底部。我不是100%相信,但我很高兴继续前进并测试它。