程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 網頁編程 >> PHP編程 >> 關於PHP編程 >> require_once的性能其實很低下

require_once的性能其實很低下

編輯:關於PHP編程

經過測試,require_once是一個性能低下的語法結構,當然,這個性能低下是相對於require而言的,本文闡述我們項目目前使用的require方式,通過實驗代碼證明其高效性,同時,描述我們在使用過程中遇到的問題,避免他人在同一個石頭上絆倒。

  • require: 引入一個文件,運行時編譯引入.
  • require_once: 功能等同於require,只是當這個文件被引用過後,不再編譯引入。

上面就是兩者的區別。可以看出,兩者的不同僅在於require_once有一個判斷是否已經引用過的機制。通過網絡搜索,可以看到很多關於require_once性能比require低很多的數據,這裡就不再做這個試驗。

我們項目中的做法是: 在每個文件起始位置定義一個全局變量,require的時候,使用isset($xxxxxx) or require 'xxxxx.php';

這種做法有什麼不足呢?

全局變量以$xxx方式定義的時候,如果該文件在函數內被require,該變量會被解析為函數的局部變量,而不是全局的,因此,函數內部的isset($xxx) or require 'xxx.php'這個語法結構會失效,帶來的結果當然是意料不到的,比如,類的重定義,方法的重定義等等。

前車之鑒,所以,全局變量的定義,請使用$GLOBALS['xxx'],require的時候,使用isset($GLOBALS['xxx']) or require 'xxx.php';,使用GLOBALS會比直接定義稍慢,但總比錯是要好很多的。

由於我們之前的全局變量是直接定義的,今天在和同事討論的過程中,想到另外一種寫法:

定義的位置仍然使用$xxx方式直接定義,require的方法中進行修改(文件頭部定義的全局變量和文件名是有關聯的)。

function ud_require($xxx) {
	global $$xxx;
    isset($$xxx) or require $xxx . '.php';
}

這種方式使用了動態變量,經過和直接的GLOBALS方式比較,有兩個顯著缺點:

  1. 性能,由於動態變量的引入,比GLOBALS方式慢2倍左右。
  2. 無法解決間接引用問題,因為我們無法預知被間接引用的文件名,也就無法用global去聲明那些被間接引用的文件中定義的標記性全局變量了。

好了,下面是我對GLOBALS方式的require和require_once的測試:

require_requireonce.php

<?php  
function test1($filename) {  
    //pathinfo($filename);  
    isset($filename) or require $filename;  
}  
function test2() {  
    require_once 'require_requireonce_requireonce.php';  
}  
$start = microtime(true);  
while($i ++ < 1000000) isset($GLOBALS['require_requireonce_require.php']) or require 'require_requireonce_require.php';  
$end = microtime(true);  
echo "不使用方法的isset or require方式: " . ($end - $start) . "<br />/n";  
$start = microtime(true);  
while($j ++ < 1000000) test1('require_requireonce_require.php');  
$end = microtime(true);  
echo "使用方法的isset or require方式: " . ($end - $start) . "<br />/n";  
$start = microtime(true);  
while($k ++ < 1000000) test2();  
$end = microtime(true);  
echo "require_once方式: " . ($end - $start) . "<br />/n";  
?>  

require_requireonce_require.php (用於測試require的被引入文件)

<?php  
$GLOBALS['require_requireonce_require.php'] = 1;  
class T1 {}  
?>  

require_requireonce_requireonce.php (用於測試require_once的被引入文件)

<?php  
class T2 {}  
?>  

下面是測試的結果(單位: 秒):

  • 不使用方法的isset or require方式: 0.22953701019287
  • 使用方法的isset or require方式: 0.23866105079651
  • require_once方式: 2.3119640350342

可以看出,不套一個方法的require速度是比使用方法的略快的,兩者速度都是require_once的10倍左右。

那麼,性能損耗究竟在哪裡呢?

上面require_requireone.php文件中的test1方法中,我注釋了一句pathinfo($filename),因為,我本來意圖是使用文件名不帶後綴作為標記性的全局變量名的,但是,當我使用pathinfo之後,我發現這種方式的性能消耗和require_once基本一致了。因此,我在那裡單獨的加了一個pathinfo的調用,又做了測試,果然是pathinfo在搗鬼。所以,後面我就修改為了現在的版本,直接使用文件名作為變量名,如果你害怕文件名重復,那不妨加上路徑名...

猜測: 加上pathinfo之後,require和require_once的性能消耗基本一致,那我們是否可以猜測PHP內部對require_once的處理是基於它的呢?據說PHP5.3中對require_once做了顯著的優化,但是,我測試過程中使用的是PHP5.3.5版本,仍然能夠看到和require明顯的差距,難道只是比之前版本較大優化?這個倒還沒有測試....

嘗試把test1方法做了如下修改:isset($GLOBALS[substr($filename, 0, strlen($filename) - 4)]) or require $filename;

使用手動的字符串截取,當然,截取是要耗時的,不過比pathinfo的版本是要好一點的。這次的測試結果是:

  • 不使用方法的isset or require方式: 0.21035599708557
  • 使用方法的isset or require方式: 0.92985796928406
  • require_once方式: 2.3799331188202

對於require_once修改為isset or require方式,需要注意以下幾方面:

  1. 每個文件頭部定義唯一的一個標記性變量,使用$GLOBALS['XXX'] = 1;的方式定義,並且,建議變量名是文件名或帶路徑的文件名(如果單獨的文件名會重復)
  2. 定義一個自定義require方法:
  3. function ud_require_once($filename) {
    	isset($GLOBALS[$filename]) or require $filename;
    }
    

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved