gpt4 book ai didi

ruby - 带有 Ruby 的 XML 数据扫描器 : case statement or dynamic dispatch in term of performance?

转载 作者:数据小太阳 更新时间:2023-10-29 08:05:17 25 4
gpt4 key购买 nike

我正在编写一个 XML 数据扫描器,它使用一些 XML 解析器库(如 nokogiri 等)读取 XML 文本,并生成一棵节点树。我需要为每个 XML 元素创建一个对象。所以,我需要一个根据给定的元素名称和属性创建对象的方法,就像这样,无论我使用哪种解析器库选项(SAX 或 DOM):

create_node(name, attributes_hash)

该方法需要根据名称进行分支。实现可能性是:

  1. 案例陈述
  2. 方法调度和预定义方法

由于这种方法可能成为瓶颈,我编写了一个基准测试脚本来检查 Ruby 的性能。 (基准脚本附加在这个问题的最后一部分。我不喜欢脚本的某些部分——特别是如何创建案例语句——所以评论我如何改进它也是欢迎,但请将其作为评论而不是答案提供...我可能也需要为此创建一个问题..)。

该脚本以两种范围大小衡量以下四种情况:

  1. 使用常量名称的方法调度
  2. 名称与 #{} 连接的方法分派(dispatch)
  3. 方法调度,名称与 + 连接
  4. 使用case语句,调用相同的方法

结果:

                                                 user     system      total        real
a to z: method_calls (with const name) 0.090000 0.000000 0.090000 ( 0.092516)
a to z: method_calls (with dynamic name) 1 0.180000 0.000000 0.180000 ( 0.181793)
a to z: method_calls (with dynamic name) 2 0.200000 0.000000 0.200000 ( 0.202818)
a to z: switch_calls 0.130000 0.000000 0.130000 ( 0.132633)

user system total real
a to zz: method_calls (with const name) 2.900000 0.000000 2.900000 ( 2.894273)
a to zz: method_calls (with dynamic name) 1 6.500000 0.010000 6.510000 ( 6.507099)
a to zz: method_calls (with dynamic name) 2 6.980000 0.000000 6.980000 ( 6.987534)
a to zz: switch_calls 4.750000 0.000000 4.750000 ( 4.742448)

我观察到基于常量名称的方法调度比使用 case 语句更快,但是,如果在确定方法名称时涉及字符串操作,确定方法名称的成本比实际方法调用成本要高,有效地使这些选项( 2 和 3) 比选项 4 慢。此外,选项 2 和 3 之间的差异可以忽略不计。

为了使扫描器安全,我更喜欢为方法添加一些前缀,因为如果没有它,就可以制作一个 XML 来调用一些方法,我不希望发生这种情况。但是确定方法名称的成本是不可忽略的。

你是怎么写这些扫描器的?我想知道以下问题的答案:

  1. 除了以上还有什么好的方案吗?
  2. 如果不是,您选择哪种(case-when 或方法分派(dispatch))方案?
  3. 如果我不计算方法名称,它会更快。有什么好的方法可以安全地进行方法分派(dispatch)吗? (例如,通过限制要调用的节点名称。)

基准脚本

# Benchmark to measure the difference of
# use of case statement and message passing

require 'benchmark'

def bench(title, tobj, count)
Benchmark.bmbm do |b|
b.report "#{title}: method_calls (with const name)" do
(1..count).each do |c|
tobj.run_send_using_const
end
end

b.report "#{title}: method_calls (with dynamic name) 1" do
(1..count).each do |c|
tobj.run_send_using_dynamic_1
end
end

b.report "#{title}: method_calls (with dynamic name) 2" do
(1..count).each do |c|
tobj.run_send_using_dynamic_2
end
end

b.report "#{title}: switch_calls" do
(1..count).each do |c|
tobj.run_switch
end
end
end
end


class Switcher
def initialize(names)
@method_names = { }
@names = names
names.each do |n|
@method_names[n] = "dynamic_#{n}"
@@n = n
class << self
mname = "dynamic_#{@@n}"
define_method(mname) do
mname
end
end
end

swst = ""
names.each do |n|
swst << "when \"#{n}\" then dynamic_#{n}\n"
end

st = "
def run_switch_each(n)
case n
#{swst}
end
end
"
eval(st)
end

def run_send_using_const
@method_names.each_value do |n|
self.send n
end
end

def run_send_using_dynamic_1
@names.each do |n|
self.send "dynamic_#{n}"
end
end

def run_send_using_dynamic_2
@names.each do |n|
self.send "dynamic_" + n
end
end

def run_switch
@names.each do |n|
run_switch_each(n)
end
end

end


sw1 = Switcher.new('a'..'z')
sw2 = Switcher.new('a'..'zz')

bench("a to z", sw1, 10000)
bench("a to zz", sw2, 10000)

最佳答案

我认为这是一个过早优化的案例。

But the cost to determine the method name is not negligible.

与什么相比不可忽略?这里的方法有不同的性能数字,但是分派(dispatch)一个节点所花费的时间是否与解析节点(使用 Nokogiri 等)、构建专用节点对象以及用它做任何你需要的事情所花费的时间相当?

我相信不会。我没有基准来证明该声明(你需要实际代码),但字符串连接与字符串插值实际上在结果(dynamic1 与 dynamic2)上有显着差异这一事实是一个很好的指标,表明你是测量一些微不足道的东西。

或者每次分派(dispatch)添加一个字符串连接会使结果时间增加 2-2.5 倍(const 与 dynamic2)。

关于ruby - 带有 Ruby 的 XML 数据扫描器 : case statement or dynamic dispatch in term of performance?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11339527/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com