Ruby Ruby 的 symbol, 感觉意义不大。。

xjz19901211 · 2014年11月16日 · 最后由 lhy20062008 回复于 2014年11月21日 · 3996 次阅读

虽然速度快了那么一点点,但却让我们陷入选择字符串还是 symbol 的蛋疼事中,更是出现像 Hash#stringify_keysHash#symbolize_keys这种方法,被调用者得到一个 hash 不得不去想一下传入的是 symbol 还是 string。

而 hash 的写法也出现了{:xx => 1}{xx: 1}, 前者可以是任何对象,而后者的 key 就是一个 symbol。。

今天闲的无聊测试了一下性能

sym_keys = []
str_keys = []
1000000.times {|i| str_keys << "number_#{i}_asdf"; sym_keys << "number_#{i}_asdf".to_sym }

require 'benchmark'

[100000, 1000000].each do |length|
  str_hash = {}; sym_hash = {}

  Benchmark.bm do |x| 
    x.report("#{length} string write") { length.times {|i| str_hash[str_keys[i]] = i } } 
    x.report("#{length} symbol write") { length.times {|i| sym_hash[sym_keys[i]] = i } } 

    x.report("#{length} string read") { length.times {|i| str_hash[str_keys[rand(length)]] } } 
    x.report("#{length} symbol read") { length.times {|i| sym_hash[sym_keys[rand(length)]] } } 
  end 
end

Ruby 2.1.3 结果如下

       user     system      total        real
100000 string write  0.060000   0.000000   0.060000 (  0.064129)
100000 symbol write  0.040000   0.000000   0.040000 (  0.044240)
100000 string read  0.090000   0.000000   0.090000 (  0.098106)
100000 symbol read  0.050000   0.000000   0.050000 (  0.052136)
       user     system      total        real
1000000 string write  1.030000   0.040000   1.070000 (  1.066663)
1000000 symbol write  0.810000   0.020000   0.830000 (  0.833698)
1000000 string read  1.010000   0.010000   1.020000 (  1.021844)
1000000 symbol read  0.710000   0.000000   0.710000 (  0.713959)

从数据上看,symbol 相对于 string 的性能提升并没有多大,而我在 2.1.2 下测试得到的是,在 100W 数据下,symbol 反而变慢了,可能是 symbol 表太大了,查询也变慢了?这就不清楚了

抛砖引玉,大家发表下看法?我测试的只是 hash, 在其它情况下是否会有更优异的表现?

写着有点离题了,我疑惑的是 symbol 存在的意义,现在感觉他的出现,带来的麻烦比带来的好处更多。。

100W 的数据就可能有 GC 的干扰了,牵涉到 symbol 的回收问题。

这些性能的差别我觉得对实际应用来说基本没有什么影响。相对于速度,我认为使用 symbol 的时候更多地是表达了一种正式性,即这个 key 不是随意可以更改的。

至于是 String 还是 Symbol,在 Rails 里面大多数情况下你都不用担心,很多都已经被 ActiveSupport::HashWithIndifferentAccess 处理过了。http://api.rubyonrails.org/classes/ActiveSupport/HashWithIndifferentAccess.html

至于写法,更不是问题,{:xx => 1} 与 {xx: 1} 没什么好纠结的,肯定是第二种,这个既少打几个键,也是社区通用的偏好。

@billy 是的,在 rails 下,大多数情况都会被自动处理,但在非 rails 的时候,还是需要自动来处理一下,而像写法,{xx: 1}并不能满足所有需求,不得已时,需要两种写法混用,比如 key 是一个变量时,非常别扭的

我觉得,更多的意义是为了让这个 key 不能随意更改,虽然我在使用中,还没有出现过修改一个 key 的情况,但确实可能出现这种情况

相对来说,我更愿意少掉一个 symbol 的概念,少掉一堆像symbolize_key这样的操作,统一的 hash 写法,自己来维护一个 hash 的 key 不被修改,因为这种情况平常基本上不会见到,有的时间自己处理一下也 ok

感觉我帖子内容,写着有点离题了,我想问的是 symbol 存在的意义,现在感觉他的出现,带来的麻烦比带来的好处更多。。

首先有个本质的问题,字符串的比较其实是字符串对应的 symbol 的比较。 没有 symbol 的话基本上 String 的大部分方法都会变得奇慢无比。

symbol 对应的 C 语言的类型是 unsigned int,并且和 ruby 进程中的 String 是一一对应的,这样的话,一些比较字符串之类的处理就可以通过简单的比较整数来解决。自然 unsigned int 的比较要比 char * 的速度快。

symbol 是 lisp 的遗留产物啦,后来 string 的越来越优化了就逐渐没多大意义了

#5 楼 @luikore 我也觉得是这样,会不会哪天 Ruby 就取消 symbol 了呢

数学里的减号,感觉意义不大。。 关键字 unless, 感觉意义不大。。

symbol 更重要的是一个概念上面的意义。symbol 就是一个概念标识,而字符串就是一个需要处理的数据。没有 symbol,像 python 一样只用 string,感觉都混在一起了。

我也同样觉得,symbol 在 Hash 里使用意义不大。甚至觉得 Hash 里就不应该允许 symbol 出现。

其他地方还是有意义的。

能用 symbol 的地方,我绝对不用 string

ruby 中的 symbol 更多是起规范意义,比如强制用 snake_case(不要拿“”.to_sym 说事),表示这些是特定意义的 key(同名 smybol 对象的 objet id 是唯一/同一的);而 string 是比较随意的/变量性质的,一般用于表达式右值中。

这种测试的场合不太好,也没有多大意义,如果一个 rails 程序中,把代码中大量的不变的字符串都替换成 symbol 的话,我觉得内存消耗,性能上一定会大有不同的。

:sym'sym' 看起来爽

第一次看到:symbol 这个样式,我都醉了,不晓得啥意思,头大

性能只是一个方面,在高并发多用户的情况下,sym 更节省内存。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号