PHP: Kako napisati visoko zmogljiv skript
PHP je morda jezik, podoben skriptu, in ga kritizirajo zaradi počasnosti, vendar ostaja eden najbolj razširjenih jezikov spletnih strežnikov. Sčasoma so glavna spletna podjetja, ki uporabljajo PHP, poskušala optimizirati motor. Uvedba koncepta Zbiralec smeti v različici 5.3 je bil pomemben napredek. Vendar obstaja veliko načinov za'optimizira delovanje skripta.
Te optimizacije, ki se uporabljajo predvsem v okviru enostavnega generiranja strani, kljub temu, da lahko prinesejo določen dobiček, niso dovolj vidne za uporabnika. Optimizacija podatkovnih baz, spletnih strežnikov z uporabo Nginxa, optimizacija motorja s prihodom HHVM ali celo HippyVM vam bo omogočila, da preprosto pospešite izrisovanje vaših strani in na preprostejši način optimizirate čas odgovorov na vaša vprašanja. Kljub temu včasih razvijemo PHP skripte z namenom izdelave težko zdravljenje, ali spletni strežniki ne morejo storiti ničesar.
V tej objavi bom podrobno opisal tri področja optimizacije ki sem ga pred kratkim uporabil za skript, ki je moral obdelati datoteke CSV ali XLS, ki vsebujejo veliko količino informacij. Količina uporabljenega pomnilnika je brez skrbi dosegla 1 GB in je lahko zdržala več kot 1/2 ure.
Preden vam predstavim tri pojme PHP, ki vam lahko to omogočijooptimizirajte čas izvedbe kot tudi količina zavzetega pomnilnika, vedite, da preden se pritožujete nad jezikom X, je algoritem vašega skripta odgovoren za dober del vaše obdelave. C++ je morda neskončno hitrejši od PHP, vendar če so vaši algoritmi C++ slabi, ne bo takoj rešil vaših težav.
Sprostite dodelitev njegovih spremenljivk, omejite porabo pomnilnika
Pred izdajo PHP 5.3 je bil PHP problem pretirana poraba pomnilnika. Poleg tega sem pogosto slišal, da porabe pomnilnika PHP skripte ne bi bilo mogoče nikoli zmanjšati ... Če je to nekaj časa veljalo, na srečo ni več res. To upravljanje uporablja pojem Garbage Collector, ki ga bomo videli malo nižje.
Izgubljena dobra navada...
V nekaterih jezikih je bilo to obvezno. V PHP je ta pojem popolnoma pozabljen! Ta majhna izvorna funkcija unset() je zaizjemen pripomoček. Je enakovredna funkciji free() v C++ in omogoča razdeliti in zato od takoj sprostite pomnilnik uporablja spremenljivka. Spremenljivka je popolnoma uničena. To na začetku osvobodi PHP neuporabljenih spremenljivk. To ima še en interes, saj nam pomaga bolje strukturirati naše algoritme. Seveda, ko se metoda konča, se spremenljivke samodejno sprostijo, vendar boste spodaj videli, da vam to omogoča optimizacijo časa, ki ga "pobiralec smeti" porabi za delo ali preprosto za opravljanje svojega dela v primeru, da je deaktiviran. Obstaja torej tudi pridobitev v smislu hitrosti. In verjemite mi, GC porabi veliko virov za sprostitev pomnilnika.
Kot sem rekel v prejšnjem odstavku, PHP na koncu funkcije ali metode odstrani neuporabljene spremenljivke. Toda v primeru skripta, ki obdeluje množične podatke, je možno, da ena funkcija upravlja druge. Kot v nekaterih jezikih, če gre za glavno funkcijo, boste verjetno težili k shranjevanju spremenljivk nazaj iz funkcije, preden jih posredujete drugim funkcijam. Hitro lahko končamo z veliko količino podatkov. Takoj, ko ni več v uporabi, ga nima smisla "prenašati" naokoli, ga razdelimo.
Pred kratkim sem med pisanjem skripta, kjer sem izvlekel celotno datoteko, večjo od 15 MB, ko sem jo uporabil, izbrisal in dovolil pridobiti spomin !
Razumevanje zbiralnika smeti
Med raziskovanjem upravljanja pomnilnika sem naletel na članek kolega. V tem članku, ki je zdaj že malo star, Pascal Martin pojasnjuje novost, ki je ni več, Garbage Collector.
Kaj je Garbage Collector?
Za tiste, ki ne poznajo ali so že slišali zanj, vendar ga še nikoli niso imeli priložnosti uporabiti, Garbage Collector, ki je v francoščini preveden kot "pobiranje drobtin", nizka raven funkcionalnosti ki v intervalu X ustavi tekoči proces in izvede skeniranje pomnilnika, ki ga dodeli skript, da odkrije, katere spremenljivke niso več dostopne, da jih izbriše.
Ne razumem, prej so mi povedali, da PHP na koncu metode uniči spremenljivke.
To ne drži povsem. Na enak način kot C ali C++ (starš PHP) se PHP na koncu metode zapre in izgubi referenco, ki jo je imel na objekt. V C obstaja samo pojem kazalca, v C++ pojma kazalec in sklic obstajata soobstojno.
Ko se sklic na objekt izgubi in ko se Garbage Collector zažene, bo slednji ugotovil, da spremenljivka ni več dostopna, in jo uničil.
Ta koncept je zelo prisoten tako v JAVI kot tudi v drugih novejših jezikih in posledično na višji ravni.
Kaj je prinesel Smetar?
GC je zagotovil precejšnjo razvojno prilagodljivost. Očitno je, da je razvoj v C bolj tehničen kot v Javi, PHP ali drugih. Kljub temu so nekateri vidiki zdaj pozabljeni, kot je upravljanje pomnilnika, in naša koda je tista, ki včasih sočustvuje. Če je to prineslo fleksibilnost v našem razvoju, je hkrati upočasnilo izvajanje naših programov.
S temi orodji ne smemo pozabiti, da lahko vedno na nek način nadzorujemo spomin. Vprašanje volje ali nuje ...
Nenazadnje menim, da so tovrstna orodja zelo praktična, vendar ne morejo nadomestiti navodil razvijalcev. Z moje strani ugotavljam, da je pojem C++ shared_ptr
je veliko bolj zanimiv in bi lahko v prihodnosti ponudil preveliko povečanje zmogljivosti, če se Zend odloči za uporabo te metode v nekaterih primerih.
In v PHP?
Od PHP 5.3 so bile dodane štiri funkcije.
- gc_enable
- gc_enabled
- gc_onemogoči
- gc_collect_cycles
Pustim vam prosti čas, da preberete dokumentacijo, vendar vam omogočajo, da aktivirate GC, ugotovite, ali je aktiven, ga deaktivirate in ročno zaženete zbiranje.
Pascal Martin je v objavi, ki sem jo povezal z vami zgoraj, objavil skript, v katerem izvaja preizkus baterije. V testnem poročilu lahko jasno vidimo, da različica brez GC doseže enormno porabo pomnilnika, celo doseže mejo in se zruši.
merilo
Tukaj je izvedena koda. To je bilo vzeto iz bloga Pascala Martina in natančneje iz njegovega članka.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
razred Node {
javnega $parentNode;
javnega $childNodes = matrika();
funkcija Node() {
$ to->nodeValue = str_repeat('0123456789', 128);
}
}
funkcija createRelationship() {
$parent= novo vozlišče();
$otrok = novo vozlišče();
$parent->childNodes[] = $child;
$child->parentNode = $parent;
}
za ($i = 0; $i 500000; $i++) {
createRelationship();
// Ta del se izvede med drugim testom
if ($options[“gc_manual”]) {
gc_collect_cycles();
}
}
|
Ločeno opravim tri teste:
- Ta test izvajam z osnovnimi možnostmi. Zbiralnik smeti je omogočen in PHP ga izvaja redno. Scenarij ima trajanje 7.55 sekund in uporabljeni pomnilnik je dosegel 20.98 Mo.
- Izvedem isti test tako, da onemogočim zbiralnik smeti in ga pokličem ob vsakem obratu zanke. Scenarij traja 3.79 sekunde in uporabljeni pomnilnik je dosegel vrhunec 244.77 KB.
- Tretji preizkus izvedem tako, da onemogočim zbiralnik smeti in nikoli ne zbiram ročno. Pomnilnik se mora zato močno napolniti. Scenarij traja 4.46 sekunde in spomin je dosegel 1.98 Go.
S tem testom lahko jasno vidimo pomen upravljanja pomnilnika. V našem primeru, ki je veden do skrajnosti, lahko jasno vidimo pomembnost. Po eni strani Garbage Collector veliko dela na porabi pomnilnika, vendar bo to ponavadi upočasnilo izvajanje skripta. To lahko zelo dobro vidimo, če primerjamo 1. test (z GC) in 3. test brez upravljanja.
Kjer je naše delo na uspešnosti opravljeno, je preizkus 2. Brez samodejnega upravljanja pomnilnika, optimizirano ročno upravljanje znotraj tega skripta. Hitrost obdelave uspemo pospešiti za skoraj dvakrat (7.55 s / 3.79 s = 1.99). V to smer, omejili smo tudi naš pomnilnik na 245 KB proti 21 MB za samodejno upravljanje. To je koeficient skoraj 88 ((20.98 * 1024) / 244.77 = 87.77).
zaključek
Čeprav zgornji primer potisne upravljanje pomnilnika do meje, je ta test namenjen temu, da nam pokaže, koliko je to lahko. pomembno preučiti upravljanje pomnilnika v naših skriptih. Izplačila so lahko v nekaterih primerih impresivna. Prihranite lahko čas obdelave, pomnilnik in vse, kar spada zraven.
Razumeti in uporabljati reference
Kot sem vam povedal malo prej, PHP izvaja posredovanje spremenljivk po sklicu in kot sem vam povedal zgoraj, je ta vrsta podajanja bolj ali manj enakovredna podajanju po kazalcu.
- Razumevanje prenosa po sklicu
- Razumevanje podajanja kazalca
To je pomemben del, ne le za PHP, ker je ta pojem obstajal že dolgo prej, ampak tudi za IT. V bistvu, ko deklarirate funkcijo, se prehod izvede s kopiranjem.// Posredovanje funkcije javne kopije foo($arg) {…}
To pomeni nekaj prednosti in slabosti odvisno od vaše kode. Objekt je kopiran, kar pomeni, da se bo dodeljeni pomnilnik povečal za težo posredovanega objekta. To je morda smešno, če posredujete logično vrednost, vendar je lahko veliko bolj pomembno, če na primer posredujete matriko.
1
2
3
4
5
6
7
8
9
10
11
|
javnega funkcija foo() {
$a = 1;
bar($a);
echo $a; //$a je 1
}
javnega funkcija bar($arg) {
$arg = 2;
}
|
V tem primeru vidimo, da vrednost ni bila spremenjena, to je lahko zanimivo, če uporabimo spremenljivko $a kot osnovo in se nikoli ne želimo dotakniti njene vrednosti. Tu bi lahko govorili o pojmu, ki v PHP ne obstaja, o konstantni spremenljivki (const).
1
2
3
4
5
6
7
8
9
10
11
12
|
javnega funkcija foo() {
$a = 1;
$a = bar($a);
echo $a; //$a je 2
}
javnega funkcija bar($arg) {
$arg = 2;
vrnitev $arg;
}
|
Prepričan sem, da ste že napisali takšne metode. V tem primeru gre očitno za napako. Seveda je sintaksa popolnoma resnična, vendar je bila izvedena dodelitev za kopijo parametra in sprostitev spremenljivke. Dragi pomnilniški klici in nepotreben čas obdelave. Spodnji obrazec bi bil enakovreden, vendar veliko hitrejši.
1
2
3
4
5
6
7
8
9
10
11
|
javnega funkcija foo() {
$a = 1;
bar($a);
echo $a; //$a je 2
}
javnega funkcija bar(&$arg) {
$arg = 2;
}
|
Tukaj smo izvedli popolnoma enak tretma, le da je hitrejši in manj zamuden.
To optimizacijo je zelo enostavno izvesti in lahko celo olajša branje vaše kode. Obstaja več sintaks, ki jih je treba poznati pri izvajanju prenosa po sklicu.
- Prenos parametrov po sklicu (kar smo pravkar videli)
- Vrnitev funkcije po sklicu (tu ni podrobno opisano, ker motor PHP optimizira ta del in nisem začutil nobenega prepričljivega dobička)
- Kopiranje spremenljivke s sklicevanjem, ne preveč uporabno, a izjemno. Pustim vam, da preberete dokumentacijo PHP, ki je izjemno podrobna. Z lahkoto se lahko naučiš stvari, ki sem jih izpustil 🙂
Druga optimizacija
Zahvaljujoč trem zgornjim optimizacijam ne bi smeli imeti težav s pospešitvijo obdelave. Vendar pa obstajajo še drugi predmeti. Navajam vam zanimive članke o različnih optimizacijah, ki jih lahko izvedete.
- PHP String Concat proti Array Implode
zaključek
Trije deli, ki sem vam jih pravkar podrobno predstavil, so dobra alternativa za moj okus za optimizacijo skriptov in izogibanje ponovnemu zagonu razvoja v hitrejši jezik kot prevedeni jezik.
Te tri prehode sem naredil na enem od svojih scenarijev in med drugim mi je uspelo zmanjšajte porabo pomnilnika za 3 približno in doseči povečanje hitrosti za 2.