这篇文章是献给 Leigh Purdie(5 年前)和 Dade Brandon(4 个月前)的。
所以我说 Leigh 发布了帖子,然后我心想天哪,我需要把所有 fgets 都改成 stream_get_line。然后我按照 Leigh Purdie 评论中的说明运行了测试,他的结果是:
$ time yes "This is a test line" | head -1000000 | php -r '$fp=fopen("php://stdin","r"); while($line=stream_get_line($fp,65535,"\n")) { 1; } fclose($fp);'
real 0m1.482s
user 0m1.616s
sys 0m0.152s
$ time yes "This is a test line" | head -1000000 | php -r '$fp=fopen("php://stdin","r"); while($line=fgets($fp,65535)) { 1; } fclose($fp);'
real 0m7.281s
user 0m7.392s
sys 0m0.136s
我的结果是:
$ time yes "This is a test line" | head -1000000 | php -r '$fp=fopen("php://stdin","r"); while($line=stream_get_line($fp,65535,"\n")) { 1; } fclose($fp);'
real 0m0.341s
user 0m0.352s
sys 0m0.148s
$ time yes "This is a test line" | head -1000000 | php -r '$fp=fopen("php://stdin","r"); while($line=fgets($fp,65535)) { 1; } fclose($fp);'
real 0m4.283s
user 0m4.128s
sys 0m0.448s
我的结果与他的结果显示了相同的问题。但首先,PHP 至少比第一次运行测试时快了 2 到 5 倍(或者硬件更好了)。
现在让我们来谈谈 Dade Brandon,他指出如果你使用正确的缓冲区大小,性能会相当接近。
$ time yes "This is a test line" | head -1000000 | php -r '$fp=fopen("php://stdin","r"); while($line=stream_get_line($fp,21,"\n")) { 1; } fclose($fp);'
real 0m0.336s
user 0m0.412s
sys 0m0.076s
$ time yes "This is a test line" | head -1000000 | php -r '$fp=fopen("php://stdin","r"); while($line=fgets($fp,21)) { 1; } fclose($fp);'
real 0m0.312s
user 0m0.364s
sys 0m0.192s
如你所见,它们非常接近,fgets 稍微领先了一点点。我怀疑 fgets 是反向读取缓冲区,或者将所有内容加载到自身,然后尝试找出答案,而正确设置的缓冲区则可以解决问题。Dade Brandon 指出 fgets 会让你知道行是如何分隔的。stream_get_line 允许你使用它的第三个参数选择你想要使用的分隔符。
fgets 还有一个重要的选项,你无需设置行长度。因此,在你不确定行长度的情况下,例如在处理 HTTP 协议或日志行等其他内容时,你可以简单地省略它,仍然可以获得出色的性能。
$ time yes "This is a test line" | head -1000000 | php -r '$fp=fopen("php://stdin","r"); while($line=fgets($fp)) { 1; } fclose($fp);'
real 0m0.261s
user 0m0.260s
sys 0m0.232s
这比设置缓冲区要好。