Daha iyi PHP kodlama uygulamaları hakkında okuduğum her şey require_once
hız nedeniyle kullanmamaya devam ediyor .
Bu neden?
Aynı şeyi yapmanın doğru / daha iyi yolu require_once
nedir? Eğer önemliyse, PHP 5 kullanıyorum.
Daha iyi PHP kodlama uygulamaları hakkında okuduğum her şey require_once
hız nedeniyle kullanmamaya devam ediyor .
Bu neden?
Aynı şeyi yapmanın doğru / daha iyi yolu require_once
nedir? Eğer önemliyse, PHP 5 kullanıyorum.
Yanıtlar:
require_once
ve include_once
her ikisi de sistemin zaten dahil edilmiş / gerekli olan şeylerin bir kaydını tutmasını gerektirir. Her *_once
çağrı, o günlüğü kontrol etmek anlamına gelir. Yani kesinlikle orada bazı ekstra orada yapılıyor iş ama bütün app hızını zarar veren yeterli?
... Gerçekten şüpheliyim ... Gerçekten eski bir donanıma sahip değilseniz veya çok fazla bir şey yapmazsanız olmaz .
Eğer varsa edilmektedir binlerce işinin *_once
, daha hafif bir şekilde çalışmalarını kendiniz yapabilir. Basit uygulamalar için sadece yapım emin sadece bir kez yer verdik gerektiğini yeterli ancak yine Redefine hataları alıyorsanız, yapabilirsin böyle bir şey:
if (!defined('MyIncludeName')) {
require('MyIncludeName');
define('MyIncludeName', 1);
}
Şahsen *_once
ifadelere sadık kalacağım, ancak aptal milyon geçişte bir kıyaslama üzerinde, ikisi arasında bir fark görebilirsiniz:
php hhvm
if defined 0.18587779998779 0.046600103378296
require_once 1.2219581604004 3.2908599376678
10-100 × daha yavaş require_once
ve require_once
görünüşe göre daha yavaş görünüyor hhvm
. Yine, bu sadece kodunuzu *_once
binlerce kez çalıştırıyorsanız geçerlidir .
<?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.
Bu konu beni rahatsız ediyor, çünkü zaten bir "çözüm gönderildi" ve tüm niyetler ve amaçlar için yanlış. Numaralayalım:
Tanımlar şunlardır gerçekten PHP'de pahalı. Sen olabilir o kadar bak ya kendiniz test edin, ama PHP Genel bir sabit tanımlamanın verimli tek yolu bir uzantısı üzerinden gerçekleştirilir. (Sınıf sabitleri aslında oldukça iyi performans açısından akıllıca, ancak bu 2'den beri tartışmalı bir nokta)
require_once()
Uygun şekilde kullanıyorsanız , yani sınıfların dahil edilmesi için bir tanımlamaya bile ihtiyacınız yoktur; sadece kontrol edin class_exists('Classname')
. Eklediğiniz dosya kod içeriyorsa, yani bunu yordamsal yöntemle kullanıyorsanız, sizin için kesinlikle require_once()
gerekli bir neden yoktur ; her rutin aramayı yaptığınızı varsaydığınız dosyayı her eklediğinizde.
Bu yüzden bir süre için birçok kişi bu class_exists()
yöntemi dahil etmeleri için kullandı. Sevmiyorum çünkü çirkin, ama iyi bir nedeni vardı:require_once()
PHP'nin bazı yeni sürümlerinden önce oldukça verimsizdi. Ancak bu düzeltildi ve benim koşulum, koşullu ve ekstra yöntem çağrısı için derlemek zorunda olduğunuz ekstra bayt kodunun, herhangi bir dahili hashtable kontrolünden çok daha ağır basacağıdır.
Şimdi, bir kabul: bu şeyleri test etmek zor, çünkü yürütme süresinin çok azını oluşturuyor.
Düşünmeniz gereken soru şudur: genel bir kural olarak, PHP'de pahalıdır, çünkü yorumlayıcı birine her vurduğunda ayrıştırma moduna geri dönmesi, opcodları üretmesi ve sonra geri atlaması gerekir. 100'den fazla içeriğiniz varsa, bunun kesinlikle bir performans etkisi olacaktır. Requir_once kullanmanın veya kullanmamanın bu kadar önemli bir soru olmasının nedeni, opcode önbellekleri için hayatı zorlaştırmasıdır. Bunun için bir açıklama burada bulunabilir, ancak bunun kaygılanması şudur:
Ayrıştırma süresi boyunca, isteğin tüm ömrü boyunca hangi dosyaları içereceğinizi tam olarak biliyorsanız, require()
en başta olanlar ve opcode önbellek sizin için her şeyi halledecektir.
Bir opcode önbellek çalıştırmıyorsanız, zor bir yerdesiniz. Tüm içeriklerinizi tek bir dosyaya satır içine almak (bunu geliştirme sırasında, sadece üretimde yapmayın) kesinlikle ayrıştırmaya yardımcı olabilir, ancak bu bir acıdır ve ayrıca, istek.
Otomatik yükleme çok uygundur, ancak yavaştır, çünkü otomatik yükleme mantığının her ekleme yapıldığında çalıştırılması gerekir. Uygulamada, bir istek için birkaç özel dosyanın otomatik olarak yüklenmesinin çok fazla soruna neden olmadığını buldum, ancak ihtiyacınız olacak tüm dosyaları otomatik olarak yüklememelisiniz.
Belki 10 tane içeriğiniz varsa (bu zarf hesaplamasının çok gerisindedir), tüm bu wanking buna değmez: sadece veritabanı sorgularınızı veya bir şeyi optimize edin.
define()
, require_once()
ve defined()
tüm almak 1-2 hakkında mikrosaniye benim makinedeki her.
Merak ettim ve Adam Backstrom'un Tech Your Universe ile olan bağlantısını kontrol ettim . Bu makalede, requir_once yerine kullanılması gereken nedenlerden biri açıklanır. Ancak iddiaları benim analizime dayanamadı. Çözümü nereden yanlış değerlendirmiş olabileceğimi görmek isterim. Karşılaştırmalar için PHP 5.2.0 kullandım.
Başka bir başlık dosyası eklemek için requir_once kullanılan 100 başlık dosyaları oluşturarak başladı. Bu dosyaların her biri şöyle görünüyordu:
<?php
// /home/fbarnes/phpperf/hdr0.php
require_once "../phpperf/common_hdr.php";
?>
Bunları hızlı bir Bash kesmek kullanarak yarattım:
for i in /home/fbarnes/phpperf/hdr{00..99}.php; do
echo "<?php
// $i" > $i
cat helper.php >> $i;
done
Bu şekilde kolayca requir_once kullanma ve başlık dosyaları dahil ederken gerektiren arasında takas olabilir. Sonra yüz dosyayı yüklemek için bir app.php oluşturdum. Bu şuna benziyordu:
<?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);
?>
Requir_once üstbilgileri, aşağıdaki gibi bir üstbilgi dosyası kullanılan üstbilgileri ile kontrast:
<?php
// /home/fbarnes/phpperf/h/hdr0.php
if(!defined('CommonHdr'))
{
require "../phpperf/common_hdr.php";
define('CommonHdr', 1);
}
?>
Requir_once vs bu ile çalışırken çok fark bulamadım. Aslında, benim ilk testler requir_once biraz daha hızlı olduğunu ima gibi görünüyordu, ama ben mutlaka buna inanmıyorum. Deneyi 10000 giriş dosyasıyla tekrarladım. Burada tutarlı bir fark gördüm. Testi birden çok kez çalıştırdım, sonuçlar yakın ancak ortalama 30.8 kullanıcı jiffies ve 72.6 sistem jiffies requir_once kullanır; ortalama 39.4 kullanıcı jiffies ve 72.0 sistem jiffies kullanımı gerektirir. Bu nedenle yük, requir_once kullanılarak biraz daha düşük görünmektedir. Ancak, duvar saati biraz artar. 10.000 zorunlu_once çağrısı ortalama olarak 10.15 saniye, 10.000 zorunlu çağrı ise ortalama 9.84 saniye kullanır.
Bir sonraki adım bu farklılıklara bakmak. Yapılan sistem çağrılarını analiz etmek için strace kullandım .
Require_once'dan bir dosya açmadan önce aşağıdaki sistem çağrıları yapılır:
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
Bu, aşağıdakilerle çelişir:
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
Tech Your Universe, requir_once öğesinin daha fazla lstat64 çağrısı yapması gerektiğini belirtir. Ancak, her ikisi de aynı sayıda lstat64 çağrısı yapar. Muhtemelen, fark yukarıdaki kodu optimize etmek için APC çalıştırmıyorum olmasıdır. Ancak, daha sonra tüm çalışmalar için strace çıktısını karşılaştırdım:
[fbarnes@myhost phpperf]$ wc -l strace_1000r.out strace_1000ro.out
190709 strace_1000r.out
210707 strace_1000ro.out
401416 total
Requir_once kullanılırken başlık dosyası başına yaklaşık iki sistem çağrısı daha etkili olur. Farklardan biri, requir_once'ın time () işlevine ek bir çağrısının olmasıdır:
[fbarnes@myhost phpperf]$ grep -c time strace_1000r.out strace_1000ro.out
strace_1000r.out:20009
strace_1000ro.out:30008
Diğer sistem çağrısı getcwd ():
[fbarnes@myhost phpperf]$ grep -c getcwd strace_1000r.out strace_1000ro.out
strace_1000r.out:5
strace_1000ro.out:10004
Bu denir çünkü ben hdrXXX dosyalarında başvuru göreli yol karar verdi. Bu mutlak bir başvuru yaparsanız, tek fark kodda yapılan ek zaman (NULL) çağrısıdır:
[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
Bu, göreli yollar yerine mutlak yollar kullanarak sistem çağrılarının sayısını azaltabileceğiniz anlamına gelir. Bunun dışındaki tek fark, kodun daha hızlı olanı karşılaştırmak için kullanılması için kullanılan zaman (NULL) çağrılarıdır.
Diğer bir not, APC optimizasyon paketinin "apc.include_once_override" adlı bir seçeneğin, requir_once ve include_once çağrıları tarafından yapılan sistem çağrılarının sayısını azalttığını iddia eden bir seçeneğe sahip olmasıdır ( PHP belgelerine bakın ).
Bu kodlama uygulamalarına, bundan kaçındığını söyleyen herhangi bir bağlantı verebilir misiniz? Bence bu tam bir sorun değil . Ben kaynak kodu kendim bakmadım ama arasındaki tek fark hayal ediyorum include
ve include_once
olmasıdır include_once
dizinin her zaman üzerinde bir dizi ve kontrolleri bu dosya adını ekler. Bu diziyi sıralı tutmak kolay olurdu, bu yüzden üzerinde arama yapmak O (log n) olmalı ve orta büyüklükteki bir uygulamanın bile sadece birkaç düzine içermesi gerekir.
Bir şeyler yapmanın daha iyi bir yolu, nesne yönelimli bir yaklaşım kullanmak ve __autoload () kullanmaktır .
__autoload()
önerilmez ve gelecekte kullanım dışı olabilir, kullanmak gerekir spl_autoload_register(...)
bugünlerde ... PS2: beni yanlış anlamayın, ben bazen kullanım özdevinimli_yükle işlevselliği yapmak; )
Kötü olan işlevi kullanmıyor. Genel bir kod tabanında nasıl ve ne zaman kullanılacağına dair yanlış bir anlayış. Bu yanlış anlaşılmış düşünceye biraz daha bağlam ekleyeceğim:
İnsanlar requir_once'ın yavaş bir işlev olduğunu düşünmemelidir. Kodunuzu şu veya bu şekilde eklemeniz gerekir. require_once()
vs require()
hız sorunu değil. Körü körüne kullanmakla sonuçlanabilecek performans engelleri hakkında. Bağlam dikkate alınmadan yaygın olarak kullanılırsa, büyük bellek israfına veya israf koduna yol açabilir.
Gerçekten kötü gördüğüm şey, büyük monolitik çerçevelerin require_once()
, özellikle karmaşık bir nesne yönelimli (OO) ortamda tüm yanlış şekillerde kullanmasıdır .
require_once()
Birçok kütüphanede görüldüğü gibi her sınıfın en üstünde kullanma örneğini ele alalım :
require_once("includes/usergroups.php");
require_once("includes/permissions.php");
require_once("includes/revisions.php");
class User{
// User functions
}
Böylece User
sınıf diğer üç sınıfı da kullanmak üzere tasarlanmıştır. Yeterince adil!
Ama şimdi bir ziyaretçi siteye göz atıyor ve hatta giriş yapmıyorsa ve çerçeve yüklenirse: require_once("includes/user.php");
her bir istek için.
Bu , belirli bir talep sırasında hiç kullanmayacağı 1 + 3 gereksiz sınıfı içerir. Şişirilmiş çerçeveler, 5 MB veya daha az yerine istek başına 40 MB kullanarak bu şekilde sonuçlanır.
Yanlış kullanılmasının diğer yolları, bir sınıfın başkaları tarafından tekrar kullanılmasıdır! Diyelim ki helper
işlevleri kullanan yaklaşık 50 dersiniz var . helpers
Bu sınıflar yüklendiklerinde kullanılabilir olduklarından emin olmak için şunları elde edersiniz:
require_once("includes/helpers.php");
class MyClass{
// Helper::functions(); // etc..
}
Burada yanlış olan hiçbir şey yok. Ancak bir sayfa isteği 15 benzer sınıf içerirse. require_once
15 kez koşuyorsunuz ya da hoş bir görsel için:
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");
Requir_once () kullanımı, bu gereksiz satırları ayrıştırmak zorunda kalmadan, bu işlevi 14 kez çalıştırmak için performansı teknik olarak etkiler. Bu benzer soruna sahip diğer sadece 10 sınıf ile, bu tür anlamsız tekrarlayan kodun 100'den fazla satırını açıklayabilir.
Bununla, muhtemelen require("includes/helpers.php");
uygulamanızın veya çerçevenizin önyüklemesinde kullanmaya değer . Ancak her şey göreceli olduğu için, her şey sınıfın kullanım sıklığına karşı ağırlığının 15-100 satır tasarruf etmeye değer olup olmadığına bağlıdır . Ancak dosyayı herhangi bir istekte kullanmama olasılığı yoksa , o zaman kesinlikle ana sınıfınızda olmalıdır. Having her sınıfta ayrı ayrı kaynak israfı olur.helpers
require_once()
helpers
require
require_once
require_once
Fonksiyon gerektiğinde yararlıdır, ancak bir monolitik bir çözüm tüm sınıfları yüklenmesi için her yerde kullanmak gibi kabul edilmemelidir.
PEAR2 wiki (mevcut olduğunda) , en azından kütüphane kodu için otomatik yükleme lehine olan tüm zorunlu / içerme yönergelerinden vazgeçmenin iyi nedenlerini listelemek için kullanılır . Bunlar, phar gibi alternatif ambalaj modelleri ufukta olduğunda sizi katı dizin yapılarına bağlar .
Güncelleme: Wiki'nin web arşivlenmiş sürümü çirkin bir şekilde çirkin olduğundan, aşağıdaki en zorlayıcı nedenleri kopyaladım:
- (PEAR) paketini kullanmak için include_path gereklidir. Bu, bir PEAR paketini başka bir uygulama içinde kendi include_path ile paketlemeyi, gerekli sınıfları içeren tek bir dosya oluşturmayı, bir PEAR paketini kapsamlı kaynak kodu değişikliği olmadan bir phar arşivine taşımayı zorlaştırır.
- üst düzey requir_once koşullu requir_once ile karıştırıldığında, PHP 6 ile birlikte paketlenecek APC gibi opcode önbellekleri tarafından önbelleğe alınamayan kodla sonuçlanabilir.
- göreli requir_once, include_path öğesinin zaten doğru değere ayarlanmasını gerektirir ve bu, include_path öğesinin uygun bir paket kullanılmasını imkansız hale getirir
*_once()
Fonksiyonlar Stat Eğer dahil olduğunuz dosya zaten dahil oldu aynı değildir sağlamak için her ebeveyn dizini. Bu yavaşlamanın nedeninin bir parçası.
Kıyaslama için Siege gibi bir araç kullanmanızı tavsiye ederim . Önerilen tüm yöntemleri deneyebilir ve yanıt sürelerini karşılaştırabilirsiniz.
Daha üzerinde require_once()
yer almaktadır Tech Evreniniz .
Ve (veya alternatifleri ne olursa olsun) require_once
ve include_once
daha yavaş olsa bile , burada en küçük mikro optimizasyon seviyesinden bahsediyoruz. Zamanınız, kötü yazılmış döngü veya veritabanı sorgusunu optimize etmek gibi bir şey hakkında endişelenmekten çok daha iyi harcanır .require
include
require_once
Şimdi, require_once
kötü kodlama uygulamalarına izin veren bir argüman yapılabilir, çünkü içeriklerinizi temiz ve düzenli tutmaya dikkat etmenize gerek yoktur, ancak bunun işlevin kendisi ve özellikle hızı ile ilgisi yoktur .
Açıkçası, otomatik yükleme, kod temizliği ve bakım kolaylığı için daha iyidir, ancak bunun hızla ilgisi olmadığını netleştirmek istiyorum .
İnclude'u kullanarak oli'nin alternatifini ve __autoload () yöntemini test edersiniz; ve APC gibi bir şeyle test edin .
Sabit kullanmak işleri hızlandıracağından şüpheliyim.
Evet, düz ol 'gerektiren () biraz daha pahalıdır. Bence size bazı döngüler kaydedecek gibi kodunuzu yinelenen içermeyecek kadar düzenli tutabiliyorsanız, * _once () işlevlerini kullanmayın.
Ancak _once () işlevlerini kullanmak uygulamanızı öldürmeyecektir. Temel olarak, içeriklerinizi düzenlemek zorunda kalmak için bir bahane olarak kullanmayın . Bazı durumlarda, onu kullanmak hala kaçınılmazdır ve büyük bir sorun değildir.
Ben PEAR belgelerinde, requir_once, include ve include_once için bir öneri olduğunu düşünüyorum. Ben bu kılavuzu takip ediyorum. Başvurunuz daha açık olur.
Hızla ilgisi yok. Zarifçe başarısız olmakla ilgili.
Requir_once () başarısız olursa, komut dosyanız tamamlanır. Başka hiçbir şey işlenmiyor. İnclude_once () kullanırsanız, komut dosyanızın geri kalanı oluşturmaya devam edecektir, böylece kullanıcılarınız komut dosyanızda başarısız olan bir şeye potansiyel olarak hiçbir şey yapmayacaklardır.
Benim kişisel görüşüm, requir_once (veya include_once) kullanımının kötü bir uygulama olduğudur, çünkü requir_once bu dosyayı zaten eklediyseniz sizi kontrol eder ve çift hata ile sonuçlanan (hatalı işlevler / sınıflar / vb. .
Bir dosya eklemeniz gerekip gerekmediğini bilmelisiniz.