2016-03-29 11 views
5

Ho un pezzo di codice che analizza il file XML da 500 MB utilizzando la gemma libxml-ruby. Ciò che è sorprendente per me, questo codice corre più lento con GC disabilitato, che sembra contro-intuitivo. Quale potrebbe essere la ragione? Ho a disposizione molta memoria e il sistema non si sta scambiando.Perché questo codice XML di analisi Ruby viene eseguito più lentamente con GC disabilitato?

require 'xml' 

#GC.disable 

@reader = XML::Reader.file('books.xml', :options => XML::Parser::Options::NOBLANKS) 

@reader.read 
@reader.read 

while @reader.name == 'book' 
    book_id = @reader.get_attribute('id') 
    @reader.read 

    until @reader.name == 'book' && @reader.node_type == XML::Reader::TYPE_END_ELEMENT 
    case @reader.name 
    when 'author' 
     author = @reader.read_string 
    when 'title' 
     title = @reader.read_string 
    when 'genre' 
     genre = @reader.read_string 
    when 'price' 
     price = @reader.read_string 
    when 'publish_date' 
     publish_date = @reader.read_string 
    when 'description' 
     description = @reader.read_string 
    end 
    @reader.next 
    end 

    @reader.read  

end 
@reader.close 

Ecco i risultati che ho ottenuto:

ruby  gc on gc off 
2.2.0 16.93s 18.81s 
2.1.5 16.22s 18.58s 
2.0.0 17.63s 17.99s 

Perché disattivare il garbage collector? Ho letto nel libro Ruby Performance Optimization che Ruby è lento soprattutto perché i programmatori non pensano al consumo di memoria, il che rende il garbage collector utilizzare un sacco di tempo di esecuzione. Quindi, spegnere il GC dovrebbe velocizzare istantaneamente le cose (a costo dell'utilizzo della memoria, ovviamente), a condizione che il sistema non stia cambiando.

Volevo vedere se il mio modulo di analisi XML può essere migliorato, così ho iniziato a sperimentarlo disabilitando il GC, che mi ha portato a questo problema. Mi aspettavo una significativa accelerazione con GC disabilitato, ma invece ho ottenuto il contrario. So che le differenze non sono enormi, ma comunque è strano per me.

libxml-ruby gem usa un'implementazione nativa C LibXML sotto il cofano - può essere la ragione?

Il file che ho usato è moltiplicata manualmente books.xml campione scaricato da un po 'di documentazione di Microsoft:

<catalog> 
<book id="bk101"> 
    <author>John Doe</author> 
    <title>XML for dummies</title> 
    <genre>Computer</genre> 
    <price>44.95</price> 
    <publish_date>2000-10-01</publish_date> 
    <description>Some description</description> 
</book> 
.... 
</catalog> 

La mia configurazione: OS X Yosemite, processore Intel Core i5 2.6 GHz, 16 GB di RAM.

Grazie per eventuali suggerimenti.

+0

@engineersmnky se sta chiedendo come rendere più veloce, appartiene qui, non sulla revisione del codice. Se vuole aiuto con tutto questo, dovrebbe andare lì. – Riker

+0

@engineersmnky "Quale potrebbe essere la ragione?Ho un sacco di memoria disponibile e il sistema non si sta scambiando. " – Riker

+1

@engineersmnky Vedi il bit più sotto, che dice" Voglio un feedback su qualsiasi o tutti gli aspetti del codice? ". OP non vuole quel tipo di –

risposta

1

Stai dimenticando il sistema operativo: hai disattivato GC nel tuo processo di risonanza magnetica ma non hai alcun controllo sul kernel linux/unix e su come assegna memoria alla tua applicazione MRI.

In effetti, ritengo che disabilitando GC si sia ostacolato in modo significativo il comportamento dell'applicazione, rendendo probabile che il programma richieda continuamente più RAM dal kernel. Ciò probabilmente comporterà una qualche forma di sovraccarico nel kernel poiché alloca swap o memoria.

I dati di origine sono un file xml da 500 mb che si sta leggendo, nodo per nodo, nel footprint di memoria del programma MRI. È probabile che il tuo processo di risonanza magnetica consuma diversi GB di dati nel momento in cui viene eseguita l'elaborazione; e nessuno dei valori nel blocco di lettura principale viene scartato dopo ogni iterazione: rimangono semplicemente in memoria, e alla fine vengono eliminati solo quando l'applicazione viene chiusa e la memoria viene restituita al sistema operativo.

GC è in atto per gestire questo; ha lo scopo di impedire alla tua applicazione di richiedere memoria aggiuntiva dal kernel, a meno che non ne abbia assolutamente bisogno, e di consentire alla tua applicazione di funzionare "abbastanza bene" nella memoria assegnata ad essa entro limiti ragionevoli.

Quindi non sono sinceramente sorpreso di vedere un rallentamento con GC disabilitato. Quello che direbbe è la media del carico e l'utilizzo dello swap della tua scatola durante i tuoi benchmark.

+0

Grazie per il suggerimento con overhead di allocazione del sistema operativo. – mjkpl

+0

Presumibilmente hai ragione: posso vedere che il processo sta costantemente assegnando sempre più memoria in piccoli incrementi. Per dimostrare il tuo punto ho cercato un modo per localizzare una grande quantità di memoria all'inizio (come l'opzione '-Xmx' in Java), in modo che non sarebbe necessario passare alcun tempo su quello durante l'esecuzione. Ma a quanto pare non è possibile in Ruby. – mjkpl