author: selfimpr
blog: http://blog.csdn.net/lgg201
mail: [email protected]
本文博客地址: http://blog.csdn.net/lgg201/archive/2011/02/24/6204951.aspx四种语法的差异
在PHP中,包含一个文件有4种方式:require,require_once,include,include_once.其中require_once/include_once与require/include相比,在功能上,仅仅是增加了一个是否已经加载过的检测,require_once/include_once在一次PHP执行过程中,保证一个文件只被加载一次.
require_once/include_once这样只加载一次的功能,通常是为了避免发生函数/类重定义等异常,或者多次包含同一文件导致的变量覆写.
在说明了_once版本和没有_once的版本之间的区别后,我们同样需要知道require和include之间的区别.下面是php手册对require的解释:
require() is identical to include() except upon failure it will also produce a fatal E_ERROR level error. In other words, it will halt the script whereas include() only emits a warning (E_WARNING) which allows the script to continue.
可以看出,require和include之间的区别仅在于发生错误时(比如被包含文件查找不到),require引发一个E_ERROR级别的错误,而include引发一个E_WARNING级别的错误.(E_ERROR级别的错误会中断脚本执行)
require_once和include_once之间的区别也就不言而喻了.一个需要注意的点
require/require_once/include/include_once都是语法结构,不是函数,可以通过function_exists验证性能问题
require/require_once的性能问题,在http://blog.csdn.net/lgg201/archive/2011/02/14/6184745.aspx中已经做了比较详细的阐述,这里仅仅列举一个用以发现性能问题的示例:
main.php
<?php
/**
 * author: selfimpr
 * blog: http://blog.csdn.net/lgg201
 * mail: [email protected]
 * require/require_once性能测试
 *//**
 * 测试require
 * @param unknown_type $filename
 */
function test_require($filename) {
        isset($GLOBALS[$filename]) or (($GLOBALS[$filename] = 1) and require $filename);    
}
/**
 * 测试require_once
 * @param unknown_type $filename
 */
function test_require_once($filename) {
        require_once($filename);
}
/**
 * cpu时间缓存
 */
$cpu_time_tmp = array();
/**
 * 记录开始cpu时间
 */
function cputime_start() {
        global $cpu_time_tmp;
        $rusage = getrusage();
        $cpu_time_tmp[] = $rusage['ru_utime.tv_sec'] + $rusage['ru_utime.tv_usec'] / 1000000;
        $cpu_time_tmp[] = $rusage['ru_stime.tv_sec'] + $rusage['ru_stime.tv_usec'] / 1000000;
}
/**
 * 输出运行cpu消耗
 */
function cputime_end() {
        global $cpu_time_tmp;
        $rusage = getrusage();
        printf("user_cpu: %.8f, system_cpu: %.8f<br />\n", $rusage['ru_utime.tv_sec'] + $rusage['ru_utime.tv_usec'] / 1000000 - $cpu_time_tmp[0], $rusage['ru_stime.tv_sec'] + $rusage['ru_stime.tv_usec'] / 1000000 - $cpu_time_tmp[1]);
        $cpu_time_tmp = array();
}$times = 1000000;
print "<h1>测试用例运行次数: $times</h1>";print "<b><font color='blue'>require方式消耗: </font></b>";
cputime_start();
while($i ++ < $times) test_require("required_require.php");
cputime_end();print "<b><font color='blue'>require_once方式消耗: </font></b>";
cputime_start();
while($j ++ < $times) test_require_once("required_requireonce.php");
cputime_end();
?>
<meta http-equiv="Content-Type: text/html; charset=utf-8" />required_require.php
<?php
class T1{}
?>required_require_once.php
<?php
class T2{}
?>输出结果:
测试用例运行次数: 1000000
require方式消耗: user_cpu: 0.56000000, system_cpu: 0.01000000
require_once方式消耗: user_cpu: 2.84000000, system_cpu: 1.05000000
多次运行,结果稳定. 可以看出require_once与require相比,系统cpu消耗是其105倍,用户cpu消耗约为5倍左右, 总体cpu时间消耗是其8倍左右.自动加载
PHP引入了__autoload机制, 如果脚本解释执行过程中定义了__autoload函数, 那么当需要类而类未定义的时候, 就会将类名作为参数, 调用__autoload函数, __autoload由用户程序员自己来实现一个文件的加载机制.
在使用__autoload机制时,我们需要确保文件层次结构,命名有严格的规范.
自动加载的应用场景:
当我们需要一个动态的加载机制的时候,比如使用可变类名的情况(new $classname),在这种情形下,我们为了避免一次去加载所有可能的类定义文件,通常会有一个规则去require类定义文件,那我们可以把这个require放入到__autoload中,对其做一个统一的规范.spl自动加载
__autoload解决了类的自动加载问题,但是,当系统比较大的时候,可能一个autoload并不能完全的(或难度较大)规范自动加载规则(特别是在与其他系统协同工作时).那我们首先想到的就是提供多个autoload功能的函数来解决这个问题,spl自动加载就是解决这个问题的.
通过函数spl_autoload_register($autoload_callback, $throw, $prepend)/spl_autoload_unregister来维护一个autoload函数的栈, 三个参数都是可选的, $autoload_callback是要加入的函数名, $throw指函数注册过程发生错误(比如提供的函数名不存在)时是否抛出异常, $prepend用来指明是否是向autoload栈的前面插入.总结
关于"包含文件",能够想到的就这么多,在这里做一个小的总结:
1. 尽可能自定义一个require_once函数来管理这个once的加载机制, 这样做不仅有性能上的提升,而且同时对"包含文件"进行了一个统一的管理
2. 对于利用面向对象特性较多的系统,应尝试使用autoload机制, 当系统比较复杂时, 可以使用spl_autoload_xxx对autoload进行管理
3. 对于非文件域需要的(比如类的继承等)"包含文件", 尽可能的把require/include放入到函数等具体的处理过程中,在真正需要的时候条件包含.
比如:
common.func.php
<?php
function ud_require_once($filename) {
isset($GLOBALS[$filename]) or (($GLOBALS[$filename] = 1) and require $filename);
}
?>
user.func.php
<?php
function f() {
}
?>
A.class.php
<?php
class A {
}
?>
B.class.php
<?php
//common.func.php和A.class.php是当前文件B.class.php无论如何都需要的,所以在文件头部进行包含
function_exists('ud_require_once') or require 'common.func.php';
ud_require_once("A.class.php");
class B extends A {
public function test() {
//user.func.php只有在此函数调用时才需要,所以在这里进行require
ud_require_once("user.func.php");
f();
}
}
?>
4. 这一点实际是一个老生长谈的问题, 文件结构和命名的规范...无论你是否使用autoload, 无论你是否使用自定义require_once, 无论你是否使用可变类名(new $classname()方式), 都需要遵循一个统一的文件结构和命名规范, 一旦这个规范确立了, 无论这个规范有多烂, 都要遵循它, 当然不是不能更改, 但是更改需要有一个统一的规划, 并且这种对规范的修改带来的对旧有代码的改动工作, 最好开发工具进行修改.
5. 与第四点同样重要的问题, 就是代码长度的问题, PHP是解释型语言, 多一个无用的东西, 每次执行就都会多一些消耗, 哪怕那只是一个函数定义....所以, 要尽可能的让自己的php文件功能单一, 让每次require都高效的包含所需文件关于php的文件包含, 我只有这么多的认识, 希望有更多/更好这方面认知的朋友能够不吝赐教/拍砖

解决方案 »

  1.   

    看看php手册论坛上的,他提到使用了apc缓存,结果是完全不相关的,而且他还关注到web server的stat() system。
    你可以考虑把apc去掉再试验下
    http://php.net/manual/en/function.require-once.phpKonstantin Rozinov (krozinov[at]gmail) 02-Apr-2009 12:58There's been a lot of discussion about the speed differences between using require_once() vs. require().
    I was curious myself, so I ran some tests to see what's faster:
     - require_once() vs require()
     - using relative_path vs absolute_pathI also included results from strace for the number of stat() system calls.  My results and conclusions below.METHODOLOGY:
    ------------
    The script (test.php):
    [code=PHP]<?php
     $start_time = microtime(true);
       
     /*
      * Uncomment one at a time and run test below.
      * sql_servers.inc only contains define() statements.
      */
     
     //require ('/www/includes/example.com/code/conf/sql_servers.inc');
     //require ('../../includes/example.com/code/conf/sql_servers.inc');
     //require_once ('/www/includes/example.com/code/conf/sql_servers.inc');
     //require_once ('../../includes/example.com/code/conf/sql_servers.inc');
     
     $end_time = microtime(true);
     
     $handle = fopen("/tmp/results", "ab+");
     fwrite($handle, ($end_time - $start_time) . "\n");
     fclose($handle);
    ?>The test:
      I ran ab on the test.php script with a different require*() uncommented each time:
      ab -n 1000 -c 10 www.example.com/test.phpRESULTS:
    --------
    The average time it took to run test.php once:
    require('absolute_path'):      0.000830569960420
    require('relative_path'):      0.000829198306664
    require_once('absolute_path'): 0.000832904849136
    require_once('relative_path'): 0.000824960252097
    The average was computed by eliminating the 100 slowest and 100 fastest times, so a total of 800 (1000 - 200) times were used to compute the average time.  This was done to eliminate any unusual spikes or dips.The question of how many stat() system calls were made can be answered as follows:
    - If you run httpd -X and then do an strace -p <pid_of_httpd>, you can view the system calls that take place to process the request.
    - The most important thing to note is if you run test.php continuously (as the ab test does above), the stat() calls only happen for the first request:  first call to test.php (above):
      -------------------------------
      lstat64 ("/www", {st_mode=S_IFDIR|0755, st_size=...
      lstat64 ("/www/includes", {st_mode=S_IFDIR|0755,...
      lstat64 ("/www/includes/example.com", {st_mode=S...
      lstat64 ("/www/includes/example.com/code", {st_m...
      lstat64 ("/www/includes/example.com/code/conf", ...
      lstat64 ("/www/includes/example.com/code/conf/sql_servers.inc", {st_mode...
      open ("/www/includes/example.com/code/conf/sql_servers.inc", O_RDONLY) = 17
     
      subsequent calls to test.php:
      -----------------------------
      open ("/www/includes/example.com/code/conf/sql_servers.inc", O_RDONLY) = 17- The lack of stat() system calls in the subsequent calls to test.php only happens when test.php is called continusly.  If you wait a certain period of time (about 1 minute or so), the stat() calls will happen again.
    - This indicates that either the OS (Ubuntu Linux in my case), or Apache is "caching" or knows the results of the previous stat() calls, so it doesn't bother repeating them.
    - When using absolute_path there are fewer stat() system calls.
    - When using relative_path there are more stat() system calls because it has to start stat()ing from the current directory back up to / and then to the include/ directory.CONCLUSIONS:
    ------------
    - Try to use absolute_path when calling require*().
    - The time difference between require_once() vs. require() is so tiny, it's almost always insignificant in terms of performance.  The one exception is if you have a very large application that has hundreds of require*() calls.
    - When using APC opcode caching, the speed difference between the two is completely irrelevant.
    - Use an opcode cache, like APC!Konstantin Rozinov
    krozinov [at] gmail[/code]
      

  2.   

    哦,他是用的ab压力测试,你是脚本里while测试.
    我个人觉得ab来测试可能更符合实际情况.
      

  3.   


    刚才做了测试, 由于ud_require_once和require_once实际都require文件了, 所以它们的差异在于检测, 当我的测试脚本里只有一个ud_require_once或require_once的时候, ab测试的结果不足以令人致信, 所以, 我在脚本内增加了循环, 每个脚本进行1000次尝试, ab的参数设置为请求总数5000, 并发两百(我的开发机, 大了承受不了)...先发下测试代码, 对应四个测试用例分别是require, require_once相对路径及绝对路径版本.
    <?php
    while($i ++ < 100)
    isset($GLOBALS["required_require.php"]) or require "required_require.php";
    ?>
    <?php
    while($i ++ < 100)
    require_once "required_require.php";
    ?>
    <?php
    while($i ++ < 100)
    isset($GLOBALS["required_require.php"]) or require "/media/development/workspace/php/php-test-require/required_require.php";
    ?>
    <?php
    while($i ++ < 100)
    require_once "/media/development/workspace/php/php-test-require/required_require.php";
    ?>测试结果
    相对路径/自定义require_once
      50%     95
      66%    101
      75%    107
      80%    110
      90%    124
      95%    128
      98%    134
      99%    161
     100%   8877 (longest request)相对路径/系统require_once
      50%    151
      66%    154
      75%    159
      80%    162
      90%    177
      95%    185
      98%    196
      99%    225
     100%  11696 (longest request)绝对路径/自定义require_once
      50%     98
      66%    103
      75%    107
      80%    112
      90%    122
      95%    127
      98%    139
      99%    152
     100%   7988 (longest request)绝对路径/系统require_once
      50%    124
      66%    128
      75%    132
      80%    136
      90%    152
      95%    159
      98%    171
      99%    185
     100%  11695 (longest request)
    我们取99%的响应时间分析:
    相对路径ud_require_once: 161
    相对路径require_once: 225
    绝对路径ud_require_once: 152
    绝对路径require_once: 185首先, 我们可以确信的是绝对路径方式比相对路径要快, 上面的数据从这一方面来说是可信的(文件内只有一次require_once的, 经过测试, 数据不足以致信).那么从整体的响应时间来看, 我们仍然可以看出, 系统的require_once是有一定劣势的....当然, 这里扯出来的是上面我没有提到的一个问题, 就是绝对路径的require优于相对路径require.其实, 在做这个测试之前, 我们可以通过我提到的我的测试结果来推测到这个结论:
    测试用例运行次数: 1000000
    require方式消耗: user_cpu: 0.56000000, system_cpu: 0.01000000
    require_once方式消耗: user_cpu: 2.84000000, system_cpu: 1.05000000
    这里我们得到的是CPU消耗, 很明显可以看到系统提供的require_once较之自定义的, cpu消耗要高不少, 所以, 服务器整体压力是必然会上去的.
    感谢BooJS提供的文档及数据
      

  4.   

    不好意思, 上面的ab并发设置是10000请求数, 150并发, 上面写的值是我开始使用的, 后来修改为-n 10000 -c 150我感觉数据比较靠谱一些.