Posts tagged: performace

Ottimizzare le performance di un sito-web

75 milioni di richieste al giorno, ovvero quasi 900 richieste/secondo, credo sia una immensa soddisfazione per un programmatore web di alto livello, e cioè sviluppare un’applicazione web, fatta bene, che possa soddisfare quei numeri lì. E’ chiaro che un sito web pronto al decollo, non farà mai tutte quelle richieste al giorno, dovrà passare un pò di tempo, in cui dovrà essere pubblicizzato, finire al meglio sui motori di ricerca, e quant’altro. Ma è altrettanto chiaro che, se l’obiettivo, volente o nolente (vedi siti che sono diventati famosi dall’oggi al domani), è quello di cercare di sviluppare un’applicazione web che possa reggere ritmi del genere. Le cose più importanti da capire durante lo sviluppo , se si vogliono ottimizzare le prestazioni web, sono che bisogna gestire le differenti punte di traffico, discriminando tra le operazioni di lettura (READ) che sono molto economiche, e le operazioni si scrittura (WRITE) che sono molto costose, ovviamente in termini di performance. Per operazioni di READ, intendiamo l’accesso a parti statiche che sono state già memorizzate e sono disponibili all’uso immediato. Per operazioni di WRITE intendiamo le operazioni di aggiornamento di un record di un database, o l’inserimento di un record, più in generale consideriamo una qualunque operazioni che comporta un calcolo prima che l’output venga rediretto all’utente.

performance

Lo schema in figura mostra 2 cluster di database (supponendo di utilizzare MYSQL), uno per gestire le richieste di READ, ovvero ogni richiesta dell’utente di visualizzare le pagine. Tutte le pagine sono servite da memCached. Le richieste di WRITE invece, vengono gestire dall’altro cluster, e attraverso la replicazione di MYSQL gli aggiornamenti vengono trasmessi dal cluster di WRITE a quello di READ. Seguono alcuni trick per ottimizzare le prestazioni:

  • READ cluster, per velocizzare usa dei precompilati scritti in C, e non perl/php script.
  • READ cluster, usa UltraDNS per il load balancing.
  • WRITE cluster, minimizza il numero delle query ed il tempo di connessione per gestire le richieste.
  • WRITE cluster, usa il più possibile i processi offline.
  • Usa la replicazione per avere scalabilità e affidabilità del servizio.

l’avreste mai detto che…

java_memory Salve, eccomi qui… non credo che abbia molto pubblico, e chissà quando mi troverete nell’infinito di Internet, ci saranno milioni di blog sulla programmazione, quindi passerà un bel po di tempo prima che mi scoviate…spero nella bontà di gooogle che possa indicizzarmi presto come si deve. Nell’attesa volevo parlare di una cosa che ho letto poco tempo fa, che mi ha fatto notare ancora una volta quanto è potente Java. Vi sottopongo la seguente domanda: "Quale linguaggio di programmazione tra C++ e Java risulta più veloce nell’allocazione della memoria?". Non ci crederete ma la risposta è Java !!! Nelle moderne JVM l’allocazione della memoria risulta essere molto veloce rispetto alle classiche operazioni di malloc() usate in C++. Più precisamente in Java sono richieste 10 istruzioni macchina (approssimativamente) per allocare memoria per la più comune delle allocazioni
new Object() mentre in C++ sono richieste una media di 60-100 istruzioni macchina. Ovviamente non c’è trucco non c’è inganno, nel senso che quelle ottime performance si devono alla gestione della memoria da parte della JVM. La JVM utilizza un garbage collector generazionale, cioè la memoria (l’heap) viene suddiviso in spazi generazionale, uno detto young e l’altro old. In questi due spazi vengono mantenuti i riferimenti agli oggetti presenti nella memoria la cui vita risulta essere rispettivamente corta o lunga, misurata in operazioni di garbage collector. Più precisamente gli oggetti che hanno superato un certo periodo di tempo vengono trasferiti dallo spazio young a quello old e vengono etichettati come long lived. Ad ogni operazioni del garbage collector gli oggetti più vecchi vengono eliminati dalla memoria e quindi viene liberato lo spazio per i nuovi. Sebbene questo è il funzionamento di base della gestione della memoria di Java, la pratica è leggermente diversa, la JVM offre 3 young-generational collectors, che sono serial copying, parallel copying, e parallel scavenge. Tutti e tre hanno la caratteristica di dividere lo spazio di memoria a metà e utilizzano sempre una metà alla volta fino a che non viene riempita. L’allocatore della memoria soddisfa le richieste di allocazione restituendo i primi N bytes della portione di memoria libera, spostando un puntatore che separa le due parti "utilizzata" da "libera". In definitiva, l’allocazione di memoria consiste nel controllare se esiste dello spazio disponibile nell’heap e restituire un puntatore ad esso, senza effettuare alcuna ricerca o adottare alcuna politica di gestione dello spazio. C++ offre al programmatore la scelta di adottare l’allocazione di memoria sia sullo heap che sullo stack. Il fatto di utilizzare lo stack per allocare la memoria implica un aumento delle prestazioni, sia durante l’allocazione che è economica in termini prestazionali e sia durante la deallocazione che costa 0, inoltre i linguaggi implementano una attenta gestione della politica di demarcazione degli oggetti, riducendo così il rischio di non liberare lo spazio al termine della vita di un oggetto. Un altro vantaggio dello stack è dato dal fatto di essere molto più cache-friendly, e questo indica un aumento delle prestazioni in termini di diminuzione dei cache miss, per i quali, sui moderni processori, si hanno dei ritardi significativi. Java non consente al programmatore di allocare spazio direttamente sullo stack, ma questo non è un grosso vincolo, dal momento che la JVM va ad allocare all’occorrenza spazio sullo stack che permette così di aumentare di molto le prestazioni. Le JVMs utilizzano una tecnica chiamata escape analysis la quale permette di stabilire che alcuni oggetti per tutta la durata del loro lifetime, rimarranno confinati all’interno di un singolo thread e pemette di mappare questo lifetime con il lifetime di uno stack-frame. Queste valutazioni permettono l’allocazione di questi oggetti direttamente sullo stack piuttosto che sullo heap, e addirittura per piccoli oggetti la JVM può richiedere l’allocazione direttamente sui registri, aumentando ancor di più le prestazioni. Tutto questo lungo discorso fa intuire quanto l’ottimizzazione da parte della JVM conti e quanto può essere determinante in termini di prestazioni, ovviamente anche il fatto che Java non ha compilati ma byte-coded conta molto. La fonte di questo articolo è IBM se volete approfondire vi consiglio di seguire il link.

WordPress Themes