我有一个 PHP 脚本,它需要几个小时(也许几天)才能执行。它非常简单但非常占用 CPU,大部分执行时间都花在了(我可以在分析脚本后知道):
$array = explode(',', $a[$i]);
其中$a[$i]
是一个非常长的字符串,它表示由逗号分隔的 30k 个元素的向量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* 的实现来解决,但如果可以提高性能,我想避免它,以便我可以在几小时(而不是几天)内执行它。
谢谢。