【问题标题】:Is there a faster way than x >= start && x <= end in PHP to test if an integer is between two integers?有没有比 PHP 中的 x >= start && x <= end 更快的方法来测试一个整数是否介于两个整数之间?
【发布时间】:2016-01-18 10:54:23
【问题描述】:

这是一个与Fastest way to determine if an integer is between two integers (inclusive) with known sets of values 类似的问题,但由于 php 没有被严格输入并且没有可控的整数溢出,因此接受的答案在 php 中不起作用(据我所知)。

这里的用例是确定一个整数是否介于 65 和 90 之间('A' 和 'Z' 的 ASCII 值)。这些边界可能有助于优化解决方案,因为 64 是 2 的幂并充当此问题的边界条件。

到目前为止,我提出的唯一伪优化是:

//$intVal will be between 0 and 255 (inclusive)
function isCapital($intVal)
{
    //255-64=191 (bit mask of 1011 1111)
    return (($intVal & 191) <= 26) && (($intVal & 191) > 0);
}

与$intVal &gt;= 65 &amp;&amp; $intVal &lt;= 90 的正常双重比较相比,此功能并没有太大的改进(可能更慢),但这正是我在尝试优化时开始前进的地方。

【问题讨论】:

  • 嗯,我认为如果你到了需要进行这些优化的地步,那么你选择了错误的语言
  • 为什么不使用if (preg_match("/[A-Z]/",$char,$foo)) {}?
  • 如上,除非这是一个纯粹的学术练习,那么你可能想用c写一个php扩展
  • @Wobbles,有趣的是你提出了 preg_match,这是我一直在对我的解决方案进行基准测试的函数。完整的用例一直在尝试将camelCase 字符串优化为snake_case 字符串。到目前为止,我的速度比 preg_replace 快 20%。
  • @steve,这主要是一种学术练习,类似于 php 中的代码高尔夫挑战。

标签: php optimization


【解决方案1】:
function isCapitalBitwise($intVal) {
    return (($intVal & 191) <= 26) && (($intVal & 191) > 0);
}
function isCapitalNormal($intVal) {
    return $intVal >= 65 && $intVal <= 90;
}
function doTest($repetitions) {
    $i = 0;
    $startFirst = microtime();
    while ($i++ < $repetitions) {
        isCapitalBitwise(76);
    }
    $first = microtime() - $startFirst;
    $i = 0;
    $startSecond = microtime();
    while ($i++ < $repetitions) {
        isCapitalNormal(76);
    }
    $second = microtime() - $startSecond;
    $i = 0;
    $startThird = microtime();
    while ($i++ < $repetitions) {
        ctype_upper('A');
    }
    $third = $startThird - microtime();
    echo $first . ' ' . $second . ' ' . $third . PHP_EOL;
}
doTest(1000000);

在我的系统上返回:

0.217393 0.188426 0.856837

PHP 在按位运算方面不如编译语言...但更重要的是,我必须进行一百万次比较才能获得不到百分之三秒的差异。

即使ctype_upper() 也处于“您每年可以节省几秒钟的 CPU 时间”的范围内,与这些其他比较方式相比,您不必先致电ord()。

追求可读性。追求可维护性。编写您的应用程序,然后分析它以查看您的真正瓶颈在哪里。

【讨论】:

    【解决方案2】:

    与其重新造轮子,不如使用预建的php方法ctype_upper

    $char = 'A';    
    echo ctype_upper($char) ? "It's uppercase" : "It's lowercase";
    

    你甚至可以传入一个字符的整数值:

    echo ctype_upper($intVal) ? "It's uppercase" : "It's lowercase";
    

    http://php.net/manual/en/function.ctype-upper.php

    即使您确实找到了一种方法,而不是通过 && 或我上面粘贴的方法进行比较,也将是微秒级的差异。您将浪费数小时想出一种在一年内节省几秒钟的方法。

    【讨论】:

    • 我希望我能多次投票。而是:$Premature_Optimization === 'Premature Tar Pit of Death';
    • 我不知道这个功能,这可能正是我想要的!我只需要先尝试一下确认。
    【解决方案3】:

    来自How to check if an integer is within a range?:

    t1_test1: ($val >= $min && $val

    t2_test2: (in_array($val, range($min, $max)): 9.3301 毫秒

    t3_test3: (max(min($var, $max), $min) == $val): 0.7272 ms

    您也可以将范围与字符(A、B、C...)一起使用,但正如您所见,这不是一个好方法。

    【讨论】:

      【解决方案4】:

      我认为通过原生方式可以获得最好的结果,但速度只会快一点。直接使用ctype_upper。这是我的测试。

      <?php
      $numTrials = 500000;
      $test = array();
      for ($ii = 0; $ii < $numTrials; $ii++) {
        $test[] = mt_rand(0, 255);
      }
      
      function compare2($intVal) {
         return $intVal >= 65 && $intVal <= 90;
      }
      
      $tic = microtime(true);
      for ($ii = 0; $ii < $numTrials; $ii++) {
         $result = compare2($test[$ii]);
      }
      $toc = microtime(true);
      echo "compare2...: " . ($toc - $tic) . "\n";
      
      $tic = microtime(true);
      for ($ii = 0; $ii < $numTrials; $ii++) {
         $result = ctype_upper($test[$ii]);
      }
      $toc = microtime(true);
      echo "ctype_upper: " . ($toc - $tic) . "\n";
      
      echo "\n";
      

      这给出了相当一致的东西:

      compare2...: 0.39210104942322 
      ctype_upper: 0.32374000549316
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-03-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-01-16
        相关资源
        最近更新 更多