Tôi luôn nghi ngờ exception chậm hơn flow control mechanism khác trong Ruby.畢竟, exception phức tạp hơn hẳn "break" hoặc "return" đơn giản. Nhưng tôi từng sai hunches, nên test thử.
Code dưới dùng `benchmark-ips` so sánh relative performance thoát loop qua exception, break, return. Đã thấy ví dụ benchmark này MRI 1.9. Muốn thử MRI 2.2.
require 'benchmark/ips'
def exit_via_exception
5.times do
raise RuntimeError
end
rescue
end
def exit_via_break
5.times do
break
end
end
def exit_via_return
5.times do
return
end
end
Benchmark.ips do |x|
x.report("exception") { exit_via_exception }
x.report("break") { exit_via_break }
x.report("return") { exit_via_return }
end
Kết quả khá staggering. Function dùng exception kém hơn half so với break và return.
$ ruby exception_benchmark.rb
Calculating -------------------------------------
exception 50.872k i/100ms
break 125.322k i/100ms
return 124.173k i/100ms
-------------------------------------------------
exception 714.795k (± 2.7%) i/s - 3.612M
break 3.459M (± 3.1%) i/s - 17.294M
return 3.379M (± 3.0%) i/s - 16.888M
Đây không phải benchmark hoàn hảo
Có vài issue chưa biết compensate. Ví dụ, exception và break method phải return. Vì vậy làm nhiều hơn method chỉ return. Cũng tò mò rescue exception có thêm overhead không. Nhưng không rescue làm benchmark abort.
Vẫn, exception chậm hơn hẳn các ví dụ khác, tôi nghĩ kết quả có ý nghĩa dù không hoàn hảo.
Bài học rút ra?
Nếu dùng exception làm flow control mechanism. Dừng ngay! Đặc biệt nếu loop raise và catch exception lặp lại.
Sẽ thay đổi cách tôi dùng exception? Có lẽ không. Tôi chịu đựng chút chậm nếu chậm là exception to the rule. :)
...Nhưng JRuby và RBX thì sao?
Josh Cheek (@josh_cheek) viết version benchmark toàn diện hơn. Chạy trên multiple Ruby implementation. Xem kết quả tại đây. Hình như break vẫn thắng. :)