2008-10-09 112 views
131

我读到的关于更好的PHP编码实践的一切都会说因为速度不要使用require_once为什么require_once不好用?

这是为什么?

require_once做同样的事情的正确/更好的方法是什么?如果它很重要,我使用PHP5。

+1

ZF正在使用它BTW :) – 2012-11-15 13:39:45

+5

这个问题现在很老了,答案是可疑的相关了。很高兴看到来自参与者的最新答案集:) – Purefan 2015-02-27 09:57:26

回答

99

require_onceinclude_once都要求系统记录已经包含/需要的内容。每个*_once调用意味着检查该日志。所以肯定有一些在那里做了额外的工作,但足以损害整个应用程序的速度?

......我真的怀疑它......除非你真的在真的旧硬件或做它很多

如果你做成千上万的*_once,你可以自己做轻松的工作。对于简单的应用程序,只是确保你只有一次包括它应该就够了,但如果你仍然得到重新定义错误,你可以像这样:

if (!defined('MyIncludeName')) { 
    require('MyIncludeName'); 
    define('MyIncludeName', 1); 
} 

我会亲自坚持与*_once声明但傻亿通基准,可以看到两者之间的区别:用慢require_once

   php     hhvm 
if defined  0.18587779998779  0.046600103378296 
require_once 1.2219581604004  3.2908599376678 

10-100×和它的奇怪的是,require_oncehhvm看似慢。同样,如果您运行的是数千次,则这只与您的代码相关。


<?php // test.php 

$LIMIT = 1000000; 

$start = microtime(true); 

for ($i=0; $i<$LIMIT; $i++) 
    if (!defined('include.php')) { 
     require('include.php'); 
     define('include.php', 1); 
    } 

$mid = microtime(true); 

for ($i=0; $i<$LIMIT; $i++) 
    require_once('include.php'); 

$end = microtime(true); 

printf("if defined\t%s\nrequire_once\t%s\n", $mid-$start, $end-$mid); 

<?php // include.php 

// do nothing. 
+26

我怀疑你的defined()方法比内置的查找表更快,但我同意你的总体观点 - 肯定不是-问题?! – 2008-10-09 08:42:18

+1

我很确定你是对的Bobby,但我不主张通过_once定义。这只是一个选项。解释代码所花费的时间甚至可能会使其稍微慢一些,但是,那就是说,我不知道内部方法有多彻底。它可能会做额外的工作以确保没有重复。 – Oli 2008-10-09 09:37:38

6

更好的办法是使用面向对象的方法并使用__autoload()

+3

但您链接到的自动加载对象页面的第一个示例使用require_once – Shabbyrobe 2008-10-09 08:13:24

+0

我认为require也可以在那里工作(虽然没有经过测试) – Greg 2008-10-09 08:24:25

+0

我没有买这个。在许多情况下,面向对象并没有像其他范式那样合适,所以你不应该强迫它仅仅为了获得可能与__autoload()有关的微小优势。 – 2008-10-09 08:40:41

20

你能不能给我们这些编码的作业,说以避免任何链接?就我而言,这是一个完整的非问题。我自己并没有看过源代码,但我想象一下includeinclude_once之间的唯一区别是include_once将该文件名添加到数组并且每次都检查数组。将这个数组保存起来很容易,所以对它进行搜索应该是O(log n),即使是一个中等大小的应用程序也只会包含几十个。

-2

我认为在PEAR文档中,有一个require,require_once,include和include_once的建议。我确实遵循这一指导原则。你的申请会更清楚。

0

您测试,使用包括,oli的替代和__autoload();并用安装的something like APC进行测试。

我怀疑使用常量会加快速度。

-4

我个人认为require_once(或者include_once)的用法是不好的做法,因为require_once会检查你是否已经包含该文件,并且抑制导致致命错误的双重包含文件的错误(例如重复声明函数/类/等等。)。

你应该知道你是否需要包含一个文件。

5

*_once()函数统计每个父目录,以确保您包含的文件与已包含的文件不同。这是经济放缓的部分原因。

我建议使用Siege这样的工具进行基准测试。您可以尝试所有建议的方法并比较响应时间。

更多关于require_once()Tech Your Universe

0

是的,它比plain ol'require()要稍贵一些。我认为问题的关键在于,如果您可以保持足够的代码以避免重复包含,请不要使用* _once()函数,因为它会为您节省一些周期。

但是使用_once()函数不会杀死你的应用程序。基本上,只是不要以此为借口不必组织您的包含。在某些情况下,使用它仍然是不可避免的,这不是什么大问题。

5

PEAR2维基(当它存在时)用于列出good reasons for abandoning all the require/include directives in favor of autoload ing,至少对于库代码。当诸如phar等替代包装模型即将出现时,这些将您与严格的目录结构联系在一起。

更新:为了使用(PEAR)包需要

  • include_path中:由于维基网页存档版本是眼gougingly丑,我已经复制下面的最令人信服的理由。这使得将另一个应用程序中的PEAR程序包与其自身的include_path捆绑在一起以创建包含所需类的单个文件难以将PEAR程序包移动到phar归档文件,而无需扩展源代码 修改。
  • 当顶层require_once与条件require_once混合,这会导致在代码是由操作码缓存如 APC,将与PHP 6.捆绑不可缓存
  • 相对require_once要求的include_path已经被设置为正确的值,因此无法使用包而不 适当的include_path
63

我得到了好奇,并检查了亚当·斯特罗姆的链接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黑客:

for i in /home/fbarnes/phpperf/hdr{00..99}.php; do 
    echo "<?php 
// $i" > $i 
    cat helper.php >> $i; 
done 

这样我可以很容易地使用require_once和包括头文件时,需要之间进行切换。然后我创建了一个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 w/ 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_once

<?php 
// /home/fbarnes/phpperf/h/hdr0.php 
if(!defined('CommonHdr')) 
{ 
    require "../phpperf/common_hdr.php"; 
    define('CommonHdr', 1); 
} 

?> 

我没有发现太大的区别。实际上,我最初的测试似乎暗示require_once稍快,但我不一定相信。我重复了10000个输入文件的实验。在这里我看到了一致的差异。我多次运行测试,结果很接近,但使用require_once平均使用30.8个用户jiffies和72.6个系统jiffies;使用需求平均使用39.4个用户jiffies和72.0个系统jiffies。因此,使用require_once看起来负载略低。但是,挂钟略有增加。 10,000个req​​uire_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 

这与要求:

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的输出为整个运行:

[[email protected] phpperf]$ wc -l strace_1000r.out strace_1000ro.out 
    190709 strace_1000r.out 
    210707 strace_1000ro.out 
    401416 total 

有效有每头文件使用require_once时约两个系统调用。一个区别是,require_once有时间()函数的额外调用:

[[email protected] phpperf]$ grep -c time strace_1000r.out strace_1000ro.out 
strace_1000r.out:20009 
strace_1000ro.out:30008 

其他系统调用GETCWD():

[[email protected] phpperf]$ grep -c getcwd strace_1000r.out strace_1000ro.out 
strace_1000r.out:5 
strace_1000ro.out:10004 

这就是所谓的,因为我决定中引用的相对路径hdrXXX文件。如果我有这样的一个绝对的参考,那么唯一的区别是额外的时间在代码所做的(NULL)电话:

[[email protected] phpperf]$ wc -l strace_1000r.out strace_1000ro.out 
    190705 strace_1000r.out 
    200705 strace_1000ro.out 
    391410 total 
[[email protected] 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 docs)。

很抱歉的长期职位。我很好奇。

143

这个线程令我生厌,因为还有的已经是一个“解决方案贴”,这是对所有意图和目的,错误让我们一一列举:。

  1. 定义是在PHP真的昂贵您可以look it up或测试它自己,但唯一定义球体的有效方式l在PHP中不变是通过扩展。 (类常量其实是相当不错的性能明智的,但这是一个有争议的问题,因为2)

  2. 如果您正确使用require_once(),即包含类,您甚至不需要定义;只需检查是否class_exists('Classname')。如果你所包含的文件包含代码,即你正在以程序的方式使用它,那么绝对没有理由认为require_once()应该是你需要的;每次包含文件时,您都会假定正在进行子程序调用。等了一会儿

,很多人没有使用class_exists()方法为他们的夹杂物。因为它的fugly我不喜欢它,但他们有很好的理由:require_once()是之前的一些较新版本的PHP中的非常低效。但是,这是固定的,这是我的观点,你就会有额外的字节码编译为有条件的,额外的方法调用,将被胜过任何内部哈希表检查。

现在承认:这东西是很难测试,因为它占了这么少的执行时间。

这里是你应该考虑的问题:包括,作为一般规则,在PHP昂贵,因为每次解释打一个它切换回解析模式,产生的操作码,然后跳回。如果你有100 +包括,这肯定会有性能影响。为什么使用或不使用require_once是一个非常重要的问题,因为它使操作码缓存变得很困难。一个explanation for this可以在这里找到,但是这是什么归结为是:

  • 如果在分析时,你确切地知道包括文件,你需要对请求的整个生命,require()那些在最开始和操作码缓存将为您处理所有其他事情。

  • 如果你没有运行一个操作码缓存,你是在一个艰苦的地方。内联所有的包括到一个文件中(在开发过程中不这样做,不仅是生产)肯定可以帮助分析时间,但它是一个痛苦的事,而且,你需要知道你会被包括在了什么请求。

  • 自动加载是很方便,但速度慢,对于自动加载逻辑,必须每次运行一个包含完成的原因。实际上,我发现为一个请求自动加载多个专用文件不会导致太多问题,但是您不应该自动加载所有您需要的文件。

  • 如果有可能10包括(这是粗略计算的非常回),这一切手淫是不值得的:只是优化您的数据库查询或东西。

-3

它与速度无关。这是关于失败的优雅。

如果require_once()失败,你的脚本完成。没有其他处理。如果你使用include_once()的脚本的其余部分将尝试继续呈现,让您的用户可能会没有最明智的东西,失败在你的脚本。

2

即使require_onceinclude_oncerequireinclude(或任何可能存在的替代品)慢,我们在这里讨论的微优化的最小水平。在优化写得不好的循环或数据库查询时,你的时间要比考虑require_once之类的东西好得多。

现在,一个可以作出说法说require_once允许糟糕的编码实践,因为你并不需要注意保持你的包括清洁和有组织的,但无关与功能本身,特别是不是它的速度。

显然,为了代码清洁和易于维护,自动加载更好,但我想说清楚,这与速度无关。

4

它不使用的是坏的作用。这是一个不正确的理解如何以及何时使用它,在一个整体的代码库。我就添加一些背景到可能被误解的概念:

人们不应该认为require_once是一个缓慢的功能。你必须以这种或那种方式包含你的代码。 require_once()require()的速度不是问题。这是关于性能障碍,可能会导致盲目使用它。如果广泛使用而不考虑上下文,则会导致巨大的内存浪费或代码浪费。

我所看到的这是非常糟糕的,是当巨大的整体框架中的所有错误的方式使用require_once(),尤其是在复杂的面向对象的环境。

以使用require_once()的例子在每个类的顶部,看到许多图书馆:

require_once("includes/usergroups.php"); 
require_once("includes/permissions.php"); 
require_once("includes/revisions.php"); 
class User{ 
    //user functions 
} 

所以User类被设计成使用所有3个其他类。很公平! 但现在如果访问者在浏览哪些网站,并和框架负荷甚至没有登录:require_once("includes/user.php");为每一个请求。

它包括1 + 3 不必要的在特定请求期间不会使用的类。这是多么臃肿的框架最终使用每个请求40MB,而不是5MB或更少。


其他可被误用的方式是当一个类被其他许多人重复使用! 假设您有大约50个使用helper函数的类。为了确保helpers是为那些类在加载时,您将获得:

require_once("includes/helpers.php"); 
class MyClass{ 
    //Helper::functions();//etc.. 
} 

没有什么错在这里本身。但是,如果一个页面请求碰巧包含15个类似的类。您正在运行require_once 15倍,或为一个不错的视觉:

require_once("includes/helpers.php"); 
require_once("includes/helpers.php"); 
require_once("includes/helpers.php"); 
require_once("includes/helpers.php"); 
require_once("includes/helpers.php"); 
require_once("includes/helpers.php"); 
require_once("includes/helpers.php"); 
require_once("includes/helpers.php"); 
require_once("includes/helpers.php"); 
require_once("includes/helpers.php"); 
require_once("includes/helpers.php"); 
require_once("includes/helpers.php"); 
require_once("includes/helpers.php"); 
require_once("includes/helpers.php"); 
require_once("includes/helpers.php"); 

使用require_once()的技术上会影响运行功能的14倍,在具有解析那些不必要的行的顶级性能。只有10个其他类似问题的高度使用的类,它可能会占据100多行此类无意义的重复代码。

就这样,它可能是值得使用require("includes/helpers.php");在您的应用程序或框架的引导,来代替。但是因为一切都是相对的,所以这一切都取决于如果helpers类的重量与使用频率值得保存15-100行的require_once()。但是,如果在任何给定的请求中没有使用helpers文件的概率是none,那么require肯定应该在您的主类中。每个班级有require_once分别成为资源的浪费。


require_once功能是必要的时候非常有用,但它不应该被视为一个整体解决方案,在任何地方使用加载的所有类。