我需要构建一个大约1-50 MB的巨大XML文件。我认为使用构建器会足够有效,而且有点。问题是,在程序到达最后一行后它不会立即结束,但Ruby仍在做几秒钟的事情,可能是垃圾收集?之后程序终于结束了。
举一个真实的例子,我测量了构建XML文件的时间。在构建XML时,它输出55秒(后面有一个数据库,所以需要很长时间),但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所以我必须找到解决方案。
在构建XML之后,我可以做些什么来避免X秒长的开销?它甚至可能吗?
更新:
感谢Adiel Mittmann的建议,在创建我的最小工作示例期间,我能够找到问题所在。我现在有一个很小的(很小的)例子来证明这个问题。
以下代码导致问题:
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
然后Text node creation code被执行。那么,到底发生了什么?
更新2:
经过其他一些尝试后,可以通过以下方式轻松复制问题:
builder = Nokogiri::XML::Builder.new do |xml|
0.upto(81900) do
xml.text "test"
end
end
puts "End"
真的是Nokogiri本身吗?我有什么选择吗?
答案 0 :(得分:3)
您的示例在此处执行也需要很长时间。而你是对的:垃圾收集器需要很长时间才能执行。试试这个:
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文件做些什么。
答案 1 :(得分:0)
尝试使用Ruby内置(原文如此)Builder。我也使用它来生成大型XML文件,并且它的占用空间很小。