【问题标题】:PHP - Slow performance on function return assignmentPHP - 函数返回分配的性能缓慢
【发布时间】:2016-08-02 15:10:51
【问题描述】:

作为项目的一部分,我遇到了这种情况,在循环中,我存储了函数返回的值。 这恰好是应用程序的一个瓶颈,大型数组需要很长时间才能处理完毕。

对我来说,作业不应该是令人难以置信的缓慢表现的原因。 另一方面,相同的函数调用,在返回时没有赋值,性能要好得多。

你能解释一下为什么第一个循环慢得多吗?

Output: First took 1.750 sec. Second took 0.003 sec.

class one {

    private $var;

    public function __construct() {
         $this->var = array();
    }

    public function saveIteration($a) {     
        $this->var[] = $a;
        return $this->var;
    }

    public function getVar() {      
        return $this->var;      
    }
}

$one = new one();    
$time_start = microtime(true);

for ($i = 0; $i < 10000; $i++) {
    $res = $one->saveIteration($i);
}    
echo "First took " . number_format(microtime(true) - $time_start, 3) . " sec.";

$time_start = microtime(true);

for ($i = 0; $i < 10000; $i++) {
    $one->saveIteration($i);
}  

$res = $one->getVar();
echo "<br>Second took " . number_format(microtime(true) - $time_start, 3) . " sec.";

【问题讨论】:

  • 它不仅仅是一个赋值......它扩展了一个数组,这需要内存分配(以及其他开销)
  • @MarkBaker 数组扩展没有解释为什么第一个循环更慢,因为扩展发生在两个循环中

标签: php performance function variable-assignment


【解决方案1】:

根据http://php.net/manual/en/functions.returning-values.php#99660,数组返回值不是按引用传递,而是按值传递。这意味着创建了数组的副本(至少,当您再次更改它时),这反过来需要时间来实际创建副本(分配内存,memcopy 数据)。

【讨论】:

  • 是的,就是这样。你可以通过引用强行返回,但鉴于这个例子,我不知道你为什么会
  • @PeterBailey 特别是因为它是一个私有变量。这个例子无论如何都是人为的,所以问题是,为什么这最终会成为一个问题^^
  • @Jakumi 这是一个试图理解我的错误的问题,所以我不再重复了。在我看来(我不是专家),内部复制操作的时间太荒谬了。如果我错了,请纠正我,使用 ahmad 的未设置提示,看起来像是内存管理不善的情况。
【解决方案2】:

这可能与您创建 10.000 个数组的事实有关;每次将 new 数组的元素数量增加 1 个元素。

我的猜测是,当您在循环中时,局部变量不会自行释放;因此,我继续尝试使用 unset 释放它,结果非常接近。

我知道这不是一个真实的例子;但是在你的代码中,如果你有类似的东西,你可以在完成后释放(取消设置)局部变量来摆脱它

这是你的测试代码:

class one {

    private $var;

    public function __construct() {
         $this->var = array();
    }

    public function saveIteration($a) {
        $this->var[] = $a;
        return $this->var;
    }

    public function getVar() {
        return $this->var;
    }
}

$one = new one();
$time_start = microtime(true);

for ($i = 0; $i < 10000; $i++) {
    $res = $one->saveIteration($i);
    unset($res);
}
echo "First took " . number_format(microtime(true) - $time_start, 3) . " sec.".PHP_EOL;

$time_start = microtime(true);

for ($i = 0; $i < 10000; $i++) {
    $one->saveIteration($i);
}

$res = $one->getVar();
echo "Second took " . number_format(microtime(true) - $time_start, 3) . " sec.".PHP_EOL;

注意:我唯一修改的是在第一个示例中添加 unset

结果:

  • 首次耗时 0.068 秒。
  • 第二次耗时 0.062 秒。

【讨论】:

  • "您每次都创建 10.000 个数组" 您能进一步解释一下吗?当$i 从 0 递增到 1 时,如何再创建 10,000 个数组?
  • 我的错,我在这里少了一个分号,我会更新答案以避免混淆
  • 大声笑,这有什么不同。你的观察真的很有趣;谢谢分享。我不明白为什么unset 会产生如此巨大的影响。毕竟,如果我们用新的$res = 赋值覆盖前一个数组,并且没有其他变量引用前一个数组,那么它所占用的资源不应该是免费的吗? unset 不应该是多余的吗?
  • 如果您在循环之前声明变量 $res,并在返回之前取消设置,则代码会满足其目的并使其恢复到类似的性能。 $res = 数组();对于 ($i = 0; $i saveIteration($i); }
  • @ahmad 感谢您提请我注意。
【解决方案3】:

@Jakumi 提出了一个很好的观点。由于在分配时必须复制值,因此在第一个循环中需要 10,000 次额外操作和更多内存。

这两个循环之间的差异实际上比您的测试显示的要大得多。如果在两个测试之间进行比较,您的比较会更公平:

unset($one); $one = new one();

在您当前的测试中,正在执行第二个循环,同时仍将第一个循环中的大数组保存在内存中,因此您的结果不是独立的。见this modification

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-02-01
    • 2017-05-09
    • 1970-01-01
    • 2018-10-15
    • 1970-01-01
    • 1970-01-01
    • 2010-10-14
    相关资源
    最近更新 更多