2

我有一个 PHP 脚本,它需要几个小时(也许几天)才能执行。它非常简单但非常占用 CPU,大部分执行时间都花在了(我可以在分析脚本后知道):

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

  2. foreach($array as $key => $value)循环;对于每个循环,执行一些 in_array() 以及比较和赋值操作

$a实际上是一个非常大且稀疏的矩阵(30k * 30k)但我无法将它保存在内存中(8GB 似乎没有足够的 RAM)所以我只保留一个“稀疏表示”(基本上每一行都是一个字符串)并使用explode()任何时候我需要连续工作。

我知道用 C(或其他语言)重写所有内容会提高性能(多少?)但是,在这样做之前,我想知道我是否可以做任何事情来改善 PHP 的执行时间。

回答后编辑。

我尝试了您的一些建议,这是我的报告:

1) str_getcsv 在大多数情况下比爆炸慢

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

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

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

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

编辑2:我解释了问题

我将提供有关该问题的一些详细信息,我确实需要简化所有内容,否则解释时间太长,但我认为这足以更好地了解情况。

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

我有一个用三元组表示每个距离的内存表:node_1、node_2、距离(只表示非无限距离)。

我有这种我没有编写的贪心算法,我应该优化以在具有 8GB RAM 的笔记本电脑上在可行的时间内(比如说不到一天)执行它。

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

  • 必须在相对于当前节点更接近结束节点的节点集合中选择新的中间节点
  • 在这些节点中,选择离当前节点最近的那个

请考虑 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
        }
    }

}

我省略了很多检查和额外的复杂性,但这是基本的,也是算法花费大部分时间的地方。

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

谢谢。

4

3 回答 3

8

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

第一步,不要猜测。不要开始用 C 重写。不要切换 PHP 编译器。那是给傻逼的。相反,首先要尝试找到实际的瓶颈。

获取 XDEBUG 并生成应用程序的cachegrind 分析。这将向您显示大部分时间都花在了哪里。

您也可以使用xhprof

关键是,不要猜测,而是个人资料。找到算法的慢部分,然后努力优化它们。

问题可能不是代码,而是您使用的算法。我建议尝试将算法形式化,以便您可以尝试针对您的特定约束优化和调整部分。

例如。现在,您正在解析大型 CSV 字符串。为什么?为什么不将它保存在数据库中,让数据库为您完成繁重的工作?显然,对于您的特定用例,这可能是不可能的,但是每当我看到人们在 PHP 中对 30k 元素的数组进行操作时,通常那是因为他们正在做一些他们一开始就不应该做的事情。

如果所有其他方法都失败了,请尝试对算法进行分块,以便您可以部分运行它。这样,您可以尝试使用 map-reduce 或类似技术来调整运行时。

简而言之,这实际上取决于您到底在做什么。但是重新编码或切换运行时将是我最后的手段,而不是第一步......

于 2013-06-24T16:12:30.683 回答
-1

用 C 重写它会快很多!

您可以str_getcsv($a[$i]);改用它会更快一些。

关于 RAM,请随心所欲地处理数据和使用unset($a[$i])情况。

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

于 2013-06-24T15:26:03.280 回答
-1

你听说过 Facebook hiphop 编译器吗?你可以试试这个。它有助于更​​快地执行脚本并占用最少的 CPU 资源。

于 2013-06-24T15:38:09.530 回答