PHP: Kuinka kirjoittaa korkean suorituskyvyn komentosarja
PHP saattaa hyvinkin olla komentosarjan kaltainen kieli ja sitä kritisoidaan hitaudesta, mutta se on edelleen yksi laajimmin käytetyistä verkkopalvelinkielistä. Ajan myötä suuret PHP:tä käyttävät verkkoyritykset ovat pyrkineet optimoimaan moottorin. Käsitteen käyttöönotto Roskankerääjä versiossa 5.3 oli huomattava edistysaskel. Mutta on monia tapoja"optimoi komentosarjan suorituskyky.
Näitä optimointeja käytetään pääasiassa yksinkertaisen sivun luomisen yhteydessä, mutta vaikka ne voivat tuoda tiettyä hyötyä, ne eivät ole tarpeeksi näkyviä käyttäjälle. Tietokantojen, verkkopalvelimien optimointi Nginxin avulla, moottoreiden optimointi HHVM:n tai jopa HippyVM:n saapuessa mahdollistaa yksinkertaisesti sivujesi renderöinnin nopeuttamisen ja kyselyihisi vastausajan optimoinnin yksinkertaisemmalla tavalla. . Tästä huolimatta kehitämme joskus PHP-skriptejä tehdäksemme raskasta hoitoa, tai verkkopalvelimet eivät voi tehdä mitään.
Tässä postauksessa kerron tarkemmin kolme optimointialuetta jota käytin äskettäin skriptiin, jonka piti käsitellä CSV- tai XLS-tiedostoja, jotka sisältävät suuren määrän tietoa. Käytetyn muistin määrä saavutti huoletta 1 Gt ja kesti yli 1/2 tuntia.
Ennen kuin esittelen sinulle kolme PHP-käsitettä, joiden avulla voitoptimoida suoritusaika sekä käytetyn muistin määrä, tiedä, että ennen kuin valitat X-kielestä, komentosarjasi algoritmi on vastuussa suuresta osasta käsittelyäsi. C++ voi olla äärettömän nopeampi kuin PHP, mutta jos C++-algoritmit ovat huonoja, se ei ratkaise ongelmiasi heti.
Irrota sen muuttujat, rajoita muistin kulutusta
Ennen PHP 5.3:n julkaisua PHP:n ongelma oli liiallinen muistin kulutus. Sitä paitsi olen usein kuullut, että PHP-skriptin muistinkulutusta ei voitaisi koskaan vähentää... Jos se oli totta jonkin aikaa, se ei onneksi enää pidä paikkaansa. Tämä johto käyttää Garbage Collector -konseptia, jonka tulemme näkemään hieman alempana.
Hyvä tapa kadonnut...
Joillakin kielillä tämä oli pakollista. PHP:ssä tämä käsite on unohdettu kokonaan! Tämä pieni natiivifunktio unset(), on tomahtava apu. Se vastaa free()-funktiota C++:ssa ja sallii sen jakaa ja siksi vapauttaa muistin välittömästi muuttujan käyttämä. Muuttuja tuhoutuu kokonaan. Tämä vapauttaa aluksi PHP:n käyttämättömistä muuttujista. Tällä on toinen etu, joka auttaa meitä jäsentämään algoritmejamme paremmin. Tietenkin, kun menetelmä päättyy, muuttujat puretaan automaattisesti, mutta näet alla, että tämä mahdollistaa sen, että voit optimoida ajan, jonka "jätteenkeräilijä" viettää työssään tai yksinkertaisesti työssään siinä tapauksessa, että se poistetaan käytöstä. On siis myös voitto nopeuden suhteen. Ja usko minua, GC kuluttaa paljon resursseja muistin vapauttamiseen.
Kuten edellisessä kappaleessa sanoin, PHP poistaa käyttämättömät muuttujat funktion tai menetelmän lopusta. Mutta massadataa käsittelevän komentosarjan tapauksessa on mahdollista, että yksi toiminto hallinnoi muita. Kuten joissakin kielissä, jos se on pääfunktio, sinulla on todennäköisesti taipumus tallentaa muuttujat takaisin funktiosta ennen kuin siirrät ne muihin funktioihin. Voit nopeasti päätyä suureen tietomäärään. Heti kun sitä ei enää käytetä, ei ole mitään järkeä "kantaa" sitä mukana, jaamme sen.
Äskettäin kirjoittaessani skriptiä, jossa purin kokonaista yli 15 megatavua tiedostoa, poistin sen ja annoin saada muistia !
Roskienkerääjän ymmärtäminen
Muistinhallintaa tutkiessani törmäsin kollegani artikkeliin. Tässä nyt hieman vanhentuneessa artikkelissa Pascal Martin selittää uutuuden, jota ei enää ole, roskienkerääjän.
Mikä on Roskakori?
Niille, jotka eivät tiedä tai ovat jo kuulleet siitä, mutta joilla ei ole koskaan ollut mahdollisuutta käyttää sitä, Garbage Collector, joka on käännetty ranskaksi "murunpoimijaksi", matalan tason toiminnallisuus joka välillä X pysäyttää käynnissä olevan prosessin ja suorittaa skriptin varaaman muistin tarkistuksen havaitakseen, mitkä muuttujat eivät ole enää käytettävissä niiden poistamiseksi.
En ymmärrä, aiemmin minulle kerrottiin, että menetelmän lopussa PHP tuhoaa muuttujat.
Tämä ei ole täysin totta. Samalla tavalla kuin C tai C++ (PHP:n emo) menetelmän lopussa PHP poistuu ja menettää viitteen, joka sillä oli objektissa. C:ssä on olemassa vain osoittimen käsite, C++:ssa osoittimen ja viittauksen käsite esiintyy rinnakkain.
Kun viittaus kohteeseen on kadonnut ja kun Roskakeräys käynnistyy, jälkimmäinen määrittää, että muuttuja ei ole enää käytettävissä ja tuhoaa sen.
Tämä käsite on hyvin läsnä JAVA:ssa sekä muissa uudemmissa kielissä ja siten korkeammalla tasolla.
Mitä roskakori toi?
GC tarjosi huomattavaa kehitysjoustavuutta. On selvää, että C:llä kehittäminen on teknisempää kuin Javassa, PHP:ssä tai muissa. Tästä huolimatta jotkut aspektit ovat nyt unohdettu, kuten muistinhallinta, ja koodimme on toisinaan empatiakykyinen. Jos tämä toi joustavuutta kehitykseemme, se hidasti myös ohjelmiemme toteutusta.
Näillä työkaluilla emme saa unohtaa, että voimme aina hallita muistia jollakin tavalla. Kysymys tahdosta tai välttämättömyydestä...
Lopuksi uskon, että tämäntyyppiset työkalut ovat erittäin käytännöllisiä, mutta ne eivät voi korvata kehittäjien antamia ohjeita. Omalta osaltani huomaan, että C++-käsite shared_ptr
on paljon mielenkiintoisempi ja voi tarjota suuria suorituskyvyn parannuksia tulevaisuudessa, jos Zend päättää käyttää tätä menetelmää joissakin tapauksissa.
Ja PHP:ssä?
PHP 5.3:sta lähtien neljä toimintoa on lisätty.
- gc_enable
- gc_enabled
- gc_disable
- gc_collect_cycles
Jätän sinulle vapaa-ajan dokumenttien lukemiseen, mutta ollaksesi nopea, niiden avulla voit aktivoida GC:n, tietää, onko se aktiivinen, deaktivoida sen ja käynnistää kokoelman manuaalisesti.
Pascal Martin julkaisi viestissä, jonka linkitin sinulle yllä, käsikirjoituksen, jossa hän suorittaa testiakun. Näemme testiraportista selvästi, että versio ilman GC:tä saavuttaa valtavan muistinkulutuksen, jopa rajan saavuttaessa ja kaatuessa.
benchmark
Tässä on suoritettu koodi. Tämä on otettu Pascal Martinin blogista ja erityisesti hänen artikkelistaan.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
luokka Solmu {
julkinen $parentNode;
julkinen $childNodes = ryhmä();
toiminto Solmu() {
$ tätä->nodeValue = str_repeat('0123456789', 128);
}
}
toiminto luoda suhdetta() {
$vanhempi = uusi solmu();
$lapsi = uusi solmu();
$parent->childSolmut[] = $lapsi;
$lapsi->parentSolmu = $vanhempi;
}
varten ($i = 0; $i 500000; $i++) {
createRelationship();
// Tämä osa suoritetaan toisen testin aikana
if ($options["gc_manual"]) {
gc_collect_cycles();
}
}
|
Teen kolme testiä erikseen:
- Suoritan tämän testin perusvaihtoehdoilla. Roskienkerääjä on käytössä ja PHP käyttää sitä säännöllisesti. Käsikirjoituksella on kesto 7.55 secondes ja käytetty muisti on saavutettu 20.98 Mo.
- Suoritan saman testin poistamalla roskienkerääjän käytöstä ja kutsumalla sitä jokaisella silmukan käännöksellä. Käsikirjoitus kestää 3.79 sekuntia ja käytetty muisti oli huipussaan 244.77 KB.
- Suoritan kolmannen testin poistamalla roskienkerääjän käytöstä enkä koskaan kerää manuaalisesti. Muistin tulee siis täyttyä voimakkaasti. Käsikirjoitus kestää 4.46 sekuntia ja muisti on saavuttanut 1.98 Go.
Tämän testin avulla voimme selvästi nähdä muistinhallinnan tärkeyden. Äärimmäisyyteen vedetystä esimerkistämme voimme selvästi nähdä tärkeyden. Toisaalta Garbage Collector tekee paljon työtä muistin kulutuksen suhteen, mutta se hidastaa komentosarjan suorittamista. Näemme sen erittäin hyvin vertaamalla ensimmäistä testiä (GC:n kanssa) ja kolmatta testiä ilman hallintaa.
Suoritustyömme tehdään testissä 2. Ei automaattista muistinhallintaa, manuaalinen hallinta on optimoitu tämän skriptin sisällä. Onnistumme nopeuttamaan käsittelynopeutta lähes kaksinkertaiseksi (7.55 s / 3.79 s = 1.99). Tällä tavalla, rajoitimme myös muistimme 245 kilotavuun vastaan 21 Mt automaattista hallintaa varten. Se on kerroin melkein 88 ( (20.98 * 1024) / 244.77 = 87.77).
Yhteenveto
Vaikka yllä oleva esimerkki työntää muistinhallinnan äärirajoille, tämän testin tarkoituksena on näyttää meille, kuinka paljon se voi olla. tärkeää tutkia muistinhallintaa skripteissämme. Maksut voivat joissain tapauksissa olla vaikuttavia. Käsittelyaikaa, muistia ja kaikkea siihen liittyvää voidaan säästää.
Ymmärrä ja käytä viitteitä
Kuten kerroin vähän aikaisemmin, PHP toteuttaa muuttujien ohituksen viittauksella, ja kuten edellä kerroin, tämän tyyppinen ohitus vastaa enemmän tai vähemmän osoittimen ohittamista.
- Ohitusviittauksen ymmärtäminen
- Osoittimen ohituksen ymmärtäminen
Tämä on tärkeä osa, ei vain PHP:lle, koska tämä käsite oli olemassa kauan ennen, vaan myös IT:lle. Periaatteessa, kun määrität funktion, kulku tapahtuu kopioimalla.// Julkisen kopiointifunktion ohittaminen foo($arg) {…}
Se merkitsee joitain etuja ja haittoja koodistasi riippuen. Objekti kopioidaan, mikä tarkoittaa, että varattu muisti kasvaa ohitetun objektin painon verran. Tämä voi olla naurettavaa, jos ohitat boolen, mutta se voi olla paljon tärkeämpää, jos ohitat esimerkiksi taulukon.
1
2
3
4
5
6
7
8
9
10
11
|
julkinen toiminto foo() {
$a = 1;
bar($a);
kaiku $a; //$a on 1
}
julkinen toiminto baari($arg) {
$arg = 2;
}
|
Tässä tapauksessa näemme, että arvoa ei ole muokattu, tämä voi olla mielenkiintoista, jos käytämme muuttujaa $a perustana emmekä koskaan halua koskea sen arvoon. Tässä voisi puhua käsitteestä, jota ei ole PHP:ssä, vakiomuuttujasta (const).
1
2
3
4
5
6
7
8
9
10
11
12
|
julkinen toiminto foo() {
$a = 1;
$a = bar($a);
kaiku $a; //$a on 2
}
julkinen toiminto baari($arg) {
$arg = 2;
palata $arg;
}
|
Olen varma, että olet jo kirjoittanut tällaisia menetelmiä. Tässä tapauksessa kyseessä on selkeä virhe. Tietenkin syntaksi on täysin totta, mutta parametrin kopiolle on tehty varaus ja muuttujan purkaminen on tehty. Kalliit muistipuhelut ja tarpeeton käsittelyaika. Alla oleva lomake olisi vastaava, mutta paljon nopeampi.
1
2
3
4
5
6
7
8
9
10
11
|
julkinen toiminto foo() {
$a = 1;
bar($a);
kaiku $a; //$a on 2
}
julkinen toiminto baari(&$arg) {
$arg = 2;
}
|
Täällä teimme täsmälleen saman hoidon, mutta se on yksinkertaisesti nopeampaa ja vähemmän kuluttavaa.
Tämä optimointi on erittäin helppo suorittaa, ja se voi jopa tehdä koodistasi helpommin luettavan. On olemassa useita syntakseja, jotka on tiedettävä, jotta ne voidaan tehdä ohimennen.
- Parametri kulkee viitteellä (jonka olemme juuri nähneet)
- Toiminnon palautus viitteellä (ei yksityiskohtainen tässä, koska PHP-moottori optimoi tämän osan, enkä tuntenut vakuuttavaa voittoa)
- Muuttujan kopioiminen viittauksella, ei kovin hyödyllinen, mutta mahtava. Annoin sinun lukea PHP-dokumentaation, joka on erittäin yksityiskohtainen. Voisit helposti oppia asioita, jotka jätin pois 🙂
Muu optimointi
Kolmen yllä olevan optimoinnin ansiosta sinulla ei pitäisi olla ongelmia käsittelyn nopeuttamisessa. On kuitenkin muitakin kohteita. Luettelon sinulle mielenkiintoisia artikkeleita erilaisista optimoinneista, joita voit tehdä.
- PHP String Concat vs Array Implode
Yhteenveto
Kolme osaa, jotka juuri esitin sinulle, ovat hyvä vaihtoehto minun makuuni optimoida sen skriptit ja välttää kehitystyön uudelleen käynnistämistä nopeampaa kieltä käännettynä kielenä.
Tein nämä kolme siirtoa yhdelle käsikirjoituksestani ja muun muassa onnistuin vähentää muistin kulutusta 3 suunnilleen ja saavuttaa nopeuden lisäys 2:lla.