Производительность Nokogiri на огромном XML? - PullRequest
2 голосов
/ 16 марта 2012

Мне нужно создать огромный XML-файл, около 1-50 МБ.Я думал, что использование компоновщика будет достаточно эффективным и, ну, в некоторой степени.Проблема в том, что после того, как программа достигает последней строки, она не заканчивается немедленно, но Ruby все еще что-то делает в течение нескольких секунд, может быть, сборщик мусора?После этого программа окончательно завершается.

Чтобы привести реальный пример, я измерил время создания файла XML.Он выводит 55 секунд (база данных отстает, так что это занимает много времени), когда XML был построен, но Ruby все еще обрабатывает еще около 15 секунд, и процессор сходит с ума.

Псевдо / реальный кодследует:

...
builder = Nokogiri::XML::Builder.with(doc) do |xml|
  build_node(xml)
end
...

def build_node(xml)
  ...
  xml["#{namespace}"] if namespace  
  xml.send("#{elem_name}", attrs_hash) do |elem_xml|
  ...
    if has_children
      if type
        case type
          when XML::TextContent::PLAIN
            elem_xml.text text_content
          when XML::TextContent::COMMENT
            elem_xml.comment text_content
          when XML::TextContent::CDATA
            elem_xml.cdata text_content
         end
       else
         build_node(elem_xml)
       end
    end
  end
end

Обратите внимание, что я использовал другой подход, используя свою собственную структуру классов, и скорость сборки была такой же, но на последней строке программа обычно заканчивалась, но теперь яЯ вынужден использовать Nokogiri, поэтому мне нужно найти решение.

Что я могу сделать, чтобы избежать дополнительных X секунд после создания XML?Возможно ли это вообще?

ОБНОВЛЕНИЕ:

Благодаря предложению Адиэля Митманна, во время создания моего минимального рабочего примера я смог найти проблему.Теперь у меня есть небольшой (но не такой маленький) пример, демонстрирующий проблему.

Следующий код вызывает проблему:

xml.send("#{elem_name}_") do |elem_xml|
  ...
  elem_xml.text text_content #This line is the problem
  ...
end

Таким образом, строка выполняет следующий код, основанный на документации Nokogiri.:

def create_text_node string, &block
  Nokogiri::XML::Text.new string.to_s, self, &block
end

Затем выполняется код создания текстового узла .Итак, что именно здесь происходит?

ОБНОВЛЕНИЕ 2:

После некоторых других попыток проблема может быть легко воспроизведена с помощью:

builder = Nokogiri::XML::Builder.new do |xml|
  0.upto(81900) do
    xml.text "test"
  end
end
puts "End"

Так это действительно Nokogiriсам?Есть ли вариант для меня?

Ответы [ 2 ]

3 голосов
/ 25 марта 2012

Ваш пример также занимает много времени, чтобы выполнить здесь. И вы были правы: сборщик мусора занимает так много времени. Попробуйте это:

require 'nokogiri'
class A
  def a
    builder = Nokogiri::XML::Builder.new do |xml|
      0.upto(81900) do
        xml.text "test"
      end
    end
  end
end
A.new.a
puts "End1"
GC.start
puts "End2"

Здесь задержка происходит между "End1" и "End2". После печати "End2" программа немедленно закрывается.

Обратите внимание, что я создал объект, чтобы продемонстрировать его. В противном случае данные, сгенерированные компоновщиком, могут быть удалены только после завершения программы.

Что касается лучшего способа сделать то, что вы пытаетесь достичь, я предлагаю вам задать еще один вопрос, в котором подробно описывается, что именно вы пытаетесь сделать с файлами XML.

0 голосов
/ 25 марта 2012

Попробуйте использовать встроенный (SIC) Ruby. Я также использую его для генерации больших XML-файлов, и у него такой маленький размер.

...