PHP: Hvordan skrive et skript med høy ytelse
Webbyrå » Digitale nyheter » PHP: Hvordan skrive et skript med høy ytelse

PHP: Hvordan skrive et skript med høy ytelse

PHP kan godt være et skriptlignende språk og kritisert for å være tregt, men det er fortsatt et av de mest brukte webserverspråkene. Over tid har de store nettselskapene som bruker PHP søkt å optimalisere motoren. Innføringen av begrepet Søppelmann i versjon 5.3 var et bemerkelsesverdig fremskritt. Men det er mange måter å gjøre det på'optimalisere ytelsen til et skript.

Hovedsakelig brukt innenfor rammen av den enkle genereringen av siden, er disse optimaliseringene, selv om de kan gi en viss gevinst, ikke synlige nok for brukeren. Optimalisering av databaser, webservere med bruk av Nginx, motoroptimalisering med ankomsten av HHVM eller til og med HippyVM vil tillate deg å ganske enkelt øke hastigheten på gjengivelsen av sidene dine og optimalisere tidspunktet for svar på spørsmålene dine på en enklere måte. Til tross for dette utvikler vi noen ganger PHP-skript med det formål å lage tung behandling, eller webservere kan ikke gjøre noe.

I dette innlegget vil jeg detaljere tre områder for optimalisering som jeg nylig brukte på et skript som måtte behandle CSV- eller XLS-filer som inneholder en stor mengde informasjon. Mengden minne som ble brukt nådde 1 GB uten bekymringer og kunne vare i mer enn 1/2 time.

Før jeg presenterer for deg de tre PHP-begrepene som kan tillate degoptimalisere utførelsestiden i tillegg til hvor mye minne som er tatt, vet at før du klager på et X-språk, er algoritmen til skriptet ditt ansvarlig for en god del av behandlingen. C++ kan være uendelig mye raskere enn PHP, men hvis C++-algoritmene dine er dårlige, vil det ikke umiddelbart løse problemene dine.

Dealloker variablene, begrens minneforbruket

Før PHP 5.3 ble utgitt, var PHPs problem overdreven minneforbruk. Dessuten har jeg ofte hørt at minneforbruket til et PHP-skript aldri kunne reduseres... Hvis det var sant en stund, er det heldigvis ikke lenger sant. Denne ledelsen bruker forestillingen om Garbage Collector som vi vil se litt lenger ned.

En god vane tapt...

På noen språk var dette obligatorisk. I PHP har denne forestillingen blitt helt glemt! Denne lille opprinnelige funksjonen unset(), er tilet formidabelt verktøy. Den tilsvarer funksjonen free() i C++ og tillater det deallokere og derfor av frigjør minnet umiddelbart brukt av variabelen. Variabelen er fullstendig ødelagt. Dette frigjør i utgangspunktet PHP fra ubrukte variabler. Dette har en annen interesse, og hjelper oss å strukturere algoritmene våre bedre. Selvfølgelig, når en metode avsluttes, blir variablene automatisk deallokert, men du vil se nedenfor at dette lar deg optimalisere tiden "søppelsamleren" bruker på å jobbe eller bare gjøre jobben sin i tilfelle den blir deaktivert. Det er derfor også en gevinst når det gjelder hastighet. Og tro meg, GC bruker mye ressurser for å frigjøre minne.

Som jeg sa i forrige avsnitt, på slutten av en funksjon eller metode, fjerner PHP ubrukte variabler. Men når det gjelder et skript som behandler massedata, er det mulig for én funksjon å administrere de andre. Som på noen språk, hvis det er en hovedfunksjon, vil du sannsynligvis ha en tendens til å lagre variabler tilbake fra funksjonen før du overfører dem til andre funksjoner. Vi kan fort ende opp med store mengder data. Så snart den ikke lenger er i bruk, er det ingen vits i å "bære" den rundt, vi tildeler det.

Nylig, mens jeg skrev et skript der jeg pakkede ut en hel fil som er større enn 15 MB, slettet jeg den når jeg hadde brukt den og fikk lov til å få minne !

Forstå søppelsamleren

Det var mens jeg undersøkte minnehåndtering jeg kom over en kollegas artikkel. I denne artikkelen som nå er litt gammel, forklarer Pascal Martin nyheten som ikke lenger er en, Søppelsamleren.

Hva er Garbage Collector?

For de som ikke vet det eller som allerede har hørt om det, men aldri har hatt muligheten til å bruke det, er Garbage Collector som er oversatt som "crumb pick-up" på fransk, funksjonalitet på lavt nivå som ved intervall X, stopper den kjørende prosessen, og utfører en skanning av minnet som er tildelt av skriptet for å oppdage hvilke variabler som ikke lenger er tilgjengelige for å slette dem.

Jeg forstår ikke, tidligere ble jeg fortalt at på slutten av en metode ødelegger PHP variablene.
Dette er ikke helt sant. På samme måte som C eller C++ (forelder til PHP), avsluttes PHP på slutten av en metode og mister referansen den hadde på objektet. I C eksisterer bare begrepet peker, i C++ eksisterer begrepene peker og referanse side om side.

Når referansen til objektet har gått tapt, og når søppeloppsamleren starter, vil sistnevnte fastslå at variabelen ikke lenger er tilgjengelig og vil ødelegge den.

Dette konseptet er veldig tilstede i JAVA så vel som i andre nyere språk og følgelig på høyere nivå.

Hva kom med søppelsamleren?

GC ga betydelig utviklingsfleksibilitet. Det er åpenbart at utvikling i C er mer teknisk enn i Java, PHP eller andre. Til tross for dette er noen fasetter nå glemt som minnehåndtering, og det er koden vår som noen ganger føler empati. Hvis dette ga fleksibilitet i utviklingen vår, bremset det også utførelsen av programmene våre.

Med disse verktøyene må vi ikke glemme at vi alltid kan kontrollere minnet på en eller annen måte. Spørsmål om vilje eller nødvendighet...

Til slutt tror jeg at denne typen verktøy er veldig praktiske, men de kan ikke erstatte instruksjonene gitt av utviklerne. For min del finner jeg at C++ forestillingen om shared_ptr er mye mer interessant og kan tilby store ytelsesgevinster i fremtiden hvis Zend bestemmer seg for å bruke denne metoden i noen tilfeller.

Og i PHP?

Siden PHP 5.3 har fire funksjoner blitt lagt til.

  • gc_enable
  • gc_enabled
  • gc_disable
  • gc_collect_cycles
    Jeg lar deg lese dokumentasjonen, men for å være rask lar de deg aktivere GC, vite om den er aktiv, deaktivere den og starte innsamlingen manuelt.

Pascal Martin publiserte i innlegget som jeg linket til deg ovenfor, et script hvor han utfører et testbatteri. Vi kan tydelig se i testrapporten at versjonen uten GC når et enormt minneforbruk, til og med når grensen og krasjer.

benchmark

Her er den utførte koden. Dette er hentet fra Pascal Martins blogg og mer spesielt fra artikkelen hans.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
klasse Node {
offentlig $parentNode;
offentlig $childNodes = matrise();
funksjon Node() {
$ dette->nodeValue = str_repeat('0123456789', 128);
}
}
funksjon skape et forhold() {
$foreldre= nytt node();
$barn = nytt node();
$parent->childNodes[] = $child;
$child->parentNode = $parent;
}
forum ($i = 0; $i 500000; $i++) {
createRelationship();
// Denne delen utføres under den andre testen
if ($options["gc_manual"]) {
gc_collect_cycles();
}
}

Jeg utfører tre tester hver for seg:

  1. Jeg utfører denne testen med de grunnleggende alternativene. Garbage Collector er aktivert og PHP kjører den med jevne mellomrom. Skriptet har varighet 7.55 secondes og minnet som brukes har nådd 20.98 Mo.
  2. Jeg utfører den samme testen ved å deaktivere Garbage Collector og kalle den på hver løkkevending. Manuset varer i 3.79 sekunder og minnet som ble brukt nådde toppen 244.77 KB.
  3. Jeg utfører en tredje test ved å deaktivere søppeloppsamleren og aldri samle manuelt. Minnet må derfor fylles kraftig opp. Manuset varer i 4.46 sekunder og minnet har nådd 1.98 Go.

Med denne testen kan vi tydelig se viktigheten av minnehåndtering. I vårt eksempel, som er tatt til det ekstreme, kan vi tydelig se viktigheten. På den ene siden gjør Garbage Collector mye arbeid med minneforbruk, men det vil ha en tendens til å bremse skriptkjøringen. Vi kan se det veldig godt ved å sammenligne 1. test (med GC) og 3. test uten ledelse.

Der vårt ytelsesarbeid utføres er på test 2. Ingen automatisk minneadministrasjon, manuell administrasjon optimalisert innenfor dette skriptet. Vi klarer å akselerere prosesseringshastigheten med nesten to ganger (7.55s / 3.79s = 1.99). På denne måten, vi begrenset også minnet vårt til 245 KB mot 21 MB for automatisk administrasjon. Det er en koeffisient på nesten 88 ( (20.98 * 1024) / 244.77 = 87.77).

konklusjonen

Selv om eksemplet ovenfor presser minneadministrasjon til det ytterste, er denne testen ment å vise oss hvor mye det kan være. viktig å studere minnehåndtering i skriptene våre. Utbetalingene kan være imponerende i noen tilfeller. Behandlingstid, minne og alt som hører med kan lagres.

Forstå og bruke referanser

Som jeg fortalte deg litt tidligere, implementerer PHP variabel overføring ved referanse, og som jeg fortalte deg ovenfor er denne typen bestått mer eller mindre ekvivalent med passering av peker.

  • Forstå pass-by-referanse
  • Forstå pekeren passerer
    Dette er en viktig del, ikke bare for PHP fordi denne forestillingen eksisterte lenge før, men for IT. I utgangspunktet, når du erklærer en funksjon, gjøres passasjen ved å kopiere.

    // Gå forbi offentlig kopifunksjon foo($arg) {…}

    Det innebærer noen fordeler og ulemper avhengig av koden din. Objektet kopieres, noe som betyr at det tildelte minnet vil øke med vekten av det passerte objektet. Dette kan være latterlig hvis du passerer en boolean, men det kan være mye viktigere hvis du for eksempel passerer en array.

1
2
3
4
5
6
7
8
9
10
11
offentlig funksjon foo() {
$a = 1;
bar($a);
savner $a; //$a er 1
}
offentlig funksjon Bar($arg) {
$arg = 2;
}

I dette tilfellet ser vi at verdien ikke er endret, dette kan være interessant hvis vi bruker variabelen $a som base og vi aldri vil røre verdien. Vi kan her snakke om en forestilling som ikke eksisterer i PHP, om en konstant variabel (const).

1
2
3
4
5
6
7
8
9
10
11
12
offentlig funksjon foo() {
$a = 1;
$a = bar($a);
savner $a; //$a er 2
}
offentlig funksjon Bar($arg) {
$arg = 2;
retur $arg;
}

Jeg er sikker på at du allerede har skrevet metoder som dette. I dette tilfellet er det helt klart en feil. Syntaksen er selvfølgelig helt sann, men det er gjort en allokering for kopien av parameteren, og deallokeringen av en variabel er foretatt. Dyre minneanrop og unødvendig behandlingstid. Skjemaet nedenfor vil være tilsvarende, men mye raskere.

1
2
3
4
5
6
7
8
9
10
11
offentlig funksjon foo() {
$a = 1;
bar($a);
savner $a; //$a er 2
}
offentlig funksjon Bar(&$arg) {
$arg = 2;
}

Her utførte vi nøyaktig samme behandling, men det er rett og slett raskere og mindre forbruker.

Denne optimaliseringen er veldig enkel å utføre og kan til og med gjøre koden din enklere å lese. Det er flere syntakser å vite for å gjøre pass-by-referanse.

  1. Parameter passerer ved referanse (som vi nettopp har sett)
  2. Funksjonsretur ved referanse (ikke detaljert her, fordi PHP-motoren optimaliserer denne delen og jeg følte ingen overbevisende gevinst)
  3. Kopiere en variabel ved referanse, ikke veldig nyttig, men formidabel. Jeg lar deg lese PHP-dokumentasjonen som er ekstremt detaljert. Du kan lett lære ting som jeg har utelatt 🙂

Annen optimalisering

Takket være de tre optimaliseringene ovenfor, bør du ikke ha noe problem med å fremskynde behandlingen. Det er imidlertid andre elementer. Jeg viser deg de interessante artiklene om forskjellige optimaliseringer du kan gjøre.

  • PHP String Concat vs Array Implode

konklusjonen

De tre delene som jeg nettopp har beskrevet for deg, utgjør et godt alternativ for min smak for å optimere skriptene og unngå å starte utviklingen på nytt i en raskere språk som et kompilert språk.

Jeg gjorde disse tre pasningene på et av manusene mine og det klarte jeg blant annet redusere minneforbruket med 3 omtrentlig og oppnå en hastighetsøkning med 2.

★ ★ ★ ★ ★