PHP: Si të shkruani një skenar me performancë të lartë
Ueb agjenci » Lajme dixhitale » PHP: Si të shkruani një skenar me performancë të lartë

PHP: Si të shkruani një skenar me performancë të lartë

PHP mund të jetë një gjuhë e ngjashme me skriptin dhe e kritikuar për të qenë e ngadaltë, por ajo mbetet një nga gjuhët më të përdorura të serverëve të uebit. Me kalimin e kohës, kompanitë kryesore të internetit që përdorin PHP kanë kërkuar të optimizojnë motorin. Prezantimi i konceptit të Mbledhësi i mbeturinave në versionin 5.3 ishte një përparim i dukshëm. Por ka shumë mënyra për të'optimizon performancën e një skenari.

Të përdorura kryesisht në kontekstin e gjenerimit të thjeshtë të faqeve, këto optimizime, megjithëse mund të sjellin një përfitim të caktuar, nuk janë mjaftueshëm të dukshme për përdoruesin. Optimizimi i bazave të të dhënave, serverëve në internet me përdorimin e Nginx, optimizimi i motorëve me ardhjen e HHVM apo edhe HippyVM do t'ju lejojë thjesht të shpejtoni paraqitjen e faqeve tuaja dhe të optimizoni kohën e përgjigjeve për pyetjet tuaja në një mënyrë më të thjeshtë . Pavarësisht kësaj, ne ndonjëherë zhvillojmë skriptet PHP me qëllimin e krijimit trajtim i rëndë, ose serverët e uebit nuk mund të bëjnë asgjë.

Në këtë postim do të detajoj tre fusha të optimizimit të cilin kohët e fundit e aplikova në një skript që duhej të përpunonte skedarë CSV ose XLS që përmbajnë një sasi të madhe informacioni. Sasia e memories së përdorur arriti 1 GB pa shqetësime dhe mund të zgjasë më shumë se 1/2 orë.

Përpara se t'ju prezantoj tre nocionet PHP që mund t'ju lejojnëoptimizoni kohën e ekzekutimit si dhe sasia e memories së marrë, dijeni se përpara se të ankoheni për një gjuhë X, algoritmi i skriptit tuaj është përgjegjës për një pjesë të mirë të përpunimit tuaj. C++ mund të jetë pafundësisht më i shpejtë se PHP, por nëse algoritmet tuaja C++ janë të këqija, nuk do t'i zgjidhë menjëherë problemet tuaja.

Shpërndani variablat e tij, kufizoni konsumin e memories

Para se të lëshohej PHP 5.3, problemi i PHP ishte konsumi i tepërt i kujtesës. Përveç kësaj, kam dëgjuar shpesh se konsumi i memories së një skripti PHP nuk mund të reduktohet kurrë… Nëse kjo ishte e vërtetë për një kohë, për fat të mirë nuk është më e vërtetë. Ky menaxhim përdor nocionin e Mbledhësit të mbeturinave që do ta shohim pak më poshtë.

Një zakon i mirë i humbur...

Në disa gjuhë, kjo kërkohej. Në PHP, ky nocion është harruar plotësisht! Ky funksion i vogël vendas unset(), është tënjë dobi e frikshme. Është ekuivalent me funksionin free() në C++ dhe lejon shpërndaj dhe për këtë arsye të lironi menjëherë kujtesën përdoret nga ndryshorja. Variabli është shkatërruar plotësisht. Kjo fillimisht çliron PHP nga variablat e papërdorur. Ky ka një interes tjetër, duke na ndihmuar të strukturojmë më mirë algoritmet tona. Sigurisht, kur një metodë përfundon, variablat shpërndahen automatikisht, por do të shihni më poshtë se kjo ju lejon të optimizoni kohën që "mbledhësi i mbeturinave" kalon duke punuar ose thjesht duke bërë punën e tij në rast se çaktivizohet. Prandaj ka edhe një fitim për sa i përket shpejtësisë. Dhe më besoni, GC konsumon shumë burime për të çliruar kujtesën.

Siç thashë në paragrafin e mëparshëm, në fund të një funksioni ose metode, PHP heq variablat e papërdorura. Por në rastin e një skripti që përpunon të dhëna masive, është e mundur që një funksion të administrojë të tjerët. Ashtu si në disa gjuhë, nëse është një funksion kryesor, ndoshta do të prireni të ruani variablat nga funksioni përpara se t'i kaloni ato në funksione të tjera. Mund të përfundojmë shpejt me një sasi të madhe të dhënash. Sapo nuk përdoret më, nuk ka kuptim ta "mbash" ne e shpërndajmë atë.

Kohët e fundit, duke shkruar një skript ku po nxirrja një skedar të tërë më të madh se 15 MB, pasi e kisha përdorur, e fshiva dhe lejova të fitoni memorie !

Kuptimi i Mbledhësit të Plehrave

Ishte duke hulumtuar për menaxhimin e kujtesës që hasa në artikullin e një kolegu. Në këtë artikull i cili tashmë është pak i vjetër, Pascal Martin shpjegon risinë që nuk është më një, Mbledhësi i mbeturinave.

Çfarë është mbledhësi i mbeturinave?

Për ata që nuk e dinë ose që kanë dëgjuar tashmë për të, por nuk kanë pasur kurrë mundësi ta përdorin, Mbledhësi i mbeturinave që në frëngjisht përkthehet si "marrje thërrimesh", funksionalitet të nivelit të ulët i cili në intervalin X, ndalon procesin e ekzekutimit dhe kryen një skanim të memories së caktuar nga skripti për të zbuluar se cilat variabla nuk janë më të aksesueshme për t'i fshirë ato.

Nuk e kuptoj, më herët më thanë se në fund të një metode PHP shkatërron variablat.
Kjo nuk është plotësisht e vërtetë. Në të njëjtën mënyrë si C ose C++ (prind i PHP), në fund të një metode, PHP del dhe humbet referencën që kishte në objekt. Në C ekziston vetëm nocioni pointer, në C++ nocionet pointer dhe referencë bashkëjetojnë.

Pasi të jetë humbur referenca për objektin dhe pasi të fillojë Mbledhësi i mbeturinave, ky i fundit do të përcaktojë që ndryshorja nuk është më e aksesueshme dhe do ta shkatërrojë atë.

Ky koncept është shumë i pranishëm në JAVA si dhe në gjuhë të tjera më të reja dhe rrjedhimisht të nivelit më të lartë.

Çfarë solli Mbledhësi i Plehrave?

GC ofroi fleksibilitet të konsiderueshëm zhvillimi. Është e qartë se zhvillimi në C është më teknik sesa në Java, PHP apo të tjera. Pavarësisht kësaj, disa aspekte tani janë harruar si menaxhimi i kujtesës dhe është kodi ynë ai që ndonjëherë ndjen empati. Nëse kjo solli fleksibilitet në zhvillimin tonë, ajo gjithashtu ngadalësoi ekzekutimin e programeve tona.

Me këto mjete, ajo që nuk duhet të harrojmë është se ne gjithmonë mund ta kontrollojmë kujtesën në një farë mënyre. Çështje vullneti apo nevoje…

Së fundi, unë mendoj se këto lloj mjetesh janë shumë praktike, por ato nuk mund të zëvendësojnë udhëzimet e dhëna nga zhvilluesit. Nga ana ime, konstatoj se nocioni C++ i shared_ptr është shumë më interesante dhe mund të ofrojë përfitime të mëdha të performancës në të ardhmen nëse Zend vendos të përdorë këtë metodë në disa raste.

Dhe në PHP?

Që nga PHP 5.3, janë shtuar katër funksione.

  • gc_enable
  • gc_enabled
  • gc_disable
  • gc_collect_ciklet
    Ju lë kohën e lirë të lexoni dokumentacionin, por për të qenë të shpejtë, ju lejojnë të aktivizoni GC, të dini nëse është aktiv, ta çaktivizoni dhe të filloni manualisht koleksionin.

Pascal Martin publikoi në postimin që ju lidha më sipër, një skenar ku kryen një bateri testuese. Mund të shohim qartë në raportin e provës se versioni pa GC arrin konsum të madh të memories, madje duke arritur kufirin dhe duke u rrëzuar.

Etapë

Këtu është kodi i ekzekutuar. Kjo është marrë nga blogu i Pascal Martin dhe veçanërisht nga artikulli i tij.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
klasë Nyjë {
publik $parentNode;
publik $childNodes = grup();
funksion Nyjë() {
$ kjo->nodeValue = str_repeat('0123456789', 128);
}
}
funksion krijoj Marrëdhënie() {
$prind = i ri nyja ();
$fëmijë = i ri nyja ();
$parent->childNodes[] = $child;
$child->parentNode = $prind;
}
për ($i = 0; $i 500000; $i++) {
krijimi Relationship();
// Kjo pjesë ekzekutohet gjatë testit të dytë
if ($opsione["gc_manual"]) {
gc_mbledhja_ciklet();
}
}

Unë kryej tre teste veç e veç:

  1. Unë e kryej këtë test me opsionet bazë. Mbledhësi i mbeturinave është aktivizuar dhe PHP e drejton atë rregullisht. Skenari ka kohëzgjatje sekonda 7.55 dhe memoria e përdorur ka arritur 20.98 Mo.
  2. Unë kryej të njëjtin test duke çaktivizuar Mbledhësin e mbeturinave dhe duke e thirrur atë në çdo rrotullim. Skenari zgjat 3.79 sekonda dhe kujtesa e përdorur arriti kulmin në 244.77 Ko.
  3. Unë kryej një test të tretë duke çaktivizuar Mbledhësin e mbeturinave dhe duke mos e mbledhur kurrë manualisht. Prandaj, kujtesa duhet të mbushet fort. Skenari zgjat 4.46 sekonda dhe kujtesa ka arritur 1.98 Go.

Me këtë test, ne mund të shohim qartë rëndësinë e menaxhimit të kujtesës. Në shembullin tonë, i cili është marrë në ekstrem, ne mund të shohim qartë rëndësinë. Nga njëra anë, Mbledhësi i mbeturinave bën shumë punë në konsumin e memories, por kjo do të tentojë të ngadalësojë ekzekutimin e skenarit. Mund ta shohim shumë mirë duke krahasuar testin e 1-rë (me GC) dhe testin e 3-të pa menaxhim.

Aty ku kryhet puna jonë e performancës është në testin 2. Nuk ka menaxhim automatik të kujtesës, menaxhimi manual është i optimizuar brenda këtij skenari. Ne arrijmë të përshpejtojmë shpejtësinë e përpunimit pothuajse dy herë (7.55 s / 3.79 s = 1.99). Në këtë mënyrë, ne gjithashtu kufizuam memorien tonë në 245 KB kundrejt 21 MB për menaxhim automatik. Ky është një koeficient prej pothuajse 88 ((20.98 * 1024) / 244.77 = 87.77).

Përfundim

Megjithëse shembulli i mësipërm e shtyn menaxhimin e kujtesës në kufi, ky test synon të na tregojë se sa mund të jetë. e rëndësishme për të studiuar menaxhimin e kujtesës në skriptet tona. Pagesat mund të jenë mbresëlënëse në disa raste. Koha e përpunimit, memoria dhe gjithçka që shkon me të mund të ruhen.

Kuptoni dhe përdorni referencat

Siç ju thashë pak më herët, PHP zbaton kalimin e ndryshoreve me referencë dhe siç ju thashë më lart ky lloj kalimi është pak a shumë i barabartë me kalimin me tregues.

  • Kuptimi i referencës kalimtare
  • Kuptimi i kalimit të treguesit
    Kjo është një pjesë e rëndësishme, jo vetëm për PHP sepse ky nocion ka ekzistuar shumë kohë më parë, por për IT. Në thelb, kur deklaroni një funksion, kalimi bëhet duke kopjuar.

    // Kaloni nga funksioni i kopjimit publik foo($arg) {…}

    Kjo nënkupton disa të mirat dhe të këqijat në varësi të kodit tuaj. Objekti kopjohet, që do të thotë se memoria e alokuar do të rritet me peshën e objektit të kaluar. Kjo mund të jetë qesharake nëse kaloni një boolean, por mund të jetë shumë më e rëndësishme nëse kaloni një grup për shembull.

1
2
3
4
5
6
7
8
9
10
11
publik funksion foo() {
$a = 1;
bar ($a);
humbas $a; //$a është 1
}
publik funksion bar($arg) {
$arg = 2;
}

Në këtë rast, shohim që vlera nuk është modifikuar, kjo mund të jetë interesante nëse përdorim variablin $a si bazë dhe nuk duam të prekim kurrë vlerën e saj. Këtu mund të flasim për një nocion që nuk ekziston në PHP, për një ndryshore konstante (const).

1
2
3
4
5
6
7
8
9
10
11
12
publik funksion foo() {
$a = 1;
$a = bar($a);
humbas $a; //$a është 2
}
publik funksion bar($arg) {
$arg = 2;
kthim $arg;
}

Unë jam i sigurt që ju keni shkruar tashmë metoda të tilla. Në këtë rast, është padyshim një gabim. Sigurisht, sintaksa është plotësisht e vërtetë, por është bërë një ndarje për kopjen e parametrit dhe është bërë shpërndarja e një ndryshoreje. Thirrje të shtrenjta memorie dhe kohë të panevojshme përpunimi. Formulari i mëposhtëm do të ishte ekuivalent, por shumë më i shpejtë.

1
2
3
4
5
6
7
8
9
10
11
publik funksion foo() {
$a = 1;
bar ($a);
humbas $a; //$a është 2
}
publik funksion bar(&$arg) {
$arg = 2;
}

Këtu kemi kryer saktësisht të njëjtin trajtim, por është thjesht më i shpejtë dhe më pak konsumues.

Ky optimizim është shumë i thjeshtë për t'u kryer dhe madje mund ta bëjë kodin tuaj më të lehtë për t'u lexuar. Ka disa sintaksa për të ditur të bëni referencë kalimtare.

  1. Parametri kalon me referencë (që sapo e kemi parë)
  2. Kthimi i funksionit me referencë (nuk detajohet këtu, sepse motori PHP e optimizon këtë pjesë dhe nuk ndjeva ndonjë fitim bindës)
  3. Kopjimi i një ndryshoreje me referencë, jo shumë i dobishëm por i frikshëm. Unë ju lejoj të lexoni dokumentacionin PHP i cili është jashtëzakonisht i detajuar. Mund të mësoni lehtësisht gjërat që i kam lënë pas 🙂

Optimizimi të tjera

Falë tre optimizimeve të mësipërme, nuk duhet të keni asnjë problem për të shpejtuar përpunimin tuaj. Megjithatë, ka artikuj të tjerë. Unë ju listoj artikujt interesantë për optimizime të ndryshme që mund të bëni.

  • PHP String Concat vs Array Implode

Përfundim

Tre pjesët që sapo ju kam detajuar përbëjnë një alternativë të mirë për shijen time për të optimizuar skriptet e saj dhe për të shmangur rifillimin e zhvillimeve në një gjuhë më të shpejtë si gjuhë e përpiluar.

Këto tre kalime i bëra në një nga skenarët e mi dhe, ndër të tjera, ia dola zvogëloni konsumin e kujtesës me 3 përafërsisht dhe të arrijë një rritje të shpejtësisë me 2.

★ ★ ★ ★ ★