12th Jun, 2007

Rails应用性能优化3

上一讲,我们的优化策略主要是针对Rails框架进行,这一讲,我们将精力集中到Ruby语言本身。

首先,Ruby语言中的各种元素由于算法的不同,访问时间也各不相等,比如局部变量采用数组索引,在解析时进行顶问,因此访问代价总是O(1),而实例变量和和方法调用由于使用Hash访问,因此只能保持理论上的O(1)访问,也就是没有冲突的情况下,同时调用方法时如果不能在子类找到这个方法,则还需要沿继承树向上回溯查找。

因此,应该尽量避免不必要的多态继承,同时应该尽量使用局部变量,比如下面这段代码的效率就不如修改后的高:

def submit to remote(name, value, options = {})
  options[ :with ] ||= ’Form.serialize( this .form)’
  options[:html ] ||= {}
  options[:html ][ :type ] = ’button’
  options[:html ][ :o nclick ] = ”#{remote function(options)}; return false ; ”
  options[:html ][ :name] = name
  options[:html ][ :value] = value
  tag(”input” , options[:html ], false )
end

修改后:

def submit to remote(name, value, options = {})
  options[ :with ] ||= ’Form.serialize( this .form)’
  html = (options[:html ] ||= {})
  html[:type ] = ’button’
  html[ :o nclick ] = ”#{remote function(options)}; return false ; ”
  html[:name] = name
  html[:value] = value
  tag(”input” , html, false )
end

其次,对于经常用到的数据,应该进行缓存,避免每次用到时再进行计算,比如:

def capital_letters
  ( ”A” .. ”Z” ). to a
end

写成下面这样会更好:

def capital letters
  @capital letters ||= ( ”A” .. ”Z” ). to a
end

当然对于上面这种情况,如果所有类需要的数据都相同,那么完全可以将它定义成class级变量:

@@capital letters = (”A” .. ”Z” ). to a
def capital letters
  @@capital letters
end

当然,除了效率也要注意优美,下面这段代码就不够优美:

def actions
  unless @actions
    # do something complicated and costly to determine action’s value
    @actions = expr
  end
  @actions
end

改成这样会更好一些:

def actions
  @actions ||=
  begin
    # do something complicated and costly to determine action’s value
    expr
  end
end

另外,使用常量对效率也有一定提升。

def validate_find_options (options)
  options.assert valid keys( :conditions , :include , :joins , :limit , :o ffset ,
          :order , :select , :readonly, :group, :from )
end

上面这段代码进行如下修改会更好一些:

VALID FIND OPTIONS = [
    :conditions , :include , :joins , :limit ,
    :offset , :o rder , :select , :readonly, :group, :from ]
def validate find options (options)
  options.assert valid keys(VALID FIND OPTIONS)
end

同时,应该尽可能的使用局部变量。

sql << ” GROUP BY #{options[:group]} ” if options[:group]

上面这种方式明显不如以下两种:

if opts = options[:group]
  sql << ” GROUP BY #{opts} ”
end

opts = options[:group] and sql << ” GROUP BY #{opts} ”

当然,能够写成这样是最好的:

sql << ” GROUP BY #{opts} ” if opts = options[:group]

但是语法不支持。

还有一些小技巧:

logger.debug ”args: #{hash.keys.sort.join ( ’ ’ )}” if logger

这段代码的问题在于,不管logger.level是否为DEBUG,hash.keys.sort.join(’ ’) 都会被执行,因此,应该写成这样:

logger.debug ”args: #{hash.keys.sort.join ( ’ ’ )}” if logger && logger.debug?

还有就是关于ObjectSpace.each_object的,在production模式最好不要使用这个方法。

ObjectSpace.each object(Class) {|c| f(c) }

事实上跟下面的代码是相等的:

ObjectSpace.each object {|o| o.is a?(Class) && f(o) }

它会对堆上的每一个对象都进行检查,这会对性能造成极大损耗。

好了,关于Ruby语言的优化技巧就这么多了,下一讲我们将对Ruby的垃圾回收(GC)机制进行分析,并给出相应的优化策略。

留条评论?

Your response:

Categories