1 Star 0 Fork 0

周狮虎 / fast-ruby

Gitee — Enterprise-level DevOps R&D management platform
Join Gitee
This repository doesn't specify license. Without author's permission, this code is only for learning and cannot be used for other purposes.
Clone or download
Cancel
Notice: Creating folder will generate an empty file .keep, because not support in Git
Loading...
README.md

Ruby代码效率基准测试(FastRuby)


前言

这是G站上一个关于Ruby代码效率很好的学习资料,内容很多,看完包括代码在内的内容花了我近5小时,收获颇丰。本文由周狮虎翻译,重新排版,保留(我觉得是)关键的内容并增加注释,力求节约时间,一针见血。对原文感兴趣的可以去G站,或看这里

项目源自Erik Michaels-Ober的一次演讲 'Writing Fast Ruby',项目原作者为每项测试编写了代码以造福全世界人民。源代码通过每个分项中的 code 链接浏览。

注意:不代表每一对进行测试的代码在任何时候都可以通用(例如gsubtr)

测试环境:Ruby 2.2.0p0 / OS X 10.10.1 / MacBook Pro (Retina, 15-inch, Mid 2014), 2.5 GHz Intel Core i7, 16 GB 1600 MHz DDR3

The Travis Build 上有这些代码基于其他Ruby环境下的测试结果。


分类目录

1. 一般(General)
2. 调用方法 (Method Invocation)
3. 数组(Array)
4. 日期(Date)
5. 遍历(Enumerable)
6. 哈希(Hash)
7. 进程&代码块(Proc & Block)
8. 字符串(String)
9. 时间(Time)
10. 范围(Range)


General

(x,y=1,2) vs (x=1;y=2) code

(x,y=1,2):  7686954.1 i/s
(x=1;y=2):  6320425.6 i/s - 1.22x slower

attr_accessor vs getter_and_setter code

attr_accessor:      1865408.4 i/s
getter_and_setter:  1660021.9 i/s - 1.12x slower

respond_to? vs begin...rescue (流程控制) code

respond_to?:     3276972.3 i/s
begin...rescue:   371591.0 i/s - 8.82x slower

define_method vs module_eval (定义方法) code

define_method:            1345.6 i/s
module_eval with string:  1129.7 i/s - 1.19x slower

raise vs E2MM#Raise (抛出异常) code

Kernel#raise: 589148.7 i/s
E2MM#Raise:    29004.8 i/s - 20.31x slower

While true vs loop code

While true:  0.5 i/s
loop:        0.2 i/s - 2.41x slower

<= vs ancestors.include? code

<=:                  1249606.0 i/s
ancestors.include?:   192602.9 i/s - 6.49x  slower

Method Invocation

call vs send vs method_missing code

call:            3811183.4 i/s
send:            3244239.1 i/s - 1.17x slower
method_missing:  2728893.0 i/s - 1.40x slower

Array.map{|n|do_something(n)} vs Array.map(&method(:do_something)) code

Array.map{|n|do_something(n)}:     1866669.5 i/s
Array.map(&method(:do_something)):  467095.4 i/s - 4.00x slower

Function(Array) vs Function(*Array) code

Function(Array):  5580972.6 i/s
Function(*Array):   54427.7 i/s - 102.54x slower

Hash vs OpenStruct code

Hash:        5278844.0 i/s
OpenStruct:  3048139.8 i/s - 1.73x slower

Hash vs OpenStruct (创建) code

Hash:        1604259.1 i/s
OpenStruct:    96855.3 i/s - 16.56x slower

Float.round().to_s vs Kernel#format vs String#% code

Float.round:    1570411.4 i/s
Kernel#format:  1144036.6 i/s - 1.37x  slower
String#%:       1046689.1 i/s - 1.50x  slower

Array

Array.bsearch vs Array.find code

.bsearch仅对经过排序的数组有效。

bsearch: 577300.7 i/s
find:         0.2 i/s - 3137489.63x slower

Array.length vs Array.size vs Array.count code

当你只想知道数组中元素个数时使用lengthsize是其化名,count可以附加条件以计算特定元素的个数。

Array.length: 11394036.7 i/s
Array.size:   11302701.1 i/s - 1.01x slower
Array.count:   9194976.2 i/s - 1.24x slower

Array.sample vs Array.shuffle.first code

Array.sample将指针指向原数组中的元素, Array.shuffle 返回新的数组。

Array#sample:         5727032.0 i/s
Array#shuffle.first:   304341.1 i/s - 18.82x slower

Array[0] vs Array.first code

Array[0]:     8613583.7 i/s
Array.first:  7464526.6 i/s - 1.15x slower

Array[-1] vs Array.last code

Array[-1]:   8582074.3 i/s
Array.last:  7639254.5 i/s - 1.12x slower

Array.unshift vs Array.insert code

Array.unshift:   44.9 i/s
Array.insert:     0.2 i/s - 262.56x slower

Enumerable

Enumerable.map vs Enumerable.each + push code

Enumerable.map:           158090.9 i/s
Enumerable.each + push:    99634.2 i/s - 1.59x slower

Enumerable.each vs for loop code

Enumerable.each:   208157.4 i/s
For loop:     198517.3 i/s - 1.05x slower

while loop vs Enumerable.each_with_index code

while Loop:              240752.1 i/s
Enumerable.each_with_index:   126753.4 i/s - 1.90x slower

Enumerable.flat_map vs Enumerable.map vs Enumerable.flatten code

Enumerable.flat_map:    55979.6 i/s
Enumerable.map.flatten:    34529.6 i/s - 1.62x slower
Enumerable.map.flatten(1):    33800.6 i/s - 1.66x slower

Enumerable.reverse_each vs Enumerable.reverse.each code

以Array为例,Array.reverse_each不返回新的数组,Array.reverse返回新的数组。

Enumerable.reverse_each:   216060.5 i/s
Enumerable.reverse.each:   190729.1 i/s - 1.13x slower

Enumerable.min_by vs Enumerable.sort_by{}.first code

以Array为例,Array.min_by 不排序也不返回新数组,Array.sort_by排序并返回新数组。
Enumerable.sort_by{}.lastvsEnumerable.max_by, Enumerable.sort.firstvsEnumerable.min,Enumerable.sort.lastvsEnumerable.max对比测试结果均类似。

Enumerable.min_by:            157877.0 i/s
Enumerable.sort_by{}.first:   106831.1 i/s - 1.48x  slower

Enumerable.detect vs Enumerable.select.first code

Enumerable.detect:         434304.2 i/s
Enumerable.select.first:    89757.4 i/s - 4.84x slower

Enumerable.reverse.detect vs Enumerable.select.last code

Enumerable.reverse.detect:  1263100.2 i/s
Enumerable.select.last:      119386.8 i/s - 10.58x slower

Enumerable.sort_by (Symbol.to_proc) vs Enumerable.sort_by vs Enumerable.sort code

Symbol.to_proc是指类似 Array.sort_by(&:name) 的写法,后面的 Proc&Block 章节也有相关代码。

Enumerable.sort_by (Symbol.to_proc):  25916.1 i/s
Enumerable.sort_by:                   24650.2 i/s - 1.05x slower
Enumerable.sort:                      14018.3 i/s - 1.85x slower

Enumerable.inject(Symbol) vs Enumerable.inject(Proc) vs Enumerable.inject{block} code

inject symbol:    19001.5 i/s
inject to_proc:    15958.3 i/s - 1.19x slower
inject block:    14063.1 i/s - 1.35x slower

Date

Date.iso8601 vs Date.parse code

Date.iso8601:   328035.3 i/s
Date.parse:   175545.9 i/s - 1.87x  slower

Hash

Hash[] vs Hash.fetch code

Hash[symbol]:        7531355.8 i/s
Hash[string]:        6656818.8 i/s - 1.13x slower
Hash.fetch(symbol):  6643665.5 i/s - 1.13x slower
Hash.fetch(string):  3981166.5 i/s - 1.89x slower

Hash[] vs Hash.dig vs Hash.fetch code

Hash[]:               6065791.0 i/s
Hash.dig:             5719290.9 i/s - same-ish: difference falls within error
Hash[] ||:            5366226.5 i/s - same-ish: difference falls within error
Hash.fetch:           4101102.1 i/s - 1.48x slower
Hash.fetch fallback:  2974906.9 i/s - 2.04x slower
Hash[] &&:            2781646.6 i/s - 2.18x slower

Hash[] vs Hash.dup code

不要将Hash.dup全部替换为Hash[],仅凭该项无法判断程序最终的效率高低

Hash[]:     343986.5 i/s
Hash.dup:   163516.3 i/s - 2.10x slower

Hash.fetch(argument) vs Hash.fetch{block} code

该项测试是比较默认返回值不同时的效率,当默认值为常量,数组,符号时,arg稍快于block

Hash.fetch(const):  7030600.4 i/s
Hash.fetch{block}:  6814826.7 i/s - 1.03x slower
Hash.fetch(arg):    4752567.2 i/s - 1.48x slower

Hash.each_key vs Hash.keys.each code

Hash.each_key不返回新数组,Hash.keys.each返回新数组。

Hash.each_key:  1049161.6 i/s
Hash.keys.each:  869262.3 i/s - 1.21x slower

Hash.key? vs Hash.keys.include? code

Hash.key?不返回新数组,Hash.keys.include?返回新数组。

Hash.key?:        6365855.5 i/s
Hash.keys.include?:  8612.4 i/s - 739.15x  slower

Hash.value? vs Hash.values.include? code

Hash.value?不返回新数组,Hash.values.include?返回新数组。

Hash.value?:           38395.0 i/s
Hash.values.include?:  23186.8 i/s - 1.66x  slower

Hash[]= vs Hash.merge! code

Hash[]=:     28287.1 i/s
Hash.merge!: 10653.3 i/s - 2.66x slower

Hash**other vs Hash.merge code

Hash**other:   798396.6 i/s
Hash.merge:   434170.8 i/s - 1.84x  slower

Hash.merge! vs Hash.merge code

Hash.merge!:    9830.3 i/s
Hash.merge:      409.6 i/s - 24.00x slower

{}.merge!(Hash) vs Hash.merge({}) vs Hash.dup.merge!({}) code

该项测试比较保留原hash并在改动后生成新副本的3种方法

{}.merge!(Hash):    20054.8 i/s
Hash.merge({}):      7676.3 i/s - 2.61x slower
Hash.dup.merge!({}): 7439.9 i/s - 2.70x slower

Hash.sort_by vs Hash.sort code

sort_by + to_h:  122176.2 i/s
sort + to_h:      81972.8 i/s - 1.49x slower

Hash.slice vs 其他类似方法 code

slice在Ruby2.5版本中引入

Hash.slice   :          2539515.5 i/s
Array.each          :   1613665.5 i/s - 1.57x  slower
Array.each_with_object: 1352851.8 i/s - 1.88x  slower
Hash.select+include :    760944.2 i/s - 3.34x  slower

Proc & Block

Symbol.to_proc vs {Block} code

Symbol.to_proc更为精简,推荐使用

Symbol#to_proc: 54791.1 i/s
Block:          47914.3 i/s - 1.14x slower

yield vs Proc.call{block arguments} code

在Ruby2.5版本之前, 代码块参数会自动转换成新的进程, 这导致效率降低。

yield:        10436414.0 i/s
unused block:  2265399.0 i/s - 4.61x  slower
block + yield: 2146619.0 i/s - 4.86x  slower
block.call:    1967300.9 i/s - 5.30x  slower

Ruby2.5版本进行了优化:

unused block: 11176355.0 i/s
yield: 10588342.3 i/s - 1.06x  slower
block + yield:  9075355.5 i/s - 1.23x  slower
block.call:  1969834.0 i/s - 5.67x  slower

Ruby2.6版本进一步进行了优化:

unused block: 15980789.4 i/s
yield: 15351931.0 i/s - 1.04x  slower
block + yield: 12630378.1 i/s - 1.27x  slower
block.call: 10587315.1 i/s - 1.51x  slower

String

+String vs String.dup code

+String在Ruby2.3.0版本中引入。String.new与测试的两个方法不一样,因为它始终采用ASCII-8BIT编码。

+String:     7697108.3 i/s
String.dup:  3566485.7 i/s - 2.16x  slower

String.casecmp vs String.downcase + == code

String.casecmp:        3708258.7 i/s
String.downcase + ==:  2914767.7 i/s - 1.27x slower

对比5个字符串合并方法 code

"foo" "bar":  5369594.5 i/s
"#{'foo'}#{'bar'}":  5181745.7 i/s - same-ish: difference falls within error
String.append:  3075719.2 i/s - 1.75x slower
String.concat:  3016703.5 i/s - 1.78x slower
String+:  2977282.7 i/s - 1.80x slower

String.start_with?/String.end_with? vs String.match vs String.match? code (start) code (end)

当文字越长,String#match?的效率越低。对于短句,它与start_with?/end_with?的效率相近。
:warning: 可以用start_with?end_with?替换正则表达式,例如error.path =~ /^#{path}(\.rb)?$/等同于error.path.start_with?(path) && error.path.end_with?('.rb', '')。而某些时候正则表达式无法替代start_with?,例如: "a\nb" =~ /^b/ #=> 2 but "a\nb" =~ /\Ab/ #=> nil.

String.start_with?:  6314182.0 i/s
String.match?:       5138115.1 i/s - 1.23x  slower
String =~:           1088461.5 i/s - 5.80x  slower
String.end_with?:  4547871.0 i/s
String.match?:     3008554.5 i/s - 1.51x  slower
String =~:          918100.5 i/s - 4.95x  slower

String.start_with? vs String[].== code

String.start_with?:  2046618.9 i/s
String[0, n] ==:      711802.3 i/s - 2.88x slower
String[RANGE] ==:     651751.2 i/s - 3.14x slower
String[0...n] ==:     427206.8 i/s - 4.79x slower

String.match? vs Regexp === vs String.match vs String =~ code

Ruby2.4版本加入了String.match?Regexp.match?ActiveSupport为之前的版本的Regexp提供支持,但无效率提升。

String.match?:  6283591.8 i/s
String =~:      2581356.8 i/s - 2.43x  slower
Regexp ===:     2482379.7 i/s - 2.53x  slower
String.match:   2096984.3 i/s - 3.00x  slower

String[]= vs String.gsub vs String.sub code

String.dup[]=:   917873.1 i/s
String.sub:      756664.7 i/s - 1.21x slower
String.gsub:     647665.6 i/s - 1.42x slower

String.tr vs String.gsub code

String.tr:    1861860.4 i/s
String.gsub:   516604.2 i/s - 3.60x slower

Freeze vs Without Freeze code

Freeze:          9329054.3 i/s
Without Freeze:  7279203.1 i/s - 1.28x slower

String[]= vs String.sub! vs String.gsub! code

:warning: String[]将会在所给的条件不符合要求时产生错误。

String['string']=:    1214845.5 i/s
String[/regexp/]=:     840615.2 i/s - 1.45x slower
String.sub!'string':   752731.4 i/s - 1.61x slower
String.sub!/regexp/:   663075.3 i/s - 1.83x slower
String.gsub!'string':  481183.5 i/s - 2.52x slower
String.gsub!/regexp/:  342003.8 i/s - 3.55x slower

String.delete_prefix vs String.sub code

Ruby2.5版本加入了String#delete_prefix,仅用于从字符串头部移除字符。

String.delete_prefix:  4111531.1 i/s
String.sub:             814725.3 i/s - 5.05x  slower

String.chomp vs String.delete_suffix vs String.sub code

Ruby2.5版本加入了String#delete_suffix,与chomp相比效率提升很小且容易出错,仅用于从字符串尾部移除字符。

String.delete_suffix:  4202201.7 i/s
String.chomp:          3950921.9 i/s - 1.06x  slower
String.sub:             838415.3 i/s - 5.01x  slower

String.unpack1 vs String.unpack[0] code

Ruby2.4.0版本加入了unpack1以避免运算过程中产生新的Array。

String.unpack1:    4864467.2 i/s
String.unpack[0]:  3777815.6 i/s - 1.29x  slower

删除空格 (或其他连续字符) code

String.squeeze:       372910.3 i/s
String.gsub/regex+/:   14668.1 i/s - 25.42x  slower

Time

Time.iso8601 vs Time.parse code

iso8601速度快,但会在参数出错时报错.

Time.iso8601: 114485.1 i/s
Time.parse:    43710.9 i/s - 2.62x  slower

Range

plain compare vs cover? vs include? vs member? code

cover? 只检查是否在开头与结尾之间, include?需要遍历整个范围。

plain compare:  2581211.8 i/s
range#cover?:   1816038.5 i/s - 1.42x slower
range#include?:   83343.9 i/s - 30.97x slower
range#member?:    82654.1 i/s - 31.23x slower

License

CC-BY-SA
Creative Commons Attribution-ShareAlike 4.0 International License.

Code License [CC0 1.0 Universal]

To the extent possible under law, @JuanitoFatas has waived all copyright and related or neighboring rights to "fast-ruby".This work belongs to the community.

Repository Comments ( 0 )

Sign in for post a comment

About

【中文精简版】 Ruby 代码效率基准测试 from GitHub expand collapse
Cancel

Releases

No release

Contributors

All

Activities

load more
can not load any more
1
https://gitee.com/zhou_shi_hu/fast-ruby.git
git@gitee.com:zhou_shi_hu/fast-ruby.git
zhou_shi_hu
fast-ruby
fast-ruby
master

Search

181749 a2d7925e 1850385 181749 9f8568a7 1850385