PHP: Hogyan írjunk nagy teljesítményű szkriptet
Webügynökség » Digitális hírek » PHP: Hogyan írjunk nagy teljesítményű szkriptet

PHP: Hogyan írjunk nagy teljesítményű szkriptet

Lehet, hogy a PHP egy script-szerű nyelv, amelyet lassúsága miatt kritizálnak, de továbbra is az egyik legszélesebb körben használt webszerver-nyelv. Idővel a PHP-t használó nagy webcégek igyekeztek optimalizálni a motort. A fogalom bevezetése Szemetes az 5.3-as verzióban jelentős előrelépés volt. De sok módja van ennek'optimalizálja a szkript teljesítményét.

Főleg az egyszerű oldalgenerálás keretében használatos, ezek az optimalizálások, bár hozhatnak bizonyos hasznot, nem eléggé láthatóak a felhasználó számára. Az adatbázisok, webszerverek optimalizálása az Nginx használatával, a motorok optimalizálása a HHVM vagy akár a HippyVM megjelenésével lehetővé teszi, hogy egyszerűen felgyorsítsa az oldalak megjelenítését, és egyszerűbb módon optimalizálja a kérdések megválaszolásának idejét . Ennek ellenére néha fejlesztünk PHP szkripteket abból a célból, hogy készítsünk nehéz kezelés, vagy a webszerverek nem tudnak mit tenni.

Ebben a bejegyzésben részletezem az optimalizálás három területe amelyet nemrégiben alkalmaztam egy szkriptre, amelynek nagy mennyiségű információt tartalmazó CSV vagy XLS fájlokat kellett feldolgoznia. A felhasznált memória mennyisége gond nélkül elérte az 1 GB-ot, és több mint 1/2 órát bírhat.

Mielőtt bemutatnám a három PHP-fogalmat, amelyek lehetővé teszikoptimalizálja a végrehajtási időt valamint az elfoglalt memória mennyiségét, tudd, hogy mielőtt panaszkodsz egy X nyelv miatt, a szkripted algoritmusa felelős a feldolgozás jó részéért. Lehet, hogy a C++ végtelenül gyorsabb, mint a PHP, de ha rosszak a C++ algoritmusaid, nem oldja meg azonnal a problémáidat.

Oldja fel a változóit, korlátozza a memóriafelhasználást

A PHP 5.3 megjelenése előtt a PHP problémája az volt túlzott memóriafogyasztás. Emellett gyakran hallottam, hogy egy PHP szkript memóriafogyasztását soha nem lehet csökkenteni… Ha ez egy ideig igaz volt, akkor szerencsére már nem. Ez a menedzsment a szemétgyűjtő fogalmát használja, amit egy kicsit lejjebb fogunk látni.

Egy jó szokás elveszett...

Egyes nyelveken ez kötelező volt. A PHP-ben ez a fogalom teljesen feledésbe merült! Ez a kis unset(), natív függvény a tofélelmetes hasznosság. Egyenértékű a C++ free() függvényével, és lehetővé teszi felszabadítani és ezért a azonnal felszabadítja a memóriát változó használja. A változó teljesen megsemmisült. Ez kezdetben felszabadítja a PHP-t a nem használt változóktól. Ennek van egy másik érdeke is, amely segít algoritmusaink jobb felépítésében. Természetesen, amikor egy metódus véget ér, a változók automatikusan feloldódnak, de az alábbiakban látni fogjuk, hogy ez lehetővé teszi, hogy optimalizálja azt az időt, amit a "szemétgyűjtő" munkával vagy egyszerűen a munkájával tölt abban az esetben, ha deaktiválják. Létezik tehát az is gyorsaság szempontjából. És hidd el, a GC sok erőforrást fogyaszt a memória felszabadításához.

Ahogy az előző bekezdésben mondtam, a PHP egy függvény vagy metódus végén eltávolítja a nem használt változókat. De tömegadatokat feldolgozó szkript esetén lehetséges, hogy az egyik funkció adminisztrálja a többit. Egyes nyelvekhez hasonlóan, ha ez egy fő függvény, valószínűleg hajlamos lesz a változókat visszamenteni a függvényből, mielőtt átadná őket más függvényeknek. Gyorsan nagy mennyiségű adathoz juthatunk. Amint már nem használják, nincs értelme "cipelni" szétosztjuk.

Nemrég egy szkript írásakor, amelyben egy 15 MB-nál nagyobb fájlt bontottam ki, miután felhasználtam, töröltem és engedélyeztem emléket szerezni !

A szemétgyűjtő megértése

A memóriakezelés kutatása közben bukkantam egy kolléga cikkére. Ebben a mára kissé régi cikkben Pascal Martin ismerteti az újdonságot, amely már nem az, a szemétgyűjtőt.

Mi az a szemétgyűjtő?

Azok számára, akik nem ismerik, vagy már hallottak róla, de soha nem volt lehetőségük használni, a Garbage Collector, amelyet franciául "morzsafelszedőnek" fordítanak, alacsony szintű funkcionalitás amely az X időközönként leállítja a futó folyamatot, és elvégzi a szkript által lefoglalt memória vizsgálatát, hogy megállapítsa, mely változók már nem érhetők el a törléshez.

Nem értem, korábban azt mondták nekem, hogy egy metódus végén a PHP megsemmisíti a változókat.
Ez nem teljesen igaz. Ugyanúgy, mint a C vagy a C++ (a PHP szülője), egy metódus végén a PHP kilép, és elveszti az objektumon lévő hivatkozást. C-ben csak a mutató fogalma létezik, a C++-ban a mutató és a hivatkozás fogalma együtt létezik.

Miután az objektumra való hivatkozás elveszett, és amint a szemétgyűjtő elindul, az utóbbi megállapítja, hogy a változó már nem érhető el, és megsemmisíti azt.

Ez a fogalom nagyon jelen van a JAVA-ban és más újabb nyelveken, következésképpen magasabb szinten.

Mit hozott a Szemétgyűjtő?

A GC jelentős fejlesztési rugalmasságot biztosított. Nyilvánvaló, hogy a C-ben való fejlesztés technikaibb, mint a Java-ban, PHP-ben vagy másokban. Ennek ellenére néhány aspektus mára feledésbe merült, mint például a memóriakezelés, és a kódunk az, ami néha együttérző. Ha ez rugalmasságot hozott a fejlesztésünkben, az lelassította a programjaink végrehajtását is.

Ezekkel az eszközökkel nem szabad elfelejtenünk, hogy a memóriát mindig tudjuk valamilyen módon szabályozni. Akarat vagy szükség kérdése…

Végül úgy gondolom, hogy az ilyen típusú eszközök nagyon praktikusak, de nem helyettesíthetik a fejlesztők utasításait. A magam részéről úgy látom, hogy a C++ fogalma shared_ptr sokkal érdekesebb, és túlméretezett teljesítménynövekedést kínálhat a jövőben, ha a Zend úgy dönt, hogy bizonyos esetekben ezt a módszert használja.

És PHP-ben?

A PHP 5.3 óta négy funkció került hozzáadásra.

  • gc_enable
  • gc_enabled
  • gc_disable
  • gc_collect_cycles
    Meghagyom a szabadidőt a dokumentáció elolvasására, de a gyorsaság kedvéért lehetővé teszik, hogy aktiválja a GC-t, tudja, hogy aktív-e, deaktiválja, és manuálisan indítsa el a gyűjtést.

Pascal Martin az általam fentebb linkelt bejegyzésben közzétett egy forgatókönyvet, ahol teszt akkumulátort végez. A tesztjelentésben jól látható, hogy a GC nélküli verzió óriási memóriafogyasztást ér el, még a határértéket is eléri és összeomlik.

benchmark

Itt van a végrehajtott kód. Ezt Pascal Martin blogjáról, pontosabban az ő cikkéből vettük át.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
osztály Csomópont {
nyilvános $parentNode;
nyilvános $childNodes = sor,
funkció Csomópont() {
$ ezt->nodeValue = str_repeat(„0123456789”, 128);
}
}
funkció hozzon létre kapcsolatot() {
$szülő = új csomópont();
$gyerek = új csomópont();
$szülő->gyermekCsomópontok[] = $gyermek;
$gyermek->szülőcsomópont = $szülő;
}
mert ($i = 0; $i 500000; $i++) {
createRelationship();
// Ez a rész a második teszt során kerül végrehajtásra
if ($options["gc_manual"]) {
gc_collect_cycles();
}
}

Három tesztet végzek külön:

  1. Ezt a tesztet az alapvető opciókkal hajtom végre. A Garbage Collector engedélyezve van, és a PHP rendszeresen futtatja. A szkriptnek van időtartama 7.55 secondes és a használt memória elérte 20.98 Mo.
  2. Ugyanezt a tesztet úgy hajtom végre, hogy letiltom a szemétgyűjtőt, és minden hurokfordulatnál meghívom. A forgatókönyv 3.79 másodpercig tart és a használt memória tetőzött 244.77 KB.
  3. A harmadik tesztet úgy hajtom végre, hogy letiltom a szemétgyűjtőt, és soha nem gyűjtök kézzel. A memóriának ezért erősen fel kell telnie. A forgatókönyv 4.46 másodpercig tart és az emlék elérte 1.98 Go.

Ezzel a teszttel tisztán láthatjuk a memóriakezelés fontosságát. A végletekig vitt példánkon jól látható a fontosság. Egyrészt a Garbage Collector sokat dolgozik a memóriafelhasználáson, de ez lelassítja a szkriptek végrehajtását. Nagyon jól láthatjuk, ha összehasonlítjuk az 1. tesztet (GC-vel) és a 3. menedzsment nélküli tesztet.

Ahol a teljesítményt a 2. teszten végezzük. Nincs automatikus memóriakezelés, optimalizált kézi kezelés ezen a szkripten belül. A feldolgozási sebességet közel kétszeresére sikerül felgyorsítani (7.55 s / 3.79 s = 1.99). Ily módon memóriánkat is 245 KB-ra korlátoztuk 21 MB ellenében az automatikus kezeléshez. Ez egy majdnem 88 (20.98 * 1024) / 244.77 = 87.77 együttható.

Következtetés

Bár a fenti példa a végletekig feszegeti a memóriakezelést, ennek a tesztnek az a célja, hogy megmutassa, mennyi lehet. fontos a memóriakezelés tanulmányozása szkriptjeinkben. A kifizetések bizonyos esetekben lenyűgözőek lehetnek. A feldolgozási idő, a memória és minden, ami vele jár, megtakarítható.

A hivatkozások megértése és használata

Ahogy egy kicsit korábban mondtam, a PHP a változók átadását referencia alapján valósítja meg, és ahogy fentebb mondtam, ez a fajta átadás többé-kevésbé egyenértékű a mutató szerinti átadással.

  • Az áthaladás megértése
  • A mutató átadásának megértése
    Ez nem csak a PHP számára fontos, mert ez a fogalom már régen létezett, hanem az IT számára is. Alapvetően, amikor deklarálunk egy függvényt, az átmenet másolással történik.

    // Nyilvános másolási függvény átadása foo($arg) {…}

    Ez azt jelenti néhány pro és kontra kódjától függően. Az objektum átmásolódik, ami azt jelenti, hogy a lefoglalt memória az átadott objektum súlyával nő. Ez nevetséges lehet, ha átadunk egy logikai értéket, de sokkal fontosabb lehet, ha például egy tömböt adunk át.

1
2
3
4
5
6
7
8
9
10
11
nyilvános funkció ize() {
$a = 1;
bar($a);
visszhang $a; //$a az 1
}
nyilvános funkció bár($arg) {
$arg = 2;
}

Ebben az esetben azt látjuk, hogy az érték nem módosult, ez akkor lehet érdekes, ha az $a változót használjuk alapnak, és soha nem akarunk hozzányúlni az értékéhez. Beszélhetnénk itt egy PHP-ben nem létező fogalomról, egy állandó változóról (const).

1
2
3
4
5
6
7
8
9
10
11
12
nyilvános funkció ize() {
$a = 1;
$a = bar($a);
visszhang $a; //$a az 2
}
nyilvános funkció bár($arg) {
$arg = 2;
visszatérés $arg;
}

Biztosan írtál már ilyen módszereket. Ebben az esetben egyértelműen tévedésről van szó. Természetesen a szintaxis teljesen igaz, de a paraméter másolásához került egy allokáció, és egy változó felosztása megtörtént. Drága memóriahívások és felesleges feldolgozási idő. Az alábbi űrlap egyenértékű lenne, de sokkal gyorsabb.

1
2
3
4
5
6
7
8
9
10
11
nyilvános funkció ize() {
$a = 1;
bar($a);
visszhang $a; //$a az 2
}
nyilvános funkció bár(&$arg) {
$arg = 2;
}

Itt pontosan ugyanazt a kezelést hajtottuk végre, de egyszerűen gyorsabb és kevésbé fogyaszt.

Ez az optimalizálás nagyon egyszerűen végrehajtható, és még könnyebben is olvashatóvá teheti a kódot. Számos szintaxist ismerni kell az áthaladó hivatkozás elvégzéséhez.

  1. Paraméter átadás referencia alapján (amit most láttunk)
  2. A függvény visszaadása hivatkozással (itt nem részletezzük, mert a PHP motor optimalizálja ezt a részt, és nem éreztem meggyőző nyereséget)
  3. Változó másolása hivatkozással, nem túl hasznos, de félelmetes. Elolvastam a PHP dokumentációt, amely rendkívül részletes. Könnyen megtanulhatnál olyan dolgokat, amiket kihagytam 🙂

Egyéb optimalizálás

A fenti három optimalizálásnak köszönhetően nem okozhat gondot a feldolgozás felgyorsítása. Vannak azonban más elemek is. Felsorolom az érdekes cikkeket a különféle optimalizálásokról, amelyeket elvégezhet.

  • PHP String Concat vs Array Implode

Következtetés

Az imént bemutatott három rész jó alternatívát jelent az én ízlésemnek a szkriptek optimalizálására és a fejlesztések újraindításának elkerülésére. gyorsabb nyelv összeállított nyelvként.

Ezt a három lépést megcsináltam az egyik forgatókönyvemen, és többek között sikerült is csökkentse a memóriafogyasztást 3-mal hozzávetőlegesen és sebességnövekedést elérni 2-vel.

★ ★ ★ ★ ★