2012-02-07 9 views
13

Dire, ho una pagina con una galleria fotografica. Ogni miniatura ha per es. una foto, un paese, un autore e così via. Rendo questi oggetti/widget usando i tag modello (che caricano i modelli specificati) - va in questo modo a causa di DRY (io uso questi oggetti/widget separatamente in punti diversi della pagina).Django - il rendering di molti modelli utilizzando templatetags è molto lento

Ed è molto lento.

ho eseguito alcuni profiling usando django-debug-barra degli strumenti:

SQL Queries: default 84.81 ms (147 queries) 

Ma:

Total CPU time: 5768.360 msec 

che è troppo lungo per aspettare.

Dopo alcune analisi si è scoperto che il principale colpevole è la tempistica di enginge.

Quando voglio visualizzare ad es. 150 foto, 600 oggetti associati/widget sono stati resi tramite modelli. Significa 600 operazioni di I/O o anche di più. Lo spostamento di questi widget sul modello principale risolve il problema, ma non mantiene DRY.

Quindi la mia domanda è come si può evitare un simile comportamento? Essere ASCIUTTO e lento o no-ASCIUTTO e veloce? Preferirei essere asciutto e veloce ...

+0

puoi fornire il codice templatetag? –

+0

Il tempo speso per le query è relativamente poco. Ma ORM richiede molto più tempo per generare quelle query e analizzare i risultati in istanze di modello –

risposta

11

Dopo diverse ore di profilazione e ricerca ...

Grazie per il vostro aiuto, ma in questo caso mi sembra che la soluzione migliore finora è quella di utilizzare Template fragment caching:

ho provato e guadagnato il 70-80% delle prestazioni di velocità!

{% load cache %} 
{% cache 3600 mywidget_id %} 
    .. rendered mywidget is cached .. 
{% endcache %} 
10

Si potrebbe desiderare di provare il caching template loader, django.template.loaders.cached.Loader - dovrebbe certamente ridurre la quantità di IO necessario.

Modifica per aggiungere È necessario prestare attenzione assumendo che solo perché la maggior parte del tempo è trascorso nella fase di rendering del modello, che il conteggio delle query non è da incolpare. Non dimenticare che i set di query sono pigri e, a meno che tu non li stia specificatamente tagliando o iterandoli nella vista, verranno valutati solo quando il modello viene caricato. Direi che ridurre il numero di query tramite il buon uso di select_related e altre tecniche dovrebbe essere un aiuto significativo.

+0

Grazie. Lo ha ridotto a 5054.582 msec, che è un guadagno di ~ 11%. Tuttavia, il rendering richiede ancora 5 secondi inaccettabili. Cos'altro potrebbe essere fatto qui? – laszchamachla

+1

Come viene eseguito il caricamento della seconda pagina? –

+0

Lo stesso - 5 sec è un minimo. – laszchamachla

4

Sto assumendo poiché si sta utilizzando la barra degli strumenti di debug che si stanno ottenendo questi numeri in fase di sviluppo. Tuttavia, a causa di ciò, questi non sono numeri "reali".

Il server Django integrato è buono per lo sviluppo, ma ha una serie di difetti che lo rendono molto più lento di un vero webserver. In primo luogo, è single threaded, quindi ciò significa nessuna richiesta parallela. Ciò significa anche che le operazioni di IO sono discrete. Secondo, ha il compito non solo di servire richieste a Django, ma anche di risorse statiche.

Lungo e breve, se si desidera veramente un profilo del sito per i tempi di caricamento della pagina, è necessario installare un server web true localmente. Fondamentalmente impostalo come faresti nel tuo ambiente di produzione. Sarei disposto a scommettere i tempi della richiesta sarà lontano meglio, quindi.

+1

Una nota, dal momento che Django 1.4, runserver è threaded in modo che possa gestire le richieste parallele – Izkata

0

Questo potrebbe non essere applicabile a questo particolare problema, ma in alcuni casi mi ha aiutato a utilizzare select_related nelle query. Potrebbe non essere il caso, ma potrebbe ridurre il numero di query.

+0

select_related non è qualcosa da lanciare in un set di query di 84 ms. Si tratta di modelli. – jstaab

0

Il tempo trascorso per le interrogazioni è relativamente poco. Ma ORM richiede molto più tempo per generare queste query e analizzare i risultati nelle istanze del modello.

Quindi, nonostante l'elevato numero di query l'app è vincolata dalla CPU a causa dell'ORM lento (nel tuo caso potrebbe essere necessario un secondo). Quindi devi comunque ridurre il numero di query.

Molto probabilmente le query vengono eseguite all'interno del tag del modello. Quindi devi ottenere i dati desiderati in alcune query e impostarlo sulle istanze fotografiche.

{% for photo in photos|annotate_comment_count %} 
    ... 
{% endfor %} 

def annotate_comment_count(photo_list): 
    counts = dict(Comment.objects.filter(photo__in=photo_list).values('photo') \ 
           .annotate(count=models.Count('id'))) 
    for photo in photo_list: 
     photo.comments_count = counts[photo.pk] 
    return photo_list 

Quindi, all'interno del vostro templatetag non si deve interrogare contano commenti per una singola foto, ma hai già queste informazioni in attributi comments_count. E hai raggiunto questo in una query.

0

Ho avuto lo stesso problema, e forse per lo stesso motivo. Ho ottimizzato le prestazioni di un tag modello personalizzato. Il numero di richieste db è sceso da 640 a 2 e il tempo db risultante era inferiore a 20 ms. La mia pagina, tuttavia, era diventata più lenta! 7s -> 10s. Sospiro. Ho provato un caricatore di modello memorizzato nella cache, senza effetto.

Avevo rinunciato, disabilitato la barra di debug di django, dopo di che il tempo di risposta è sceso a 1,2 s, incredibile !! Nel mio caso, l'enorme tempo di risposta è stato causato solo dalla barra degli strumenti di debug! Un problema correlato può essere trovato here. Non mi sto immergendo più a fondo in questo problema della barra degli strumenti, poiché stavo pianificando di memorizzare nella cache il tag del modello usando comunque il caching dei frammenti dei template. Durante lo sviluppo ho un tempo di risposta di 10 secondi ogni 15 minuti, con cui posso convivere. Comunque, spero che questo aiuti.