【问题标题】:PHP - Recursive in place replacing of strings to numbers takes too longPHP - 将字符串递归替换为数字需要太长时间
【发布时间】:2016-10-20 21:14:34
【问题描述】:

因为 MYSQL 的“SELECT”选择整数和浮点数作为字符串,并且我需要(从 JS)获得的每个响应都在正确的数据模型中 -

  • 1 不是“1”,
  • 53.2 不是“53.2”,

我创建了这个适用于混合类型 - 数组/对象的递归函数:

private function cast_number(&$mixed) {
    if(is_array($mixed)) {
        foreach ($mixed as $key => $val)
            if (is_numeric($val))
                $mixed[$key] = (double)$val;
            else if (is_array($val) || is_object($val))
                $mixed[$key] = $this->cast_number($val);
    } else if(is_object($mixed)) {
        foreach ($mixed as $key => $val)
            if (is_numeric($val))
                $mixed->$key = (double)$val;
            else if (is_array($val) || is_object($val))
                $mixed->$key = $this->cast_number($val);
    }
    return $mixed;
}

非常简单的函数 - 如果是数字,则强制转换为双精度,如果是数组或对象,则递归遍历。

这里一切就绪。

我有两个问题: - 在 6MB 的数据上,大部分以字符串表示的数字需要 0.5 秒 - 在 200MB 的数据上(是的,我需要它,请不要关注它),它在几分钟(通常是几秒钟)后失败,说它需要超过 4GB 的内存..

  1. 如何改进此功能? (速度、记忆力)
  2. 为什么需要这么长时间?甚至 json_encode,我认为更大的功能需要更少的时间..

【问题讨论】:

  • 您是否尝试过输入杂耍:$mixed[$key] = 0 + $val;
  • @IvanGabriele 还没有。我不明白这对速度有什么帮助?当然,内存几乎是 4 个字节,而不是大多数时候的 8 个。现在正在尝试
  • 您的问题还有另一种解决方案。这是在服务器上更新 mysqlnd,以便 PHP 从 MySQL 中获取正确的数据类型...Here's more info
  • 您可能还想与codereview.stackexchange.com的人们核实
  • @Amit 我给了你一个完整的答案,向你展示不同的时间,你可以看到,如果通过引用结合变量,它会大大降低执行时间。

标签: php mysql performance recursion memory-management


【解决方案1】:

由于强制 used to be faster than casting,我运行此代码来计算 PHP 7 上的时间:

function getTime($start) {
    return round((microtime(true) - $start) * 1000000) / 1000;
}

function mockData($length) {
    $data = [];
    $i = -1;

    while ($i++ < $length) {
        $data[$i] = strval(rand(1, 10000) / 100);
    }

    return $data;
}

$data = mockData(100000);

// Let's check that they are string before
echo gettype($data[0]) . '<br><br>';

$start = microtime(true);
$convertedData = [];
foreach ($data as $key => $value) {
    $convertedData[$key] = (double) $value;
}
echo '(double) cast took ' . getTime($start) . ' ms.<br>';

$start = microtime(true);
$convertedData = [];
foreach ($data as $key => $value) {
    $convertedData[$key] = 0 + $value;
}
echo 'Coercion took ' . getTime($start) . ' ms.<br>';

我的结果是:

(double) cast took 27.508 ms.
Coercion took 28.789 ms.

结论

由于使用floatval(实现字符串到双精度转换的第三种方法)甚至更长,因此您无法比使用 PHP 做得更好。您要实现的是脚本操作,不应将其用作 Web 应用程序的正常后端操作。

但如果你仍然想这样做,你可以在你的php.ini 文件中提高你的memory_limit as long as you don't use the -1 workaround

更新

我忘记了一种可能的优化,你应该 pass your variable by reference 至少执行立即分配:

$start = microtime(true);
foreach ($data as $key => $value) {
    $data[$key] = (double) $value;
}
echo getTime($start) . ' ms.<br>';

=> 34.018 毫秒。

$start = microtime(true);
foreach ($data as &$value) {
    $value = (double) $value;
}
echo getTime($start) . ' ms.<br>';

=> 17.081 毫秒。

显然使用强制引用可以得到更好的结果:

$start = microtime(true);
foreach ($data as &$value) {
    $value = 0 + $value;
}
echo getTime($start) . ' ms.<br>';

=> 13.1 毫秒。

【讨论】:

  • 这些是非常好的比较!但它可能是在 PHP7 上。我忘了提,我使用的是 5.5.34(无法升级),这里有一个例子:sandbox.onlinephpfunctions.com/code/… - 它需要 0.35 秒,看起来非常优化.. 看起来确实如此现在不要占用太多内存,这很棒
  • @Amit 这与我在 13.1 毫秒内找到的功能完全相同...无论如何,它不会改善您的内存问题,这与您的原始数据大小太大有关.你仍然需要提高你的memory_limit。您应该 read that 了解 PHP 中的内存分配是如何工作的。
【解决方案2】:

这应该会提高速度和内存:

private function cast_number(&$mixed)
{
    foreach ($mixed as &$val) {
        if (is_numeric($val)) {
            $val = (double) $val;
        } else if (is_array($val) || is_object($val)) {
            $this->cast_number($val);
        }
    }
    // do not return $mixed
}

但也许您可以使用array_walk_recursive 函数实现更大的改进。

【讨论】:

  • 虽然这行得通,但它并没有改变很多执行时间。此外,数组遍历递归无法处理对象
  • 这就是为什么我没有提供 array_walk_recursive 的解决方案;它实际上部分可以,如果你的对象实现ArrayAccess它可以;
猜你喜欢
  • 2011-05-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-27
  • 1970-01-01
  • 2017-01-30
  • 2017-04-24
  • 1970-01-01
相关资源
最近更新 更多