Wszystko, co czytałem o lepszych praktykach kodowania PHP, ciągle mówi, że nie używaj require_once
powodu szybkości.
Dlaczego to?
Jaki jest właściwy / lepszy sposób zrobienia tego samego, co require_once
? Jeśli ma to znaczenie, używam PHP 5.
php
performance
require-once
Uberfuzzy
źródło
źródło
Odpowiedzi:
require_once
iinclude_once
oba wymagają, aby system prowadził dziennik tego, co już zostało uwzględnione / wymagane. Każde*_once
połączenie oznacza sprawdzenie tego dziennika. Więc na pewno niektóre dodatkowa praca wykonywana tam, ale na tyle na niekorzyść szybkości całej aplikacji?... Naprawdę w to wątpię ... Nie, chyba że jesteś na naprawdę starym sprzęcie lub często to robisz .
Jeśli są robi tysiące
*_once
, można zrobić samemu w jaśniejszy sposób. W przypadku prostych aplikacji, po prostu upewniając się wcześniej tylko raz włączone to powinno wystarczyć, ale jeśli nadal się błędy przedefiniować, można było coś takiego:Osobiście będę trzymał się
*_once
stwierdzeń, ale na głupim benchmarku z milionem przebiegów widać różnicę między nimi:10-100 × wolniej z
require_once
i ciekawe, żerequire_once
wydaje się wolniejhhvm
. Ponownie, ma to znaczenie tylko dla twojego kodu, jeśli wykonujesz*_once
tysiące razy.źródło
Wątek ten sprawia, że się wzdrygam, ponieważ zostało już „opublikowane rozwiązanie” i jest ono pod każdym względem błędne. Wymieńmy:
W PHP definicje są naprawdę drogie. Możesz to sprawdzić lub przetestować samodzielnie, ale jedynym skutecznym sposobem zdefiniowania stałej globalnej w PHP jest użycie rozszerzenia. (Stałe klasowe są w rzeczywistości całkiem przyzwoite, jeśli chodzi o wydajność, ale jest to kwestia dyskusyjna, ponieważ 2)
Jeśli używasz
require_once()
odpowiednio, to znaczy włączasz klasy, nie potrzebujesz nawet definicji; po prostu sprawdź, czyclass_exists('Classname')
. Jeśli plik, który dołączasz, zawiera kod, tj. Używasz go w sposób proceduralny, nie ma absolutnie żadnego powodu, któryrequire_once()
powinien być dla Ciebie potrzebny; za każdym razem, gdy dołączasz plik, zakładasz, że wywołuje podprogram.Tak więc przez jakiś czas wiele osób używało tej
class_exists()
metody do swoich inkluzji. Nie podoba mi się to, ponieważ jest zepsuty, ale mieli dobry powód, aby:require_once()
był dość nieefektywny przed niektórymi nowszymi wersjami PHP. Ale to zostało naprawione i twierdzę, że dodatkowy kod bajtowy, który musiałbyś skompilować dla warunku, i dodatkowe wywołanie metody, zdecydowanie przeważałyby nad jakimkolwiek wewnętrznym sprawdzaniem z tablicą haszującą.A teraz przyznaję: to jest trudne do przetestowania, ponieważ zajmuje tak mało czasu wykonania.
Oto pytanie, o którym powinieneś pomyśleć: obejmuje z reguły są drogie w PHP, ponieważ za każdym razem, gdy interpreter trafia w jeden, musi przełączyć się z powrotem w tryb parsowania, wygenerować kody operacyjne, a następnie skoczyć z powrotem. Jeśli masz ponad 100 elementów, z pewnością będzie to miało wpływ na wydajność. Powodem, dla którego używanie lub nieużywanie require_once jest tak ważne pytanie, jest to, że utrudnia życie buforom opcode. Wyjaśnienie tego można znaleźć tutaj, ale to, co sprowadza się do tego, że:
Jeśli w czasie parsowania wiesz dokładnie, jakich plików nagłówkowych będziesz potrzebować przez cały czas trwania żądania,
require()
te na samym początku i pamięć podręczna kodu operacji zajmie się wszystkim innym za Ciebie.Jeśli nie korzystasz z pamięci podręcznej kodu operacyjnego, jesteś w ciężkim miejscu. Umieszczanie wszystkich dołączeń w jednym pliku (nie rób tego podczas programowania, tylko w środowisku produkcyjnym) może z pewnością pomóc przeanalizować czas, ale jest to uciążliwe, a ponadto musisz dokładnie wiedzieć, co będziesz uwzględniać podczas żądanie.
Automatyczne ładowanie jest bardzo wygodne, ale powolne, ponieważ logika automatycznego ładowania musi być uruchamiana za każdym razem, gdy jest wykonywane dołączanie. W praktyce odkryłem, że automatyczne ładowanie kilku wyspecjalizowanych plików dla jednego żądania nie powoduje zbyt dużego problemu, ale nie powinno się automatycznie ładować wszystkich potrzebnych plików.
Jeśli masz może 10 włączeń (to bardzo koniec kalkulacji obwiedni), całe to wankowanie nie jest tego warte: po prostu zoptymalizuj zapytania do bazy danych lub coś takiego.
źródło
define()
,require_once()
adefined()
każde z nich zajmuje około 1-2 mikrosekund na moim komputerze.Zaciekawiło mnie to i sprawdziłem link Adama Backstroma do Tech Your Universe . W tym artykule opisano jeden z powodów, dla których należy użyć wymagania zamiast require_once. Jednak ich twierdzenia nie potwierdziły mojej analizy. Chciałbym zobaczyć, gdzie mogłem źle przeanalizować rozwiązanie. Do porównań użyłem PHP 5.2.0.
Zacząłem od stworzenia 100 plików nagłówkowych, które wykorzystywały require_once do dołączenia innego pliku nagłówkowego. Każdy z tych plików wyglądał mniej więcej tak:
Stworzyłem je za pomocą szybkiego hacka Bash:
W ten sposób mogłem łatwo przełączać się między użyciem require_once i wymaganiem przy dołączaniu plików nagłówkowych. Następnie utworzyłem plik app.php, aby załadować sto plików. Wyglądało to tak:
Kontrastowałem nagłówki require_once z nagłówkami wymagającymi, które używały pliku nagłówkowego wyglądającego tak:
Nie znalazłem dużej różnicy podczas uruchamiania tego z wymaganiem a require_once. W rzeczywistości moje wstępne testy wydawały się sugerować, że require_once była nieco szybsza, ale niekoniecznie w to wierzę. Powtórzyłem eksperyment z 10000 plików wejściowych. Tutaj widziałem stałą różnicę. Przeprowadziłem test wiele razy, wyniki są bliskie, ale użycie require_once używa średnio 30,8 jiffies użytkownika i 72,6 systemowych jiffies; używanie require używa średnio 39,4 jiffies użytkownika i 72,0 systemowych jiffies. Dlatego wydaje się, że obciążenie jest nieco mniejsze przy użyciu require_once. Jednak zegar ścienny nieco się wydłuża. 10 000 połączeń require_once zajmuje średnio 10,15 sekundy, a 10 000 połączeń wymaga średnio 9,84 sekundy.
Następnym krokiem jest przyjrzenie się tym różnicom. Użyłem strace do analizy wywołań systemowych, które są wykonywane.
Przed otwarciem pliku z require_once wykonywane są następujące wywołania systemowe:
Kontrastuje to z wymaganiami:
Tech Your Universe sugeruje, że require_once powinno wykonywać więcej wywołań lstat64. Jednak oba wykonują taką samą liczbę wywołań lstat64. Być może różnica polega na tym, że nie używam APC do optymalizacji powyższego kodu. Jednak następnie porównałem wyniki strace dla całych biegów:
W efekcie w przypadku korzystania z require_once istnieje około dwóch dodatkowych wywołań systemowych na plik nagłówkowy. Jedna różnica polega na tym, że require_once ma dodatkowe wywołanie funkcji time ():
Drugie wywołanie systemowe to getcwd ():
Nazywa się to, ponieważ zdecydowałem się na ścieżkę względną, do której odwołują się pliki hdrXXX. Jeśli uczynię to odniesieniem bezwzględnym, jedyną różnicą jest wywołanie dodatkowego czasu (NULL) wykonane w kodzie:
Wydaje się to sugerować, że można zmniejszyć liczbę wywołań systemowych, używając ścieżek bezwzględnych zamiast ścieżek względnych. Jedyną różnicą poza tym jest wywołania time (NULL), które wydają się być używane do instrumentowania kodu w celu porównania tego, co jest szybsze.
Inną uwagą jest to, że pakiet optymalizacyjny APC ma opcję o nazwie „apc.include_once_override”, która twierdzi, że zmniejsza liczbę wywołań systemowych wykonywanych przez wywołania require_once i include_once (zobacz dokumentację PHP ).
źródło
Czy możesz podać nam linki do tych praktyk kodowania, które mówią, że należy tego unikać? Jeśli o mnie chodzi, to kompletny nie problem . Sam nie przeglądałem kodu źródłowego, ale wyobrażam sobie, że jedyną różnicą między
include
iinclude_once
jest to, żeinclude_once
dodaje tę nazwę pliku do tablicy i sprawdza tablicę za każdym razem. Łatwo byłoby posortować tę tablicę, więc przeszukiwanie jej powinno mieć wartość O (log n), a nawet średnio-duża aplikacja miałaby tylko kilka tuzinów dołączeń.źródło
Lepszym sposobem na robienie rzeczy jest użycie podejścia obiektowego i użycie __autoload () .
źródło
__autoload()
jest odradzane i może być przestarzałe w przyszłości, powinieneś używaćspl_autoload_register(...)
tych dni ... PS2: nie zrozum mnie źle, czasami używam funkcji automatycznego ładowania; )Nie używa złej funkcji. Jest to niepoprawne zrozumienie, jak i kiedy go używać w ogólnej bazie kodu. Dodam tylko trochę więcej kontekstu do tego prawdopodobnie niezrozumianego pojęcia:
Ludzie nie powinni myśleć, że require_once to powolna funkcja. Musisz dołączyć swój kod w ten czy inny sposób.
require_once()
vs.require()
prędkość nie jest problemem. Chodzi o wydajność utrudniającą zastrzeżenia, które mogą skutkować używaniem go na ślepo. Jeśli jest używany szeroko bez uwzględnienia kontekstu, może prowadzić do ogromnych strat pamięci lub marnotrawstwa kodu.To, co widziałem, jest naprawdę złe, gdy ogromne monolityczne frameworki używają
require_once()
na wszystkie niewłaściwe sposoby, szczególnie w złożonym środowisku obiektowym (OO).Weź przykład użycia
require_once()
na początku każdej klasy, jak widać w wielu bibliotekach:Tak więc
User
klasa została zaprojektowana do używania wszystkich trzech innych klas. Słusznie!Ale co teraz, jeśli użytkownik przegląda witrynę i nawet nie jest zalogowany, a struktura się ładuje:
require_once("includes/user.php");
dla każdego żądania.Zawiera 1 + 3 niepotrzebnych klas, których nigdy nie użyje podczas tego konkretnego żądania. W ten sposób rozdęte platformy zużywają 40 MB na żądanie, zamiast 5 MB lub mniej.
Innym sposobem, w jaki może być nadużywany, jest sytuacja, gdy klasa jest ponownie używana przez wielu innych! Załóżmy, że masz około 50 klas korzystających z
helper
funkcji. Aby upewnić się, żehelpers
są dostępne dla tych klas po ich załadowaniu, otrzymujesz:Nie ma tu nic złego per se. Jeśli jednak jedno żądanie strony zawiera 15 podobnych klas. Biegasz
require_once
15 razy lub dla ładnego obrazu:Użycie require_once () technicznie wpływa na wydajność działania tej funkcji 14 razy, oprócz konieczności analizowania tych niepotrzebnych wierszy. Mając zaledwie 10 innych, często używanych klas, które mają podobny problem, może to stanowić ponad 100 wierszy takiego raczej bezcelowego, powtarzalnego kodu.
W związku z tym prawdopodobnie warto użyć
require("includes/helpers.php");
zamiast tego na ładowaniu aplikacji lub frameworku. Ale ponieważ wszystko jest względne, wszystko zależy od tego, czy waga w porównaniu z częstotliwością użytkowaniahelpers
klasy jest warta zaoszczędzenia 15-100 liniirequire_once()
. Ale jeśli prawdopodobieństwo nieużywaniahelpers
pliku w jakimkolwiek żądaniu jest zerowe, torequire
zdecydowanie powinno być zamiast tego w twojej głównej klasie. Posiadanierequire_once
osobno w każdej klasie staje się marnotrawstwem zasobów.require_once
Funkcja jest przydatna, gdy jest to konieczne, ale nie powinny być traktowane jako monolityczny rozwiązanie wszędzie używać do ładowania wszystkich klas.źródło
Wiki PEAR2 (o ile istniało) podawało dobre powody do porzucenia wszystkich dyrektyw require / include na rzecz automatycznego ładowania , przynajmniej dla kodu biblioteki. To przywiązuje Cię do sztywnych struktur katalogów, gdy na horyzoncie pojawiają się alternatywne modele pakowania, takie jak phar .
Aktualizacja: ponieważ wersja wiki zarchiwizowana w Internecie jest niesamowicie brzydka, skopiowałem poniżej najważniejsze powody:
źródło
*_once()
Funkcje stat każdym katalogu nadrzędnego, aby upewnić się, że plik jesteś tym nie jest taki sam jak ten, który jest już zawarty. To jest jeden z powodów spowolnienia.Zalecam użycie narzędzia takiego jak Siege do testów porównawczych. Możesz wypróbować wszystkie sugerowane metodologie i porównać czasy odpowiedzi.
Więcej na
require_once()
jest w Tech waszego wszechświata .źródło
Nawet jeśli
require_once
iinclude_once
są wolniejsze niżrequire
iinclude
(lub jakiekolwiek alternatywy mogą istnieć), mówimy tutaj o najmniejszym poziomie mikro-optymalizacji. Znacznie lepiej poświęcić czas na optymalizację tej źle napisanej pętli lub zapytania do bazy danych niż martwienie się o coś takiegorequire_once
.Teraz można by argumentować, mówiąc, że
require_once
dopuszcza to złe praktyki kodowania, ponieważ nie musisz zwracać uwagi na utrzymanie czystości i porządku w dołączeniach, ale nie ma to nic wspólnego z samą funkcją , a zwłaszcza z jej szybkością.Oczywiście automatyczne ładowanie jest lepsze ze względu na czystość kodu i łatwość konserwacji, ale chcę wyjaśnić, że nie ma to nic wspólnego z szybkością .
źródło
Testujesz, używając funkcji include, alternatywy oli i __autoload (); i przetestuj go z zainstalowanym czymś takim jak APC .
Wątpię, by używanie stałej przyspieszyło sprawę.
źródło
Tak, jest to nieco droższe niż zwykłe wymagania (). Myślę, że jeśli możesz utrzymać swój kod na tyle zorganizowany, aby nie powielać dołączeń, nie używaj funkcji * _once (), ponieważ pozwoli to zaoszczędzić kilka cykli.
Ale użycie funkcji _once () nie zabije twojej aplikacji. Zasadniczo nie używaj tego jako wymówki, aby nie organizować swoich dołączeń . W niektórych przypadkach używanie go jest nadal nieuniknione i nie jest to wielka sprawa.
źródło
Myślę, że w dokumentacji PEAR jest zalecenie dla require, require_once, include i include_once. Przestrzegam tych wytycznych. Twoja aplikacja byłaby bardziej przejrzysta.
źródło
Nie ma to nic wspólnego z szybkością. Chodzi o to, żeby wdzięcznie zawieść.
Jeśli require_once () zawiedzie, Twój skrypt jest gotowy. Nic więcej nie jest przetwarzane. Jeśli użyjesz funkcji include_once (), reszta twojego skryptu będzie próbowała kontynuować renderowanie, więc Twoi użytkownicy potencjalnie nie byliby mądrzejsi w przypadku czegoś, co zawiodło w twoim skrypcie.
źródło
Osobiście uważam, że użycie require_once (lub include_once) jest złą praktyką, ponieważ require_once sprawdza za ciebie, czy już dołączyłeś ten plik i pomija błędy podwójnie dołączonych plików, które powodują błędy krytyczne (takie jak zduplikowana deklaracja funkcji / klas / itp.) .
Powinieneś wiedzieć, czy musisz dołączyć plik.
źródło