PHP: kā uzrakstīt augstas veiktspējas skriptu
PHP, iespējams, ir skriptiem līdzīga valoda un tiek kritizēta par lēnu darbību, taču tā joprojām ir viena no visplašāk izmantotajām tīmekļa serveru valodām. Laika gaitā lielākie tīmekļa uzņēmumi, kas izmanto PHP, ir centušies optimizēt dzinēju. Jēdziena ieviešana Atkritumu savācējs 5.3 versijā bija ievērojams progress. Bet ir daudz veidu, kā'optimizējiet skripta veiktspēju.
Šīs optimizācijas galvenokārt tiek izmantotas vienkāršās lapas ģenerēšanas ietvaros, lai gan tās spēj dot zināmu ieguvumu, tomēr nav pietiekami pamanāmas lietotājam. Datu bāzu, tīmekļa serveru optimizācija, izmantojot Nginx, dzinēju optimizācija, ienākot HHVM vai pat HippyVM, ļaus jums vienkārši paātrināt lapu renderēšanu un vienkāršāk optimizēt atbilžu uz jūsu jautājumiem laiku. Neskatoties uz to, mēs dažreiz izstrādājam PHP skriptus, lai tos izveidotu smaga ārstēšana, vai tīmekļa serveri neko nevar darīt.
Šajā amatā es aprakstīšu sīkāk trīs optimizācijas jomas ko nesen izmantoju skriptam, kuram bija jāapstrādā CSV vai XLS faili, kas satur lielu informācijas daudzumu. Izmantotās atmiņas apjoms bez bažām sasniedza 1 GB un varēja ilgt vairāk nekā 1/2 stundu.
Pirms es iepazīstināšu jūs ar trim PHP jēdzieniem, kas var jums ļautoptimizēt izpildes laiku kā arī aizņemtās atmiņas apjomu, ņemiet vērā, ka, pirms sūdzaties par X valodu, jūsu skripta algoritms ir atbildīgs par lielu apstrādes daļu. C++ var būt bezgalīgi ātrāks par PHP, taču, ja jūsu C++ algoritmi ir slikti, tas nekavējoties neatrisinās jūsu problēmas.
Atdaliet tā mainīgos lielumus, ierobežojiet atmiņas patēriņu
Pirms PHP 5.3 izlaišanas PHP problēma bija pārmērīgs atmiņas patēriņš. Turklāt bieži esmu dzirdējis, ka PHP skripta atmiņas patēriņu nekad nevarētu samazināt... Ja kādu laiku tā bija taisnība, tad par laimi vairs nav taisnība. Šī vadība izmanto atkritumu savācēja jēdzienu, ko mēs redzēsim nedaudz tālāk.
Pazaudēts labs ieradums...
Dažās valodās tas bija obligāti. PHP šis jēdziens ir pilnībā aizmirsts! Šī mazā vietējā funkcija unset(), ir tomilzīga lietderība. Tas ir līdzvērtīgs free() funkcijai C++ un ļauj to darīt izdalīt un tāpēc no nekavējoties atbrīvojiet atmiņu izmanto mainīgais. Mainīgais ir pilnībā iznīcināts. Tas sākotnēji atbrīvo PHP no neizmantotiem mainīgajiem. Tam ir vēl viena interese, kas palīdz mums labāk strukturēt mūsu algoritmus. Protams, kad metode beidzas, mainīgie tiek automātiski noņemti, taču tālāk redzēsit, ka tas ļauj optimizēt laiku, ko "atkritumu savācējs" pavada strādājot vai vienkārši veicot savu darbu, ja tas tiek deaktivizēts . Tāpēc ir arī pieaugums ātruma ziņā. Un ticiet man, GC patērē daudz resursu, lai atbrīvotu atmiņu.
Kā jau teicu iepriekšējā rindkopā, funkcijas vai metodes beigās PHP noņem neizmantotos mainīgos. Bet, ja skripts apstrādā masu datus, viena funkcija var administrēt pārējās. Tāpat kā dažās valodās, ja tā ir galvenā funkcija, jums, iespējams, ir tendence saglabāt mainīgos atpakaļ no funkcijas, pirms tos nododat citām funkcijām. Mēs ātri varam iegūt lielu datu apjomu. Tiklīdz to vairs neizmanto, nav jēgas to "nēsāt" līdzi, mēs to izdalām.
Nesen, rakstot skriptu, kurā es izvilku veselu failu, kas lielāks par 15 MB, kad biju to izmantojis, es to izdzēsu un atļāvu iegūt atmiņu !
Izpratne par atkritumu savācēju
Tieši pētot atmiņas pārvaldību es uzgāju kolēģes rakstu. Šajā rakstā, kas tagad ir nedaudz vecs, Paskāls Mārtins izskaidro jaunumu, kas vairs nav tāds, — atkritumu savācējs.
Kas ir atkritumu savācējs?
Tiem, kas nezina vai jau ir dzirdējuši par to, bet nekad nav bijuši izdevīgi to izmantot, Atkritumu savācējs, kas franču valodā tiek tulkots kā "drupu savācējs", zema līmeņa funkcionalitāte kas ar intervālu X aptur darbības procesu un veic skripta piešķirtās atmiņas skenēšanu, lai noteiktu, kuri mainīgie vairs nav pieejami, lai tos dzēstu.
Es nesaprotu, agrāk man teica, ka metodes beigās PHP iznīcina mainīgos.
Tā nav gluži taisnība. Tāpat kā C vai C++ (PHP vecāks), metodes beigās PHP iziet un zaudē atsauci, kas tai bija uz objektu. C valodā eksistē tikai rādītāja jēdziens, C++ jēdzieni rādītājs un atsauce pastāv līdzās.
Kad atsauce uz objektu ir pazaudēta un kad tiek startēts atkritumu savācējs, tas noteiks, ka mainīgais vairs nav pieejams, un iznīcinās to.
Šis jēdziens ir ļoti izplatīts gan JAVA, gan citās jaunākajās valodās un līdz ar to arī augstākā līmenī.
Ko atnesa Atkritumu savācējs?
GC nodrošināja ievērojamu attīstības elastību. Acīmredzami, ka izstrāde C ir tehniskāka nekā Java, PHP vai citos. Neskatoties uz to, daži aspekti tagad ir aizmirsti, piemēram, atmiņas pārvaldība, un tas ir mūsu kods, kas dažreiz jūt līdzi. Ja tas nodrošināja elastību mūsu attīstībā, tas arī palēnināja mūsu programmu izpildi.
Izmantojot šos rīkus, mēs nedrīkstam aizmirst, ka mēs vienmēr varam kaut kādā veidā kontrolēt atmiņu. Jautājums par gribu vai nepieciešamību...
Visbeidzot, es domāju, ka šāda veida rīki ir ļoti praktiski, taču tie nevar aizstāt izstrādātāju sniegtos norādījumus. No savas puses es uzskatu, ka C++ jēdziens shared_ptr
ir daudz interesantāka un nākotnē varētu piedāvāt lielu veiktspējas pieaugumu, ja Zend dažos gadījumos nolems izmantot šo metodi.
Un PHP?
Kopš PHP 5.3 ir pievienotas četras funkcijas.
- gc_enable
- gc_enabled
- gc_disable
- gc_collect_cycles
Es atstāju jums brīvu laiku, lai lasītu dokumentāciju, taču, lai būtu ātri, tie ļauj aktivizēt GC, zināt, vai tas ir aktīvs, deaktivizēt to un manuāli sākt kolekciju.
Paskāls Mārtins publicēja skriptu, kuru es iepriekš saitiju ar jums, skriptu, kurā viņš veic akumulatora pārbaudes darbu. Testa ziņojumā skaidri redzams, ka versija bez GC sasniedz milzīgu atmiņas patēriņu, pat sasniedzot limitu un avārijā.
etalons
Šeit ir izpildīts kods. Tas tika ņemts no Paskāla Mārtina emuāra un jo īpaši no viņa raksta.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
klase mezgls {
valsts $parentNode;
valsts $childNodes = masīvs,
funkcija mezgls() {
$ šo->nodeValue = str_repeat("0123456789", 128);
}
}
funkcija izveidot attiecības() {
$vecāks= jauns mezgls ();
$bērns = jauns mezgls ();
$parent->childNodes[] = $bērns;
$bērns->parentNode = $parent;
}
forums ($i = 0; $i 500000; $i++) {
izveidotRelationship();
// Šī daļa tiek izpildīta otrā testa laikā
if ($options[“gc_manual”]) {
gc_collect_cycles();
}
}
|
Es veicu trīs testus atsevišķi:
- Es veicu šo testu ar pamata opcijām. Atkritumu savācējs ir iespējots, un PHP to regulāri palaiž. Skriptam ir ilgums 7.55 secondes un izmantotā atmiņa ir sasniegusi 20.98 Mo.
- Es veicu to pašu testu, atspējojot atkritumu savācēju un izsaucot to katrā cilpas pagriezienā. Skripts ilgst 3.79 sekundes un izmantotā atmiņa sasniedza maksimumu 244.77 KB.
- Es veicu trešo pārbaudi, atspējojot atkritumu savācēju un nekad nevācot manuāli. Tāpēc atmiņai ir ļoti jāpiepilda. Skripts ilgst 4.46 sekundes un atmiņa ir sasniegusi 1.98 Go.
Izmantojot šo testu, mēs varam skaidri redzēt atmiņas pārvaldības nozīmi. Mūsu piemērā, kas ir novests līdz galējībai, mēs skaidri redzam nozīmi. No vienas puses, atkritumu savācējs daudz strādā pie atmiņas patēriņa, taču tas palēninās skripta izpildi. To ļoti labi varam redzēt, salīdzinot 1. testu (ar GC) un 3. testu bez vadības.
Mūsu veiktspējas darbs tiek veikts 2. testā. Nav automātiskas atmiņas pārvaldības, manuāla pārvaldība ir optimizēta šajā skriptā. Mums izdodas gandrīz divas reizes paātrināt apstrādes ātrumu (7.55 s / 3.79 s = 1.99). Pa šo ceļu, mēs arī ierobežojām savu atmiņu līdz 245 KB pret 21 MB automātiskai pārvaldībai. Tas ir koeficients gandrīz 88 ((20.98 * 1024) / 244.77 = 87.77).
Secinājumi
Lai gan iepriekš minētais piemērs ierobežo atmiņas pārvaldību, šis tests ir paredzēts, lai parādītu, cik daudz tas var būt. svarīgi pētīt atmiņas pārvaldību mūsu skriptos. Dažos gadījumos izmaksas var būt iespaidīgas. Var ietaupīt apstrādes laiku, atmiņu un visu, kas ar to saistīts.
Saprast un izmantot atsauces
Kā jau teicu nedaudz iepriekš, PHP ievieš mainīgo nodošanu ar atsauci, un, kā jau teicu iepriekš, šāda veida nodošana ir vairāk vai mazāk līdzvērtīga nodošanai ar rādītāju.
- Izpratne par garāmejošu atsauci
- Izpratne par rādītāja nodošanu
Šī ir svarīga daļa ne tikai PHP, jo šis jēdziens pastāvēja jau sen, bet arī IT. Būtībā, kad deklarējat funkciju, fragments tiek veikts, kopējot.// Iet garām publiskās kopēšanas funkcijai foo($arg) {…}
Tas nozīmē daži plusi un mīnusi atkarībā no jūsu koda. Objekts tiek kopēts, kas nozīmē, ka piešķirtā atmiņa palielināsies par nodotā objekta svaru. Tas var būt smieklīgi, ja izlaižat Būla vērtību, taču tas var būt daudz svarīgāk, ja, piemēram, izdodat masīvu.
1
2
3
4
5
6
7
8
9
10
11
|
valsts funkcija foo() {
$a = 1;
bārs($a);
palaist garām $a; //$a ir 1
}
valsts funkcija bar($arg) {
$arg = 2;
}
|
Šajā gadījumā mēs redzam, ka vērtība nav mainīta, tas var būt interesanti, ja mēs izmantojam mainīgo $a kā bāzi un mēs nekad nevēlamies pieskarties tā vērtībai. Šeit mēs varētu runāt par jēdzienu, kas neeksistē PHP, par nemainīgu mainīgo (const).
1
2
3
4
5
6
7
8
9
10
11
12
|
valsts funkcija foo() {
$a = 1;
$a = josla($a);
palaist garām $a; //$a ir 2
}
valsts funkcija bar($arg) {
$arg = 2;
atgriešanās $arg;
}
|
Esmu pārliecināts, ka jūs jau esat rakstījis par šādām metodēm. Šajā gadījumā tā ir acīmredzama kļūda. Protams, sintakse ir pilnīgi patiesa, taču parametra kopijai ir piešķirts sadalījums un ir veikta mainīgā lieluma atdalīšana. Dārgi atmiņas zvani un nevajadzīgs apstrādes laiks. Zemāk esošā veidlapa būtu līdzvērtīga, bet daudz ātrāka.
1
2
3
4
5
6
7
8
9
10
11
|
valsts funkcija foo() {
$a = 1;
bārs($a);
palaist garām $a; //$a ir 2
}
valsts funkcija bar(&$arg) {
$arg = 2;
}
|
Šeit mēs veicām tieši tādu pašu apstrādi, taču tā ir vienkārši ātrāka un mazāk patērējoša.
Šo optimizāciju ir ļoti vienkārši veikt, un tā var pat padarīt jūsu kodu vieglāk lasāmu. Ir vairākas sintakses, kas jāzina, lai veiktu garāmejošu atsauci.
- Parametrs tiek nodots pēc atsauces (ko mēs tikko redzējām)
- Funkciju atgriešana pēc atsauces (šeit nav sīkāk, jo PHP dzinējs optimizē šo daļu un es nejutu nekādu pārliecinošu ieguvumu)
- Mainīgā kopēšana pēc atsauces, nav ļoti noderīgs, bet milzīgs. Es ļāvu jums izlasīt PHP dokumentāciju, kas ir ļoti detalizēta. Jūs varētu viegli uzzināt lietas, kuras es izlaidu 🙂
Cita optimizācija
Pateicoties trim iepriekšminētajām optimizācijām, jums nevajadzētu rasties problēmām, paātrinot apstrādi. Tomēr ir arī citi priekšmeti. Es uzskaitu interesantus rakstus par dažādām optimizācijām, ko varat veikt.
- PHP String Concat vs Array Implode
Secinājumi
Trīs daļas, kuras es jums tikko aprakstīju, ir laba alternatīva manai gaumei, lai optimizētu tā skriptus un izvairītos no izstrādes restartēšanas ātrāka valoda kā apkopota valoda.
Es veicu šīs trīs piespēles vienā no saviem scenārijiem, un, cita starpā, man tas izdevās samaziniet atmiņas patēriņu par 3 aptuveni un Palieliniet ātrumu par 2.