2012-06-11 18 views
10

L'app su cui sto lavorando risponde alla maggior parte delle richieste con oggetti JSON o loro raccolte. Stiamo usando Jbuilder per costruire quelle risposte. La quantità di dati resi è abbastanza grande (diverse migliaia di oggetti in varie strutture nidificate - una volta formattati e completamente espansi, ci sono ben 10.000 linee di JSON per una risposta tipica). Questo rendering richiede molto tempo, circa 1/3 del tempo di richiesta totale, secondo NewRelic.Miglioramento delle prestazioni di rendering con Jbuilder e Rails 3

Sto cercando una guida, una serie di suggerimenti o altre risorse che mi consentano di ottenere le migliori prestazioni possibili da JBuilder. Sono anche curioso di sapere se sono disponibili confronti delle prestazioni per Jbuilder vs. RABL o altri strumenti simili.

Modifica: ho trovato un GitHub Issue che si lamenta delle prestazioni di Jbuilder, ma l'unico suggerimento effettivo che qualcuno ha fatto è 'non usare Jbuilder'. Beh, in realtà, hanno usato un linguaggio leggermente più forte, ma non c'è ancora nessuna parola su perché Jbuilder è così lento, cosa può essere fatto per aggirarlo, o come confrontare gli altri strumenti per lo stesso compito.

risposta

12

jbuilder crea un grosso hash contenente i dati e quindi utilizza ActiveSupport::JSON per trasformarlo in JSON. Ci sono emettitori JSON più veloci come mostrato nella seguente benchmark micro (assicuratevi di avere le gemme multijson e yajl-rubino installati)

require 'benchmark' 
require 'active_support' 
require 'multi_json' 
sample = {menu: { 
    header: "SVG Viewer", 
    items: [ 
     {id: "Open"}, 
     {id: "OpenNew", label: "Open New"}, 
     nil, 
     {id: "ZoomIn", label: "Zoom In"}, 
     {id: "ZoomOut", label: "Zoom Out"}, 
     {id: "OriginalView", label: "Original View"}, 
     nil, 
     {id: "Quality"}, 
     {id: "Pause"}, 
     {id: "Mute"}, 
     nil, 
     {id: "Find", label: "Find..."}, 
     {id: "FindAgain", label: "Find Again"}, 
     {id: "Copy"}, 
     {id: "CopyAgain", label: "Copy Again"}, 
     {id: "CopySVG", label: "Copy SVG"}, 
     {id: "ViewSVG", label: "View SVG"}, 
     {id: "ViewSource", label: "View Source"}, 
     {id: "SaveAs", label: "Save As"}, 
     nil, 
     {id: "Help"}, 
     {id: "About", label: "About Adobe CVG Viewer..."} 
    ] 
}} 


MultiJson.engine = :yajl 
Benchmark.bmbm(5) do |x| 
    x.report 'activesupport' do 
    1000.times {ActiveSupport::JSON.encode(sample)} 
    end 
    x.report 'yajl' do 
    1000.times {MultiJson.encode(sample)} 
    end 
end 

Sulla mia macchina questo produce

    user  system  total  real 
activesupport 1.050000 0.010000 1.060000 ( 1.068426) 
yajl   0.020000 0.000000 0.020000 ( 0.021169) 

cioè per codificare l'oggetto del campione Il supporto attivo 1000 volte ha richiesto più di 1 secondo, MultiJson (usando il motore yajl) ha impiegato 21ms.

JBuilder è hardcoded per utilizzare ActiveSupport :: JSON, ma MultiJSON (una gemma che consente di passare da una libreria json a un'altra) è un drop banale ed è già una dipendenza di ActiveSupport - vedere il mio fork of jbuilder. Ho aperto una richiesta di pull, ma fino ad allora potevi provare a usare questa fork (o crearne una personale - è una modifica di una riga)

+0

Passare a MultiJson sembra tagliare circa 60ms al di fuori dei 400 ms o così trascorriamo il rendering per una richiesta tipica. Non l'enorme cambiamento che speravo, ma per un cambio di una sola riga, non per niente male. Grazie. – MrTheWalrus

+0

Sì. Questo. È. Grande. – maletor

+0

@Frederick: Vedo la riga di modifica dal link: - https://github.com/fcheung/jbuilder/commit/a58b355f68bc39b1fddf8b178f3844c5d4f65501 unito con il master di rails, quindi suppongo che non sia necessario apportare alcuna modifica. Puoi per favore confermare se la mia inferenza è corretta. – boddhisattva

4

Considerare di passare a Rabl e aggiungere caching. Dato che hai migliaia di oggetti in strutture nidificate, alcuni nodi del tuo JSON risultante possono essere resi come partial e memorizzati nella cache: il guadagno in termini di prestazioni può essere enorme.

Oltre a questa prestazione Rabl è leggermente migliore delle prestazioni di JBuilder, ma trovo la sintassi Rabl a volte confusa e passerei a JBuilder una volta implementato il caching dei frammenti.

0

Come indicato prima dello JBuilder crea un hash, quindi serializza tale hash su JSON.

Lo stesso con il caching, c'è l'hash principale e l'hash della cache viene unito nell'hash principale che deve ancora essere convertito in JSON.

La mia soluzione era TurboStreamer. TurboStreamer emette direttamente su un IO/Stream/String, saltando quindi il passo di serializzazione di JBuilder (e a prima vista ciò vale ancora per Rabl e to_json a seconda dell'utilizzo).

Per noi questo ha ridotto significativamente il tempo di rendering & volte GC (a causa della creazione dell'hash in jbuilder) e ci consente di avviare lo streaming di JSON al client mentre otteniamo i nostri risultati. Lo svantaggio è che TurboStreamer è un po 'più prolisso ed esplicito.

Performance Test A (senza caching coinvolti):

Performance Test B (per lo più tutti caching):

Problemi correlati