php循环有多糟糕

时间:2017-12-25 01:54:12

标签: php

我有一个小函数可以将数字转换为1000到1k。

<?php

/**
 * /Converts a number into a short version, eg: 1000 -1k.
 *
 * @param int $number    The number
 * @param int $precision The precision
 *
 * @return string The formated number
 */
function short_number_format($number, $precision = 1)
{
    $lookup = [
        1 => '',
        1000 => 'K',
        1000000 => 'M',
        1000000000 => 'B',
        1000000000000 => 'T',
    ];
    $result = '';
    foreach ($lookup as $boundary => $postfix) {
        if ($number < 900 * $boundary) {
            $result = number_format($number / $boundary, $precision).$postfix;
            break;
        }
    }
    //if we didnt get the result which is most likely to happen
    // for larger numbers (rarely) ,  return the plain number

    return $result ?: number_format($number, $precision);
}

一些高级开发人员告诉我,由于循环,代码非常慢。 那是真的吗?

3 个答案:

答案 0 :(得分:3)

试试这个:

0.

这样你就可以执行divison 一次而不是乘法每次

  • 编辑:改进答案:

由于分部比乘法花费更多的处理,我们最好将其更改为:

$lookup = [
    900 => '',
    900000 => 'K',
    900000000 => 'M',
    900000000000 => 'B',
    900000000000000 => 'T',
];

$result = '';
foreach ($lookup as $boundary => $postfix) {
    if ($number < $boundary) {
        $result = number_format($number / ($boundary/900), $precision) . $postfix;
        break;
    }
}
理论上,如果从具有大量迭代次数的另一个循环调用short_number_format()函数,理论上这应该更快,并且可以更快地进行操作

答案 1 :(得分:0)

在我看来,这个代码没有任何内容可以引起性能问题。固定长度循环是O(1)操作,几个基本的数学计算根本不是性能问题的根本原因。

我修复了你的后缀中的千兆字节错误,并咨询你的开发人员,看看他/她的关注是否是因为在运行时实际上出现了性能问题。也许你可以提供一些定时器来测量哪些操作使用最多的时间。您还可以创建一个单元测试,以验证您已经直观了解自己的功能表现。

添加:我创建了一个基于字符串的版本,它不使用循环并根据您的版本计时。虽然无循环版本的运行速度可能会稍微快一些,但您会发现现有版本已经可以在相当适中的CPU上每秒处理数十万个请求。对您的功能进行微观优化不太可能产生任何明显的影响。

<!DOCTYPE html>
<html>
<body>
<?php
$t1 = microtime(true);
for ($i=0; $i<100000; $i++) {
    scaleV1($i, $i%3 + 1);
}
timesince($t1);
$t2 = microtime(true);
for ($i=0; $i<100000; $i++) {
    short_number_format($i, $i%3 + 1);
}
timesince($t2);


function timesince($micro) {
    $sec=(microtime(true) - $micro);
    echo "<p>" . number_format($sec, 3) . "</p>\n";
}


function scaleV1($number, $precision=1) {
    $scale_id=" KMGTP";
    $w=strlen(intval($number));
    $scale=intval($w/3);
    if (($w%3==0) && (substr($number,0,1)!=9)) $scale--;
    $postfix=$scale==0 ? '' : $scale_id[$scale];
    return number_format(($number / pow(1000,$scale)), $precision) . 
$postfix;
}

function short_number_format($number, $precision = 1)
{
    $lookup = [
        1 => '',
        1000 => 'K',
        1000000 => 'M',
        1000000000 => 'G',
        1000000000000 => 'T',
    ];
    $result = '';
    foreach ($lookup as $boundary => $postfix) {
        if ($number < 900 * $boundary) {
            $result = number_format($number / $boundary, 
$precision).$postfix;
            break;
        }
    }
    return $result ?: number_format($number, $precision);
}
?>

</body>
</html>

答案 2 :(得分:0)

这是没有循环或分支的另一种方式:

 function short_number_format($number, $precision = 1)
{
    $lookup = [
               1 => [1,''],
               2 => [1,''],
               3 => [1,'']
               4 => [1000,'K'],
               5 => [1000,'K'],
               6 => [1000,'K'],
               7 => [1000000,'M'],
               8 => [1000000,'M'],
               9 => [1000000,'M'],
              10 => [1000000000,'B'],
              11 => [1000000000,'B'],
              12 => [1000000000,'B'],
              13 => [1000000000000,'T'],
              14 => [1000000000000,'T'],
              15 => [1000000000000,'T']
        ];

       $N = floor(log($number, 10) + 1);
       $result=$number/$lookup[$N][0]; 
return number_format ($result,$precision) . $lookup[$N][1];
}

在这个方法中,我们使用位数作为查找数组的索引,并使用0和1分别得到除数和字母。

另一种方法是使用(仅当$ number为double或float包含点时):

$N = strpos(strval($number),".")-1; // only if $number has dot like a double

-EDIT使用字符串函数而不是数学的另一种方法,假设输入数字是带点的双精度(未经测试):

function short_number_format($number, 
$precision = 1)
{
$lookup = [
           1 => [0,''],
           2 => [0,''],
           3 => [0,'']
           4 => [3,'K'],
           5 => [3,'K'],
           6 => [3,'K'],
           7 => [6,'M'],
           8 => [6,'M'],
           9 => [6,'M'],
          10 => [9,'B'],
          11 => [9,'B'],
          12 => [9,'B'],
          13 => [12,'T'],
          14 => [12,'T'],
          15 => [12,'T']
    ];
$sn = strval($number);
$N = strpos($sn,".") - 1;
$pos = $N - $lookup[$N][0];
$sn = str_replace('.', '', $sn) ;
$sn = substr_replace($sn, ".", $pos, 0);
return number_format (doubleval($sn),$precision) . $lookup[$N][1];


}
  • 在阅读超过20K的upvotes的答案后添加了非分支参数,它声明:

一般的经验法则是避免在关键循环中依赖数据进行分支

参考:https://stackoverflow.com/a/11227902/6281135

同样来自同一个线程,如果要保留IF语句,排序数据似乎运行得更快。通过数据我的意思是传递给你的函数的数据,以防你从另一个循环调用它。