【问题标题】:Optimizing Base Conversion Loop优化碱基转换循环
【发布时间】:2011-10-24 23:36:35
【问题描述】:

所以,对于我的密码库,我有一个经常使用的base converter。它不是世界上最高效的东西,但它适用于所有输入范围。

大部分工作由回调循环完成:

    $callback = function($source, $src, $dst) {
        $div       = array();
        $remainder = 0;
        foreach ($source as $n) {
            $e         = floor(($n + $remainder * $src) / $dst);
            $remainder = ($n + $remainder * $src) % $dst;
            if ($div || $e) {
                $div[] = $e;
            }
        }
        return array(
            $div,
            $remainder
        );
    };
    while ($source) {
        list ($source, $remainder) = $callback($source, $srcBase, $dstBase);
        $result[]                  = $remainder;
    }

基本上,它采用$srcBase 中的数字数组并将它们转换为$dstBase 中的数字数组。因此,示例输入将是array(1, 1), 2, 10,结果将给出array(3)。另一个例子是array(1, 0, 0), 256, 10,它将给出array(1, 6, 7, 7, 7, 2, 1, 6)(数组的每个元素都是$dstBase 中的一个“数字”。

我现在面临的问题是,如果我输入 2kb 的数据,它需要将近 10 秒才能运行。所以我开始优化它。到目前为止,通过用这个递归循环替换整个结构,我将它缩短到大约 4 秒:

    while ($source) {
        $div       = array();
        $remainder = 0;
        foreach ($source as $n) {
            $dividend  = $n + $remainder * $srcBase;
            $res       = (int) ($dividend / $dstBase);
            $remainder = $dividend % $dstBase;
            if ($div || $res) {
                $div[] = $res;
            }
        }
        $result[] = $remainder;
        $source   = $div;
    }

我面临的问题是如何进一步优化它(如果可能的话)。我认为问题在于大输入所需的迭代次数(对于 2000 个元素的数组,从 256 到 10,总共需要 4,815,076 次迭代)。

有什么想法吗?

【问题讨论】:

    标签: php algorithm optimization base-conversion


    【解决方案1】:

    执行此脚本所花费的时间中有 99.9% 源于对输入进行迭代的固有需求。由于 foreach 中的代码非常基本,因此减少执行时间的唯一方法是减少迭代次数。如果这不可行,那么您将拥有此功能的最高效版本。

    【讨论】:

    • 这就是我的观点。不是我如何优化$x % $y,而是如何改变算法以减少必要的迭代......
    【解决方案2】:

    是的,可以稍微优化一下:

    $source_count = count($source);
    while ($source) {
        $remainder = $i = 0;
        foreach ($source AS &$n) {
            $dividend = $n + $remainder * $srcBase;
            $remainder = $dividend % $dstBase;
            $res = ($dividend - $remainder) / $dstBase;
            if ($i || $res)
                $source[$i++] = $res;
        }
        for ($j=$i; $j < $source_count; $j++)
            unset($source[$i]);
        $source_count=$i;
        $result[] = $remainder;
    }
    

    甚至更快,但更模糊:

    $source_count = count($source);
    while ($source) {
        $remainder = $i = 0;
        foreach ($source AS &$n) {
            if (($res = ($dividend - ($remainder = ($dividend = $n + $remainder * $srcBase) % $dstBase)) / $dstBase) || $i)
                $source[$i++] = $res;
        }
        for ($j=$i; $j < $source_count; $j++)
            unset($source[$i]);
        $source_count=$i;
        $result[] = $remainder;
    }
    

    您会减少一些内存和 CPU 使用率,而且会更有趣,但当然是不可读的 (:.

    但我个人认为你做错了。我认为您应该为此类任务使用一些快速的 C 代码(通过使用系统调用或编写/安装现有的 PHP 模块)。而且我认为 Hip-Hop PHP、Zend Optimized 等代码优化器/编译器可以在这种情况下显着提高性能。

    【讨论】:

      【解决方案3】:

      我不确定,但是

      $dividend  = $remainder * $srcBase + $n;
      

      可能会快一点...

      【讨论】:

      • 你怎么看?为什么会更快?
      • 曾经读过关于做数学的内部方法,但我不确定。不是先读取整个函数,但是当第一次使用 * 时,PHP 可以在不读取下一个标记的情况下开始数学...
      • 还是要看下一个token,因为higher precedence还有其他的操作符。所以它不会有任何区别(或者如果有区别,那就是一个主要的区别),最多纳秒......
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-17
      • 1970-01-01
      • 2015-04-09
      • 1970-01-01
      • 2014-05-24
      相关资源
      最近更新 更多