草庐IT

PHP 闭包给出了奇怪的性能行为

coder 2024-04-09 原文

今天早些时候,我正在开发 PHP 5.3+ 应用程序,这意味着我可以自由使用 PHP 闭包。太好了,我想!然后我遇到了一段代码,其中使用函数式 PHP 代码会使事情变得容易得多,但是,虽然我心中有一个合乎逻辑的答案,但它让我想知道直接调用 中的闭包对性能有何影响array_map() 并将其作为变量向下传递。 IE。以下两个:

$test_array = array('test', 'test', 'test', 'test', 'test' );
array_map( function ( $item ) { return $item; }, $test_array );

$test_array = array('test', 'test', 'test', 'test', 'test' );
$fn = function ( $item ) { return $item; };
array_map( $fn, $test_array );

和我想的一样,后者确实更快,但差别不大。事实上,重复这些相同的测试 10000 次并取平均值的差异是 0.05 秒。甚至可能是侥幸。

这让我更加好奇了。 create_function() 和闭包呢?同样,经验告诉我 create_function() 应该比 array_map() 更慢,因为它创建一个函数,计算它,然后存储它。 而且,正如我所想的那样,create_function() 确实更慢。这一切都是通过 array_map() 实现的。

然后,我不确定我为什么这样做,但我确实检查了 create_function() 和闭包之间的区别,同时保存它并只调用它一次。没有任何处理,什么都没有,只是简单地传递一个字符串,然后返回该字符串。

测试变成了:

$fn = function($item) { return $item; };
$fn('test');

$fn = create_function( '$item', 'return $item;' );
$fn('test');

我分别运行了这两个测试 10000 次,然后查看结果并得到平均值。我对结果感到非常惊讶。

事实证明,这次关闭速度慢了大约 4 倍。这不可能是我想的。我的意思是,通过 array_map() 运行闭包要快得多,而通过 array_map() 通过变量运行相同的函数甚至更快,这实际上与这个测试。

结果是

array
  0 => 
    array
      'test' => string 'Closure test' (length=12)
      'iterations' => int 10000
      'time' => float 5.1327705383301E-6
  1 => 
    array
      'test' => string 'Anonymous test' (length=14)
      'iterations' => int 10000
      'time' => float 1.6745710372925E-5

我很好奇它为什么这样做,我检查了 CPU 使用率和其他系统资源,并确保没有任何不必要的运行,现在一切正常,所以我再次运行测试,但我得到了类似的结果。

所以我只尝试了一次相同的测试,然后运行了多次(当然每次都计时)。事实证明闭包确实慢了 4 倍,除了偶尔它会比 create_function() 快两到三倍,我猜这只是侥幸,但它似乎足以将我进行 1000 次测试时的时间缩短一半。

下面是我用来进行这些测试的代码。谁能告诉我这到底是怎么回事?是我的代码还是只是 PHP 在起作用?

<?php

/**
 * Simple class to benchmark code
 */
class Benchmark
{
    /**
     * This will contain the results of the benchmarks.
     * There is no distinction between averages and just one runs
     */
    private $_results = array();

    /**
     * Disable PHP's time limit and PHP's memory limit!
     * These benchmarks may take some resources
     */
    public function __construct() {
        set_time_limit( 0 );
        ini_set('memory_limit', '1024M');
    }

    /**
     * The function that times a piece of code
     * @param string $name Name of the test. Must not have been used before
     * @param callable|closure $callback A callback for the code to run.
     * @param boolean|integer $multiple optional How many times should the code be run,
     * if false, only once, else run it $multiple times, and store the average as the benchmark
     * @return Benchmark $this
     */
    public function time( $name, $callback, $multiple = false )
    {
        if($multiple === false) {
            // run and time the test
            $start = microtime( true );
            $callback();
            $end = microtime( true );

            // add the results to the results array
            $this->_results[] = array(
                'test' => $name,
                'iterations' => 1,
                'time' => $end - $start
            );
        } else {
            // set a default if $multiple is set to true
            if($multiple === true) {
                $multiple = 10000;
            }

            // run the test $multiple times and time it every time
            $total_time = 0;
            for($i=1;$i<=$multiple;$i++) {
                $start = microtime( true );
                $callback();
                $end = microtime( true );
                $total_time += $end - $start;
            }
            // calculate the average and add it to the results
            $this->_results[] = array(
                'test' => $name,
                'iterations' => $multiple,
                'time' => $total_time/$multiple
            );
        }
        return $this; //chainability
    }

    /**
     * Returns all the results
     * @return array $results
     */
    public function get_results()
    {
        return $this->_results;
    }
}

$benchmark = new Benchmark();

$benchmark->time( 'Closure test', function () {
    $fn = function($item) { return $item; };
    $fn('test');
}, true);

$benchmark->time( 'Anonymous test', function () {
    $fn = create_function( '$item', 'return $item;' );
    $fn('test');
}, true);

$benchmark->time( 'Closure direct', function () {
    $test_array = array('test', 'test', 'test', 'test', 'test' );
    $test_array = array_map( function ( $item ) { return $item; }, $test_array );
}, true);

$benchmark->time( 'Closure stored', function () {
    $test_array = array('test', 'test', 'test', 'test', 'test' );
    $fn = function ( $item ) { return $item; };
    $test_array = array_map( $fn, $test_array );
}, true);

$benchmark->time( 'Anonymous direct', function () {
    $test_array = array('test', 'test', 'test', 'test', 'test' );
    $test_array = array_map( create_function( '$item', 'return $item;' ), $test_array );
}, true);

$benchmark->time( 'Anonymous stored', function () {
    $test_array = array('test', 'test', 'test', 'test', 'test' );
    $fn = create_function( '$item', 'return $item;' );
    $test_array = array_map( $fn, $test_array );
}, true);

var_dump($benchmark->get_results());

这段代码的结果:

array
  0 => 
    array
      'test' => string 'Closure test' (length=12)
      'iterations' => int 10000
      'time' => float 5.4110765457153E-6
  1 => 
    array
      'test' => string 'Anonymous test' (length=14)
      'iterations' => int 10000
      'time' => float 1.6784238815308E-5
  2 => 
    array
      'test' => string 'Closure direct' (length=14)
      'iterations' => int 10000
      'time' => float 1.5178990364075E-5
  3 => 
    array
      'test' => string 'Closure stored' (length=14)
      'iterations' => int 10000
      'time' => float 1.5463256835938E-5
  4 => 
    array
      'test' => string 'Anonymous direct' (length=16)
      'iterations' => int 10000
      'time' => float 2.7537250518799E-5
  5 => 
    array
      'test' => string 'Anonymous stored' (length=16)
      'iterations' => int 10000
      'time' => float 2.8293371200562E-5

最佳答案

5.1327705383301E-6 不比 1.6745710372925E-5 慢 4 倍;它快了大约 3 倍。你读错了数字。似乎在您的所有结果中,闭包始终比 create_function 快。

关于PHP 闭包给出了奇怪的性能行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11278748/

有关PHP 闭包给出了奇怪的性能行为的更多相关文章

  1. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  2. ruby - Ruby gsub 替换中的行为不一致? - 2

    两个gsub产生不同的结果。谁能解释一下为什么?代码也可在https://gist.github.com/franklsf95/6c0f8938f28706b5644d获得.ver=9999str="\tCFBundleDevelopmentRegion\n\ten\n\tCFBundleVersion\n\t0.1.190\n\tAppID\n\t000000000000000"putsstr.gsub/(CFBundleVersion\n\t.*\.).*()/,"#{$1}#{ver}#{$2}"puts'--------'putsstr.gsub/(CFBundleVersio

  3. ruby-on-rails - Ruby 中意外的大小写行为 - 2

    我在一段非常简单的代码(如我所想)中得到了一个错误的值:org=4caseorgwhenorg=4val='H'endputsval=>nil请不要生气,我希望我错过了一些非常明显的东西,但我真的想不通。谢谢。 最佳答案 这是典型的Ruby错误。case有两种被调用的方法,一种是你传递一个东西作为分支的基础,另一种是你不传递的东西。如果您确实在case中指定了一个表达式语句然后评估所有其他条件并与===进行比较.在这种情况下org评估为false和org===false显然不是真的。所有其他情况也是如此,它们要么是真的,要么是假的。

  4. ruby - 使对象的行为类似于 ruby​​ 中并行分配的数组 - 2

    假设您在Ruby中执行此操作:ar=[1,2]x,y=ar然后,x==1和y==2。是否有一种方法可以在我自己的类中定义,从而产生相同的效果?例如rb=AllYourCode.newx,y=rb到目前为止,对于这样的赋值,我所能做的就是使x==rb和y=nil。Python有这样一个特性:>>>classFoo:...def__iter__(self):...returniter([1,2])...>>>x,y=Foo()>>>x1>>>y2 最佳答案 是的。定义#to_ary。这将使您的对象被视为要分配的数组。irb>o=Obje

  5. ruby - Ruby 中的闭包和 for 循环 - 2

    我是Ruby的新手,有些闭包逻辑让我感到困惑。考虑这段代码:array=[]foriin(1..5)array[5,5,5,5,5]这对我来说很有意义,因为i被绑定(bind)在循环之外,所以每次循环都会捕获相同的变量。使用每个block可以解决这个问题对我来说也很有意义:array=[](1..5).each{|i|array[1,2,3,4,5]...因为现在每次通过时都单独声明i。但现在我迷路了:为什么我不能通过引入一个中间变量来修复它?array=[]foriin1..5j=iarray[5,5,5,5,5]因为j每次循环都是新的,我认为每次循环都会捕获不同的变量。例如,这绝对

  6. Ruby 的数字方法性能 - 2

    我正在使用Ruby解决一些ProjectEuler问题,特别是这里我要讨论的问题25(Fibonacci数列中包含1000位数字的第一项的索引是多少?)。起初,我使用的是Ruby2.2.3,我将问题编码为:number=3a=1b=2whileb.to_s.length但后来我发现2.4.2版本有一个名为digits的方法,这正是我需要的。我转换为代码:whileb.digits.length当我比较这两种方法时,digits慢得多。时间./025/problem025.rb0.13s用户0.02s系统80%cpu0.190总计./025/problem025.rb2.19s用户0.0

  7. ruby - 了解在 Ruby 中与 lambda 一起使用的 inject 行为 - 2

    我经常将预配置的lambda插入可枚举的方法中,例如“map”、“select”等。但是“注入(inject)”的行为似乎有所不同。例如与mult4=lambda{|item|item*4}然后(5..10).map&mult4给我[20,24,28,32,36,40]但是,如果我制作一个2参数lambda用于像这样的注入(inject),multL=lambda{|product,n|product*n}我想说(5..10).inject(2)&multL因为“inject”有一个可选的单个初始值参数,但这给了我......irb(main):027:0>(5..10).inject

  8. ruby - Ruby 性能中的计时器 - 2

    我正在寻找一个用ruby​​演示计时器的在线示例,并发现了下面的代码。它按预期工作,但这个简单的程序使用30Mo内存(如Windows任务管理器中所示)和太多CPU有意义吗?非常感谢deftime_blockstart_time=Time.nowThread.new{yield}Time.now-start_timeenddefrepeat_every(seconds)whiletruedotime_spent=time_block{yield}#Tohandle-vesleepinteravalsleep(seconds-time_spent)iftime_spent

  9. ruby-on-rails - 浮点乘法的 Ruby 奇怪问题 - 2

    有没有人用ruby​​解决这个问题:假设我们有:a=8.1999999我们想将它四舍五入为2位小数,即8.20,然后乘以1,000,000得到8,200,000我们是这样做的;(a.round(2)*1000000).to_i但是我们得到的是8199999,为什么?奇怪的是,如果我们乘以1000、100000或10000000而不是1000000,我们会得到正确的结果。有人知道为什么吗?我们正在使用ruby​​1.9.2并尝试使用1.9.3。谢谢! 最佳答案 每当你在计算中得到时髦的数字时使用bigdecimalrequire'bi

  10. ruby-on-rails - 如果条件与 &&,是否有任何性能提升 - 2

    如果用户是所有者,我有一个条件来检查说删除和文章。delete_articleifuser.owner?另一种方式是user.owner?&&delete_article选择它有什么好处还是它只是一种写作风格 最佳答案 性能不太可能成为该声明的问题。第一个要好得多-它更容易阅读。您future的自己和其他将开始编写代码的人会为此感谢您。 关于ruby-on-rails-如果条件与&&,是否有任何性能提升,我们在StackOverflow上找到一个类似的问题:

随机推荐