2008-10-09 50 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功能是必要的時候非常有用,但它不應該被視爲一個整體解決方案,在任何地方使用加載的所有類。