我读到的所有关于更好的PHP编码实践的文章都告诉我,由于速度的原因,不要使用require_once
。
为什么会这样呢?
要做与require_once
相同的事情,正确/更好的方法是什么?如果重要的话,我使用的是PHP5。
发布于 2008-10-12 01:43:19
我很好奇,查看了Adam Backstrom与Tech Your Universe的链接。本文描述了应该使用require而不是require_once的原因之一。然而,他们的说法并不能支撑我的分析。我有兴趣看看我可能在哪里错误地分析了解决方案。我使用PHP 5.2.0进行比较。
我首先创建了100个头文件,这些文件使用require_once来包含另一个头文件。每个文件看起来都类似于:
<?php
// /home/fbarnes/phpperf/hdr0.php
require_once "../phpperf/common_hdr.php";
?>
我用一个快速的Bash hack创建了这些:
for i in /home/fbarnes/phpperf/hdr{00..99}.php; do
echo "<?php
// $i" > $i
cat helper.php >> $i;
done
这样,当包含头文件时,我可以很容易地在使用require_once和使用require之间进行切换。然后,我创建了一个app.php来加载这一百个文件。这看起来像这样:
<?php
// Load all of the php hdrs that were created previously
for($i=0; $i < 100; $i++)
{
require_once "/home/fbarnes/phpperf/hdr$i.php";
}
// Read the /proc file system to get some simple stats
$pid = getmypid();
$fp = fopen("/proc/$pid/stat", "r");
$line = fread($fp, 2048);
$array = split(" ", $line);
// Write out the statistics; on RedHat 4.5 with kernel 2.6.9
// 14 is user jiffies; 15 is system jiffies
$cntr = 0;
foreach($array as $elem)
{
$cntr++;
echo "stat[$cntr]: $elem\n";
}
fclose($fp);
?>
我将require_once头文件与使用如下头文件的require头文件进行了对比:
<?php
// /home/fbarnes/phpperf/h/hdr0.php
if(!defined('CommonHdr'))
{
require "../phpperf/common_hdr.php";
define('CommonHdr', 1);
}
?>
在使用require和require_once运行时,我没有发现太多差异。事实上,我最初的测试似乎暗示require_once稍微快一点,但我不一定相信这一点。我用10000个输入文件重复了这个实验。在这里,我确实看到了一致的差异。我多次运行测试,结果很接近,但使用require_once平均使用30.8个用户jiffies和72.6个系统jiffies;使用require平均使用39.4个用户jiffies和72.0个系统jiffies。因此,看起来使用require_once的负载稍微低一些。但是,挂钟时间略有增加。10,000个require_once调用平均使用10.15秒完成,10,000个请求调用平均使用9.84秒。
下一步是研究这些差异。我使用strace来分析正在进行的系统调用。
在从require_once打开文件之前,会进行以下系统调用:
time(NULL) = 1223772434
lstat64("/home", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat64("/home/fbarnes", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat64("/home/fbarnes/phpperf", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat64("/home/fbarnes/phpperf/h", {st_mode=S_IFDIR|0755, st_size=270336, ...}) = 0
lstat64("/home/fbarnes/phpperf/h/hdr0.php", {st_mode=S_IFREG|0644, st_size=88, ...}) = 0
time(NULL) = 1223772434
open("/home/fbarnes/phpperf/h/hdr0.php", O_RDONLY) = 3
这与require形成对比:
time(NULL) = 1223772905
lstat64("/home", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat64("/home/fbarnes", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat64("/home/fbarnes/phpperf", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat64("/home/fbarnes/phpperf/h", {st_mode=S_IFDIR|0755, st_size=270336, ...}) = 0
lstat64("/home/fbarnes/phpperf/h/hdr0.php", {st_mode=S_IFREG|0644, st_size=146, ...}) = 0
time(NULL) = 1223772905
open("/home/fbarnes/phpperf/h/hdr0.php", O_RDONLY) = 3
技术你的宇宙意味着require_once应该进行更多的lstat64调用。但是,它们都进行相同数量的lstat64调用。可能的区别是,我没有运行APC来优化上面的代码。然而,接下来我比较了整个运行的strace的输出:
[fbarnes@myhost phpperf]$ wc -l strace_1000r.out strace_1000ro.out
190709 strace_1000r.out
210707 strace_1000ro.out
401416 total
实际上,在使用require_once时,每个头文件大约多了两个系统调用。一个不同之处在于,require_once对time()函数有额外的调用:
[fbarnes@myhost phpperf]$ grep -c time strace_1000r.out strace_1000ro.out
strace_1000r.out:20009
strace_1000ro.out:30008
另一个系统调用是getcwd():
[fbarnes@myhost phpperf]$ grep -c getcwd strace_1000r.out strace_1000ro.out
strace_1000r.out:5
strace_1000ro.out:10004
之所以这样调用,是因为我决定使用hdrXXX文件中引用的相对路径。如果我将其设为绝对引用,那么唯一的区别就是在代码中进行的额外时间(NULL)调用:
[fbarnes@myhost phpperf]$ wc -l strace_1000r.out strace_1000ro.out
190705 strace_1000r.out
200705 strace_1000ro.out
391410 total
[fbarnes@myhost phpperf]$ grep -c time strace_1000r.out strace_1000ro.out
strace_1000r.out:20008
strace_1000ro.out:30008
这似乎意味着您可以通过使用绝对路径而不是相对路径来减少系统调用的数量。除此之外,唯一的区别是时间(NULL)调用,它似乎用于检测代码,以比较哪个更快。
另一个注意事项是,APC优化包有一个名为"apc.include_once_override“的选项,该选项声称它减少了由require_once和include_once调用进行的系统调用的数量(参见PHP documentation)。
发布于 2008-10-09 08:05:28
更好的做法是使用面向对象的方法并使用__autoload()。
发布于 2008-10-09 12:35:02
*_once()
函数stat每个父目录,以确保您要包含的文件与已经包含的文件不同。这是增长放缓的部分原因。
我推荐使用像Siege这样的工具进行基准测试。您可以尝试所有建议的方法,并比较响应时间。
有关require_once()
的更多信息,请访问Tech Your Universe。
https://stackoverflow.com/questions/186338
复制相似问题