Follow me on Twitter Facebook Flickr Subscribe Feeds
 

PHP: ottimizzare le nostre applicazioni

This entry was posted on September 14th, 2009 and is filed under php.

Il PHP è sicuramente il linguaggio server-side più utilizzato e famoso al mondo. Probabilmente sta perdendo terreno nell’ultimo periodo, lasciandolo a linguaggi come Ruby e Python però prima che venga surclassato ne passerà di acqua sotto i ponti. La maggior parte dei gestori di hosting offrono piattaforme con Apache-PHP installato, meno diffuse le piattaforme con IIS (per ASP) e Tomcat (JSP e Servlet). Uno dei problemi degli hoster, soprattutto quelli a buon mercato, è la banda che mettono a disposizione; spesso troppo esigua e questo comporta a volte delle lunghe attese di esecuzione di uno script.

Purtroppo possiamo fare poco per ovviare a questo problema, se non cambiare hosting, ma parte di quel poco che possiamo fare per velocizzare i nostri script voglio presentarlo nell’articolo di oggi. In queste righe presento alcuni trucchetti e consigli che consentono di ridurre il tempo (e lo spazio) di esecuzione di uno script.

Il metro di misura utilizzato in questo articolo è stato il seguente codice che restituisce il tempo di esecuzione di uno script:

<?php
$start = microtime(true);

//codice

$end  = microtime(true);
$esito = ($end - $start);
?>

Ovviamente il tempo di esecuzione varia da server a server in base alla versione, alla configurazione hardware ecc ecc però il risultato di tale funzione ha validità quando le operazioni vengono eseguite sulla stessa macchina. Il tempo di esecuzione finale è calcolato però facendo la media su 5 esecuzioni dello stesso script al fine di evitare falsi valori.

Cominciamo intanto dai consigli base.

- echo vs print

Sono ancora molti che usano la funzione print e le sue C-derivate tipo printf, sprintf, ecc. La funzione echo però risulta molto più veloce nella chiamata rispetto alla print ed è sicuramente più dinamica dell’altra. Utilizzata adeguatamente poi, consente di ridurre ancora di più il tempo di esecuzione. Infatti anzichè concatenare le stringhe con il “.” è possibile utilizzare i multi-parametri.

//concatenazione
echo "Ciao " . $nome;

//multi-parametro
echo( "Ciao ", $nome );

La media del tempo di esecuzione è stata:

  • echo: 0.15068
  • print: 0.17624

- non inventare l’acqua calda

E’ già stata scoperta secoli fa! :) Inutile creare funzioni che già sono incluse nel linguaggio. Anche se si è esperti programmatori e si ottimizza al massimo spazio e tempo di esecuzione della funzione, richiamare una funzione propria anzichè una già inclusa nel linguaggio costa comunque spazio e tempo maggiori.

- apostrofi vs apici

Usare, quando possibile l’apostrofo anziche l’apice, risulta, anche se di poco, più veloce. Questo perchè il php cerca all’interno della stringa con apici le variabili, cosa che non succede con gli apostrofi.

  • apostrofo: 0.14066
  • apice: 0.15020

Variabili

- dichiara una variabile prima di utilizzarla

A seconda delle impostazioni del php.ini una variabile non dichiarata e non valorizzata potrebbe causare warning. A prescindere da questo però, utilizzare una variabile che è stata precedentemente dichiarata riduce di molto il tempo di esecuzione di un’operazione.

- utilizza meno variabili possibili

Un codice di questo tipo utilizza il doppio della memoria di cui necessita:

$username = $_GET['user'];
$password = $_GET['pw'];

$result = connect($username, $password);

//oppure
$array = $_SESSION['dati'];
$size_array = sizeof($array);

echo "L'array contiene " . $size_array . " oggetti.";
printr($array);

In fatto di leggibilità dal codice alzo le mani in quanto risulta sicuramente più leggibile in questa maniera però in memoria vengono usate molte più variabili di quelle di cui effettivamente si necessita. Magari in fase di creazione dello script è utile avere codice leggibile ma prima di rilasciare il codice è il caso di eliminare quelle variabili che non servono a nulla.

$result = connect($_GET['user'], $_GET['password']);

//oppure
echo "L'array contiene " . sizeof( $_SESSION['dati'] ) . " oggetti.";
printr( $_SESSION['dati'] );

A conti fatti si utilizza esattamente metà dello spazio in memoria.

Un altro consiglio è riutilizzare la stessa variabile quando non si necessita più del suo contenuto. Ad esempio la variabile che contiene il risultato di una prima query, quando non ci serve più, può contenere il risultato di una seconda query anzichè usarne un’altra.

UPDATE: come mi ha fatto giustamente notare StefanoV, ho omesso una cosa importante. Ottimizzare si, però con la testa. Se usiamo spesso un valore ottenuto tramite diverse chiamate a funzione è ovvio che sia più comodo tenerlo salvato in una variabile, anche se questo comporta l’utilizzo di una variabile in più. Insomma come ho detto nel commento, ottimizzazione si, ammattimento no! Un piccolo esempio per chiare il concetto:

//senza variabile aggiuntiva
if( mysql_num_rows(mysql_query("SELECT * FROM 'table'")) > 100 ) {
   //...codice
   echo mysql_num_rows(mysql_query("SELECT * FROM 'table'"));
} else {
   echo mysql_num_rows(mysql_query("SELECT * FROM 'table'"));
}
//con variabile aggiuntiva
$rows = mysql_num_rows(mysql_query("SELECT * FROM 'table'"));
if( $rows > 100 ) {
   //...codice
   echo $rows;
} else {
   echo $rows;
}

- libera spazio quando possibile

Abbiamo effettuato una query che ha tirato fuori 1.000 risultati dal db e abbiamo copiato il risultato in un’altra variabile. Perchè tenere memorizzati su più spazi di memoria gli stessi dati? Svuotiamo le variabili quando non ne abbiamo più bisogno attraverso la direttiva unset($variabile).

- variabili locali vs variabili globali

Operare su una variabile locale è molto più veloce piuttosto di una globale. Dichiarare una variabile globale in una funzione senza mai utilizzarla rallenta notevolmente l’esecuzione dello script.

- incrementi e decrementi di una variabile

Incrementare, o decrementare una variabile, con ++$i risulta più veloce di $i++ in quanto nell’ultimo caso deve creare una variabile temporanea.

Strutture di controllo e cicli

- operatore ternario vs if … else

Utilizzare l’operatore ternario anzichè una if … then riduce il tempo di esecuzione. Il seguente codice sortisce lo stesso effetto in entrambi i casi ma il tempo di esecuzione è inferiore.

//if ... else
if($i > 0)
 $i += 20;
else
 $i -= 20;

//operatore ternario
($i > 0) ? $i += 20 : $i -= 20;
  • if … else: 0.24080
  • operatore ternario: 0.19073

- switch vs if … elseif …. elseif

Se si hanno molti controlli da fare è più indicato usare uno switch piuttosto che più if annidate. Controllare tutte le if richiede più tempo del costrutto switch.

- cicli

Innanzitutto è buona norma impostare il valore di stop di un ciclo fuori dal ciclo stesso piuttosto che al suo interno.

Poi un altro consiglio è quello di non usare chiamate a funzione per verificare il valore di stop del ciclo. Un esempio per chiarire:

//la sizeof è eseguita ad ogni ciclo quindi c'è spreco di tempo
for( $i=0; $i < sizeof($j); $i++ );

//una sola chiamata a funzione
$end = sizeof($j);
for( $i=0; $i < $end; $i++);

Chiudo qui la prima parte dell’articolo. Preferisco spezzettarlo per lasciare il tempo di assimilare le nozioni per coloro che le hanno viste per la prima volta. Nell’ultima parte mi occuperò dell’ottimizzazione degli array, delle stringhe e della programmazione OOP.

Alla prossima :)

Sommario:

Like this post? Share It! :)
Navigation:
Related Posts:
Comments

7 Responses to “PHP: ottimizzare le nostre applicazioni”

  1. StefanoV ← http://www.sv-design.org/
    September 14, 2009 at 1:43 am

    Complimenti, articolo stupendo!!

    Però non sono daccordo su una cosa… le variabili da usare in moderazione…

    Nel caso che suggerisci tu: $username = $_GET['user']; ok è uno spreco… Ma ti immagini se ogni volta lo script anziche in GET dovesse accedere a cookie o sessioni? e poi magari con funzioni…

    ad esempio se io dovessi inserire un dato piu volte non mi metterei a scrivere piu volte as esempio filtra_stringa(addslashes($_SESSION['nome']));

    piuttosto metterei il tutto in una variabile che riutilizzerei… come per l’appunto fai nell’ultimo esempio dove inserisci sizeof in una variabile $end :)

    Attendo con impazienza la seconda parte, complimenti ;)

  2. Simone D'Amico ← http://www.simonedamico.it
    September 14, 2009 at 7:53 am

    Forse $_GET['user'] non era proprio l’esempio più adatto, soprattutto considerando che sono le variabili su cui lavorare molto tra controlli, regex, ecc.

    Però ho visto codice di persone che, dopo 2mila righe mi venivano a dire: “ci vuole troppo tempo che mi si carica la pagina”.
    Dando una semplice, semplicissima occhiata, vedevo che lo stesso valore lo salvavano venti volte in variabili diverse. Finchè è una stringa di 10caratteri ok, sono 10byte e non muore nessuno; ma usando array o strutture dati più avanzate, query, ecc le cose si complicano e di molto!

    In effetti son d’accordo con te, non mi sono spiegato proprio nel migliore dei modi. Ottimizzazione si, ammattimento no :)

    Usare una variabile spreca spazio ma risparmi in tempo di coding, quindi è pur sempre ottimizzazione :)

  3. Giovanni ← http://www.quacos.com
    September 14, 2009 at 4:47 pm

    Ottimo articolo per migliorare il proprio codice!

  4. Simone D'Amico ← http://www.simonedamico.it
    September 14, 2009 at 5:41 pm

    Grazie…mi fa piacere che sia stato gradito l’articolo :)

  5. yesWEBcan ← http://www.yeswebcan.it
    September 16, 2009 at 7:36 pm

    Alla faccia dell’articolo che hai scritto….
    Complimenti, utilissimo

  6. anonimo
    May 14, 2010 at 1:47 am

    non sono del tutto d’accordo… switch anche se più intuitivo non è più veloce della corrispettiva struttura if…else…
    la stessa cosa con operatore ternario…
    per il resto va bene…

  7. Simone D'Amico ← http://simonedamico.com
    May 14, 2010 at 9:54 am

    Ciao anonimo e benvenuto sul blog.

    Ti ringrazio per l’intervento ed ho fatto una breve ricerca in rete per confrontare i risultati dei miei test.

    Posso consigliarti delle letture dove test eseguiti dimostrano che lo switch risulta uguale o il 25% più efficiente del costrutto if..else:
    http://www.php.lt/benchmark/phpbench.php
    http://sun3.org/archives/88

    Riguardo l’operatore ternario ho trovato solo un articolo online che afferma essere circa il 3% più lento (http://john.mcclumpha.org/php/PHP_speed_comparison_if_then_vs_ternary_operator/) a differenza dei test da me effettuati. Non ho trovato altre informazioni al riguardo. Finchè si tratta di valori così bassi resto anche io, come l’autore, dell’idea che è più vantaggioso in molti casi utilizzarlo al posto di un costrutto if. Almeno sul risparmio di tempo nella scrittura del codice e nella chiarezza sarai d’accordo con me è la scelta più adatta in determinati casi.

    Per sicurezza ho rieffettuato il test che feci al tempo dell’articolo e, i tempi in media, sono restati molto simili. E’ cambiato il PC su cui ho effettuato i test e la distribuzione su cui è installato PHP quindi bisogna tenere conto anche di questo. I risultati sono stati leggermente più bassi per entrambi, sicuramente perchè più performante il server. Posso affermare però che il gap tra i due si è ridotto da 0.5 a 0.2. Se hai qualche lettura oppure informazioni al riguardo sei il benvenuto. :)

Leave a Reply