为什么file_put_contents在这个基准测试中表现不佳?

时间:2013-10-24 11:42:27

标签: php text-files benchmarking

我为部分代码创建了简单的基准测试,我担心这些代码无法正常工作。我想出了非常奇怪的结果。看看这个基准:
benchmark
test file

基准代码是:

$start = microtime(true)*1000;

//code
$log=file_get_contents('test.txt').'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'."\n";
file_put_contents('test.txt', $log, LOCK_EX);

$end=microtime(true)*1000;
$time = $end-$start;
echo 'Time : '.(int)$time.'ms<br />';


$start = microtime(true)*1000;

//code
$log='aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'."\n";
file_put_contents('test.txt', $log, LOCK_EX|FILE_APPEND);

$end=microtime(true)*1000;
$time = $end-$start;
echo 'Time : '.(int)$time.'ms<br />';

我注意到的是,我认为应该更快的追加选项实际上更慢。如果问题出在我的基准上那么请告诉我 谁能解释一下为什么它可能会变慢?

另外我发现当你按住F5(连续刷新)时,文本文件会被清除(:O)为什么?

修改 Ilya Bursov说,我已经改变了基准,现在在100次迭代中完成。追加操作现在似乎花费了可以忽略的时间来完成,而读取和写入将永远保持原样。但是,即使清除缓存,单次迭代仍会产生奇怪的结果。我知道这可能会受到许多事情的影响甚至误差,但是我会很感激你的回答。

2 个答案:

答案 0 :(得分:4)

Php fopen使用open调用O_TRUNC系统调用,这意味着它在以非追加模式写入之前截断文件。这种截断需要时间,并使其变慢。

截断发生后,在真实内容到达之前,您会看到空文件。

您可以在命令行中使用strace验证这一点。

答案 1 :(得分:4)

实际上你在错误的环境中使用错误的方法测量错误的项目

  1. 第一个结果是衡量大数据的读写+
  2. 第二个结果是测量变量init +追加小数据
  3. 您只进行了1次迭代,这在多线程环境中提供了非常不稳定的结果
  4. 你是通过apache调用脚本,做基准测试最好从命令行调用它
  5. 磁盘/系统缓存缓冲区的不同设置可以真正改变结果
  6. 以下是我的猜测为什么时间如此不同:

    由于您只有一次迭代的过程,您可以拥有下一个情况:

    1. 读取文件(它在缓存中) - 我们可以在这里考虑0次
    2. 使用const的concat文件 - 几乎是0次
    3. 将数据写入输出缓冲区 - 不是写入磁盘,而是写入内存缓冲区,稍后将刷新 - 几乎为0次
    4. 现在你正在重新启动变量 - 我希望0时间,或者它可以更大,因为你重用变量它可能是php重新安排内存中的东西
    5. 你正在写文件,但是你必须等到p3的缓冲区实际刷新
    6. 您可以尝试稍微更改一下代码:

      $start = microtime(true)*1000;
      $log=file_get_contents('test.txt').'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'."\n";
      file_put_contents('test.txt', $log, LOCK_EX);
      clearstatcache(); // NOTE call here
      $end=microtime(true)*1000;
      $time = $end-$start;
      echo 'Time : '.(int)$time.'ms<br />';
      $start = microtime(true)*1000;
      $log='aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'."\n";
      file_put_contents('test.txt', $log, LOCK_EX|FILE_APPEND);
      clearstatcache(); // NOTE another call here
      $end=microtime(true)*1000;
      $time = $end-$start;
      echo 'Time : '.(int)$time.'ms<br />';
      

      现在你得到了不同的结果

      <强>因此

      测试中的时间1:从缓存中读取和写入

      时间2是实际写入磁盘,可能是内存操作的一些开销

      请考虑以下代码:

      $appendString = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
      $log=file_get_contents('test.txt').$appendString."\n";
      $iterations = 1000;
      
      $start = microtime(true);
      for ($i=0; $i<$iterations; $i++)
          file_put_contents('test.txt', $log, LOCK_EX);
      $end=microtime(true);
      
      $time = ($end*1000 - $start*1000) / $iterations;
      echo 'Time : '.$time.'ms'. "\n";
      
      $log=$appendString."\n";
      
      $start = microtime(true);
      for ($i=0; $i<$iterations; $i++)
          file_put_contents('test.txt', $log, LOCK_EX|FILE_APPEND);
      $end=microtime(true);
      
      $time = ($end*1000 - $start * 1000) / $iterations;
      echo 'Time : '.$time.'ms' . "\n";
      
      在Windows上

      Time : 0.22101293945313ms
      Time : 0.039001953125ms
      
      PHP 5.5.3 (cli) (built: Aug 20 2013 16:45:40)
      Copyright (c) 1997-2013 The PHP Group
      Zend Engine v2.5.0, Copyright (c) 1998-2013 Zend Technologies
          with Zend OPcache v7.0.3-dev, Copyright (c) 1999-2013, by Zend Technologies
      

      在linux上:

      Time : 7.6823303222656ms
      Time : 0.008222900390625ms
      
      PHP 5.4.4-14+deb7u4 (cli) (built: Aug 23 2013 14:37:41)
      Copyright (c) 1997-2012 The PHP Group
      Zend Engine v2.4.0, Copyright (c) 1998-2012 Zend Technologies
      

      这些数字看起来合理,即使有2个不同的系统,因为在第一个循环中我们必须写大字符串而在第二个循环中只有一小部分附加到它上面,我们使用迭代来计算平均结果

      结论:正如预期的那样 - 追加文件比重写整个文件更快,至少因为需要的磁盘IO数量少得多