HHVM静态输入查找表并将它们完全缓存在RAM

时间:2015-05-11 00:20:15

标签: hhvm hacklang

我正在进行科学研究,通过<强大>数百万的多兆字节阵列组合进行处理。

为了能够回答这个问题,您需要具备以下所有知识/经验

  • HHVM如何在请求之间缓存RAM中的数据结构
  • 如何告诉HHVM数据结构将是不变的
  • 如何声明数组索引和值类型

我需要处理整个数组,因此需要加载和处理大量数据。 (在局域网上几分钟内就有数百万的请求)。我可以更快地完成请求,我可以更快地完成工作。如果HHVM必须在每个请求上加载这些数据,它会占用完请求的很大一部分时间(有时超过一半,这取决于我当时正在进行的分析的复杂性)

我找到了一种方法,它允许我将这些数据结构保存在RAM中(无需从文件加载,解释代码,无理由地推送到数组数十万次,没有无意义的重复反序列化等),以及因此,我已经消除了这种巨大的可衡量的延迟。

关于如何使更快

,我有3个问题
  1. 我现在的做法是创建全局范围惩罚吗?
  2. 如何将我的数组声明为常量并告诉HHVM期望哪些数据类型? 如果我将我的数组声明为常量,甚至需要声明HHVM的类型?
  3. 如果我使用3个独立的数据结构ImmVector,PackedArray或定义类会更快,而不是使用嵌套数组?
  4. 请记住,任何阻止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可以在编译时确定,并且在运行时它不会改变。这有效,但我不知道在查找###。php文件的全局范围内是否存在$ data存在惩罚。 (或者它可能不是全局的,因为它是require到main.php的功能?)

    1. 如果我从Lookup.php中的函数返回$ data并从Main.php调用该函数怎么样? Main.php

    2. HHVM JIT会在RAM中产生getData()的结果吗? 不知怎的,我把函数与不可预测性联系起来......但是HHVM可能很聪明,知道函数结果可以在编译时确定,而且永远不会改变?

      我无法将查找表放在Main.php中,因为根据请求的类型我require个不同的查找表。

      1. 有没有办法可以告诉HHVM我的外部数组总是有一个永不改变的整数索引,外部数组的值总是一个数组? 也许我需要使用ImmVector? 那么有没有办法告诉HHVM我的内部数组将始终是一个固定长度的字符串后跟2个整数,总是没有额外的元素,内容永远不会改变?
      2. 我不想使用OO或创建课程。我怎样才能声明类型,程序风格? 如果一个课程是绝对必要的,您能否提供适合我上述要求的示例代码?

        1. 如果我不嵌套数组会更快吗? 我刚刚意识到我可以有一个带整数索引的数组和固定长度字符串的值。然后是具有整数索引和整数值的第二个数组,以及具有整数索引和整数值的第三个数组。
        2. 如果您不熟悉这种HHVM缓存技术,请不要浪费时间建议数据库,redis,APC,反序列化等.HHVM最快就是将各种$ data变量保存在RAM中。即使从ramdisk文件中反序列化$ data也很慢,因为整个数据结构必须解析为字符串,并在每个请求的内存中转换为数据结构。据我所知,APC也有同样的问题。我甚至不想复制$ data。查找表是不可变的,只读。它们必须保持完整的RAM结构。我目前的缓存解决方案(在这个问题的顶部)已经给了我巨大的收益,但根据我的3个问题,我认为可能会有更多的收获?

          如果你想知道,我已经测量了各种数据加载或缓存方法的延迟。 现在我基本上想要保持我所拥有的缓存情况,但是让HHVM JIT最大限度地确定如何键入我的数据,这样可以节省时间而不是运行类型甚至绑定(数组大小)检查。

          修改 好的,所以没有人能够给我任何代码示例,所以我只是尝试一下。

          这是我到目前为止所发现的。

          • const数组还没有在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的定义中,没有任何内容表明内部数组的第二个元素是一个整数。 我该如何改进呢? disabling the type checker可以用吗?这是否只隐藏外部类型检查器的错误,还是阻止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>中找到了对此的引用,遗憾的是它可能尚未可用。

2 个答案:

答案 0 :(得分:1)

我是Facebook的一名软件工程师,负责HHVM。

这整个问题让我过早优化。您是否完成了分析并确定加载此阵列实际上是您的应用程序的瓶颈? (不仅仅是微基准,而是它如何影响现实页面加载的性能,延迟,RPS等。)并且还与其他效果隔离,例如,如果此数组是缓存或某种预先计算的数据,您需要通过以各种不同的方式缓存数据与实际时间来预先计算数据,以加载数据。

通常,HHVM非常擅长处理数组,因为它们几乎在每个代码路径中都很热 - 特别是在像这样的常量数组中。关于如何告知数组中事物的形状和类型的问题,HHVM可以自己解决这个问题,并且非常在完全由常量组成的常量数组上做得很好。 (它对数组的思考方式并不是对数组的思考方式,所以无论如何它都可以做得更好!)基本上,除非分析说这实际上是一个热点 - - 我对此持怀疑态度 - 我不会过分担心它。一些注意事项需要注意:

  • 测量每个性能差异。不要过早地优化 - 使用分析来指导。过早优化导致开发人员的工作效率下降可能会致命。
  • 尽可能让事情摆脱困境(&#34;假象和#34;)。返回static或常量数组的函数应该没问题,并且通常可以帮助HHVM更好地优化代码。
  • 如果您非常关心性能,请尽可能避免引用,尤其是
  • 你可能应该研究repo authoritiative mode这可以帮助HHVM进一步优化很多东西 - 但特别是对于这种情况,repo auth模式可以做的更积极的内联可能是一个胜利。

编辑,旁边

  

因为那时必须将整个数据结构解析为字符串,并在每个请求的内存中转换为数据结构。就我所知,APC遇到同样的问题

这正是我过早优化的意思:你甚至在没有尝试的情况下拒绝接受APC,即使它可能是一种更干净的方式来做你想做的事情。事实证明,在大多数情况下,HHVM实际上可以优化APC中存储数组的序列化/反序列化,特别是如果它们是永不修改的常量数组。如上所述,HHVM非常擅长优化大量常见模式。只需编写干净的代码,对其进行分析并修复热点即可。

答案 1 :(得分:0)

好的,我已经解决了我的第一个问题。

  1. 我没有任何全球范围问题。我的需求是从函数main()内部完成的,所以它好像是可查询的####。php中的代码被插入到函数main()中。 HHVM docs: "If the include occurs inside a function..." 基本上,如果您打开可查找的####。php,它看起来像代码在全局范围内,但那不是从hhvm请求的文件。 main.php是被请求的,因此全局范围内没有代码。

  2. 我想我已回答了我的第二个问题,它目前在我的问题的最底层。我并非100%相信,但我很乐意继续前进并进行测试。