为什么php4.3中的bcmath比php5+快得多


Why is bcmath in php 4.3 so much faster than php 5+?

决定运行一个快速测试,看看bcmath如何在各种版本的PHP上运行,并注意到与4.3相比,最新和最新的版本在速度上明显不足,

我想知道是否有人知道这背后的原因,和/或如何在5+上提高速度,使其与4.3相当。

还要注意,5.6+的内存消耗是4.3相同操作所需的三倍:

性能测试结果(Waterflow)

它不会更快。您看到的图形不仅包括bcmath调用,还包括启动&关闭开销。

$start = microtime(true);
for ($i = 0; $i < 1000; ++$i) {
    bcdiv(40075036, 86164.098903691, 40);
}
echo microtime(true) - $start;

此片段将衡量bcdiv的性能:http://3v4l.org/unrRL

正如你所看到的,性能基本相同。

注意:你可以看到这些数字非常小,这意味着你不能完全信任它们,你应该明白机器上的任何额外负载都会影响测试结果。

只是出于好奇。我决定我不需要bcmath的荒谬准确性,但我确实想把自己从IEEE 754舍入异常中拯救出来,并创建了我自己的非常简单的FixedNumber类:

class FixedNumber {
  private static $radix = [
    0 => 1,
    1 => 10,
    2 => 100,
    3 => 1000,
    4 => 10000,
  ];
  private int $decimal_places;
  public function __construct(
    protected int $value,
    int $decimal_places=2
  ) 
  {
    $this->decimal_places = $decimal_places;
  }
  public function __toString() {
    return sprintf('%.'.$this->decimal_places.'f', $this->value/self::$radix[$this->decimal_places]);
  }
  public function __toFloat() {
    return $this->value/self::$radix[$this->decimal_places];
  }
  public function __add(FixedNumber $other) {
    if ($this->decimal_places != $other->decimal_places) {
      throw new Exception('Cannot add FixedNumber with different decimal places');
    }
    return new FixedNumber($this->value + $other->value, $this->decimal_places);
  }
  public function __sub(FixedNumber $other) {
    if ($this->decimal_places != $other->decimal_places) {
      throw new Exception('Cannot subtract FixedNumber with different decimal places');
    }
    return new FixedNumber($this->value - $other->value, $this->decimal_places);
  }
  public function __mul(FixedNumber $other) {
    if ($this->decimal_places != $other->decimal_places) {
      throw new Exception('Cannot multiply FixedNumber with different decimal places');
    }
    return new FixedNumber((int)round($this->value * $other->value / self::$radix[$this->decimal_places]), $this->decimal_places);
  }
  public function __div(FixedNumber $other) {
    if ($this->decimal_places != $other->decimal_places) {
      throw new Exception('Cannot divide FixedNumber with different decimal places');
    }
    return new FixedNumber((int)round(($this->value / $other->value) * self::$radix[$this->decimal_places]), $this->decimal_places);
  }
}

并尝试了这个小测试设置:

define("SET_SIZE",1000,false);
$start = microtime(true);
$d = 0.0;
for ($i = 0; $i < SET_SIZE; ++$i) {
    $d = 40075036+86164.098903691;
}
printf('%f<br>', microtime(true) - $start);
$start = microtime(true);
for ($i = 0; $i < SET_SIZE; ++$i) {
    bcadd("40075036", "86164.098903691", 16);
}
printf('%f<br>', microtime(true) - $start);
$start = microtime(true);
for ($i = 0; $i < SET_SIZE; ++$i) {
  $a = new 'FixedNumber(40075036);
  $b = new 'FixedNumber(861600);
  $c = $a->__add($b);
}
printf('%f<br>', microtime(true) - $start);

只是为了发现,尽管bcmath比普通数学慢20个数量级,但它仍然比我的简单FixedNumber类高0.11个数量级。1.000.000的集合大小的数字:

0.009088
0.224396
0.366447

似乎创建对象和在用户空间中的开销使bcmath成为一个更好的交易。

如果固定点算术能被包含在货币价值、学校格栅等方面,那就太好了,因为浮点数是错误的。