提高CPU密集型PHP脚本的性能

提高CPU密集型PHP脚本的性能,php,c,performance,Php,C,Performance,我有一个PHP脚本,它需要几个小时(也许几天)才能执行。它非常简单,但CPU非常密集,大部分执行时间都花在以下方面(我在分析脚本之后可以看出): $array=explode(',',$a[$i]) 其中,$a[$i]是一个非常长的字符串,表示由30k个元素组成的向量,用逗号分隔 foreach($key=>$value的数组)loops;其中,对每个循环执行一些in_array()以及比较和赋值操作 $a实际上是一个非常大且稀疏的矩阵(30k*30k),但我无法将其保存在内存中(8GB似乎不够

我有一个PHP脚本,它需要几个小时(也许几天)才能执行。它非常简单,但CPU非常密集,大部分执行时间都花在以下方面(我在分析脚本之后可以看出):

  • $array=explode(',',$a[$i])
    其中,
    $a[$i]
    是一个非常长的字符串,表示由30k个元素组成的向量,用逗号分隔

  • foreach($key=>$value的数组)
    loops;其中,对每个循环执行一些in_array()以及比较和赋值操作

  • $a
    实际上是一个非常大且稀疏的矩阵(30k*30k),但我无法将其保存在内存中(8GB似乎不够内存),因此我只保留一个“稀疏表示”(基本上每行都是一个字符串),并在需要处理行时随时使用
    explode()

    我知道用C(或其他语言)重写所有东西都会提高性能(多少?),但是在这样做之前,我想知道我是否可以做些什么来提高PHP的执行时间

    在回答后编辑

    我尝试了你的一些建议,以下是我的报告:

    1) str_getcsv在大多数情况下都比explode慢

    2) SPLFixedArray减少了存储矩阵所需的内存,但8GB仍然不足以存储30k x 30k矩阵,因此我认为它没有多大帮助;这里真正的问题是在PHP中缺少矩阵的稀疏表示

    3) 我无法存储爆炸操作的所有结果,因为这仍然意味着将整个矩阵保存在内存中(内存不足)

    4) 我尝试过数据库方法,即使我确信它会更慢:我存储了三元组(I,j,value)来表示每个矩阵元素;即使删除不太重要的值(我可以牺牲小于阈值的值,得到不太精确的结果,但仍然有用)并只存储1800万个元组,mysql myisam的方法也比内存中的数组方法慢得多

    5) 我尝试过使用内存引擎(RAM中的一个mysql表)的数据库方法,并存储除值为零的矩阵元素以外的所有矩阵元素;这次有4200万张唱片…速度更快,不是一个数量级,而是2-4倍…我想我可以在5天内完成这项工作,而不是15-20天…这仍然太多(我希望在24小时内完成),如果你有任何其他建议,欢迎你

    编辑2:我解释这个问题

    我将给出一些关于这个问题的细节,我真的需要简化所有的事情,否则解释起来会太长,但我认为这足以更好地理解情况

    我有一个表示节点之间距离的矩阵;距离可以是整数,也可以是无限的

    我有一个内存表,用三元组表示每个距离:node_1,node_2,distance(仅表示非无限距离)

    我有一种贪婪的算法,我没有写,我应该优化,在一台有8GB内存的笔记本电脑上,在一个可行的时间内(比方说不到一天)执行它

    算法基本上输入两个节点,并根据每一步必须验证的以下两个属性,逐步设计起始节点和结束节点之间的路径:

    • 新的中间节点必须在相对于当前节点更接近结束节点的节点集中选择
    • 在这些节点中,选择最靠近当前节点的节点
    请考虑一下 1) 三角形不等式不满足。 2) 这不是一个最短路径问题

    下面是我多次调用的函数的一些伪代码,直到我离结束节点足够近:

    get_next_node($node_1, $node_2){
    
        $dist = select distance from distances_table where node_2 = $node_2 and node_1 = $node_1
    
        $candidates_ar = select node_1 from distances_table where node_2 = $node_2 and distance < $dist
    
        $distances_ar = select distance from distances_table where node_1 = $node_1 and node_2 in ($candidates_ar) // e.g. $distances_ar[12] contains distance between node 12 and $node_1
    
        $min = 1000;
        foreach ($candidates_ar as $value){
            if ($distances_ar[$value] < $min){
                $min = $distances_ar[$value]
                $next_node = $value
            }
        }
    
    }
    
    get_next_节点($node_1,$node_2){
    $dist=从距离表中选择距离,其中节点_2=$node_2和节点_1=$node_1
    $candidates_ar=从距离表中选择节点_1,其中节点_2=$node_2,距离<$dist
    $distances\u ar=选择距离表中的距离,其中节点1=$node\u 1和节点2位于($candidates\u ar)//例如,$distances\u ar[12]包含节点12和$node\u 1之间的距离
    $min=1000;
    foreach($value){
    如果($distance_ar[$value]<$min){
    $min=$distance\u ar[$value]
    $next_node=$value
    }
    }
    }
    
    我省略了很多检查和额外的复杂性,但这是基本的,这是算法花费大部分时间的地方

    我想它可以通过一个*的实现来解决,但是如果可以提高性能,使我可以在几个小时(而不是几天)内执行它,我想避免它


    谢谢。

    用C重写会快得多

    你可以使用str_getcsv($a[$i])相反,这会快一点

    关于RAM,对数据做您想做的事情,并在执行过程中使用
    unset($a[$i])


    因此,要么用C重写,要么分阶段进行,将CSV分成10个块,并以这种方式进行处理,甚至可以同时运行所有10个块,这可能会提高速度。或者将你的CSV文件放在数据库中以真正降低速度。

    你听说过Facebook hiphop编译器吗。你可以试试。
    这有助于加快脚本的执行速度,减少cpu占用。

    好的,您遇到了性能问题。现在有趣的部分开始了

    第一步,不要猜测。不要开始用C语言重写。不要切换PHP编译器。那是给笨蛋的。相反,从寻找实际的瓶颈开始

    获取XDEBUG并生成应用程序的。这会告诉你大部分时间花在哪里

    您也可以使用

    重点是,不要猜测,但是轮廓。找出算法中缓慢的部分,然后对其进行优化

    问题可能不是代码,而是您正在使用的算法。我建议你试着