2014-05-20 15 views
20

Tôi đã thực hiện kiểm tra hiệu suất nhỏ của mảng Ruby concat() và hoạt động +concat() là quá nhanh.Ruby Array concat so với + speed?

Tôi tuy nhiên không rõ lý do tại sao concat() quá nhanh?

Có ai có thể trợ giúp ở đây không?

Đây là mã tôi đã sử dụng:

t = Time.now 
ar = [] 
for i in 1..10000 
ar = ar + [4,5] 
end 
puts "Time for + " + (Time.now - t).to_s 


t = Time.now 
ar = [] 
for i in 1..10000 
ar.concat([4,5]) 
end 
puts "Time for concat " + (Time.now - t).to_s 
+6

FYI :) http://www.ruby-doc.org/stdlib-1.9.3/libdoc/benchmark/rdoc /Benchmark.html –

+0

http://www.joelonsoftware.com/articles/fog0000000319.html – Fuser97381

Trả lời

34

Theo Ruby docs, sự khác biệt là:

Mảng # +:

Concatenation - Trả về một mảng mới xây dựng bằng cách nối hai mảng lại với nhau để tạo ra một mảng thứ ba.

Mảng # concat:

Mảng # concat: Gắn các yếu tố của other_ary tự.

Vì vậy, toán tử + sẽ tạo mảng mới mỗi lần được gọi (đắt tiền), trong khi concat chỉ chắp thêm phần tử mới.

13

Câu trả lời nằm trong triển khai C cơ bản của Ruby của toán tử + và phương thức concat.

Array#+

rb_ary_plus(VALUE x, VALUE y) 
{ 
    VALUE z; 
    long len, xlen, ylen; 

    y = to_ary(y); 
    xlen = RARRAY_LEN(x); 
    ylen = RARRAY_LEN(y); 
    len = xlen + ylen; 
    z = rb_ary_new2(len); 

    ary_memcpy(z, 0, xlen, RARRAY_CONST_PTR(x)); 
    ary_memcpy(z, xlen, ylen, RARRAY_CONST_PTR(y)); 
    ARY_SET_LEN(z, len); 
    return z; 
} 

Array#concat

rb_ary_concat(VALUE x, VALUE y) 
{ 
    rb_ary_modify_check(x); 
    y = to_ary(y); 
    if (RARRAY_LEN(y) > 0) { 
     rb_ary_splice(x, RARRAY_LEN(x), 0, y); 
    } 
    return x; 
} 

Như bạn có thể thấy, các nhà điều hành + được sao chép vào bộ nhớ từ mỗi mảng, sau đó tạo ra và trả về một mảng thứ ba với những nội dung của cả hai. Phương pháp concat chỉ đơn giản là nối mảng mới vào mảng gốc.

+0

Hoàn hảo. Cảm ơn bạn! –

+0

Còn khoảng + =? Nó giống như #concat, về mặt kỹ thuật? –

+1

@NoICE Trong Ruby, bạn có thể viết biểu thức toán học theo một vài cách. Có liên quan nhất ở đây là 'x = x + y', tương đương với' x + = y'. Trừ khi một lớp đặc biệt ghi đè toán tử cộng để ủy nhiệm cho 'concat',' + = 'sẽ không thực hiện chính xác giống như' concat' vì nó sử dụng 'rb_ary_plus' thay vì' rb_ary_concat'. –

7

Nếu bạn định chạy điểm chuẩn, hãy tận dụng các công cụ dựng sẵn và giảm thử nghiệm ở mức tối thiểu cần thiết để kiểm tra những gì bạn muốn biết.

Bắt đầu với Fruity, cung cấp rất nhiều thông tin tình báo để chuẩn của nó:

require 'fruity' 

compare do 
    plus { [] + [4, 5] } 
    concat { [].concat([4, 5]) } 
end 
# >> Running each test 32768 times. Test will take about 1 second. 
# >> plus is similar to concat 

Khi điều này là đủ gần để không thực sự lo lắng về, Fruity sẽ cho chúng tôi biết họ đang "tương tự".

Vào thời điểm đó Ruby built-in Benchmark lớp có thể giúp:

require 'benchmark' 

N = 10_000_000 
3.times do 
    Benchmark.bm do |b| 
    b.report('plus') { N.times { [] + [4, 5] }} 
    b.report('concat') { N.times { [].concat([4,5]) }} 
    end 
end 
# >>  user  system  total  real 
# >> plus 1.610000 0.000000 1.610000 ( 1.604636) 
# >> concat 1.660000 0.000000 1.660000 ( 1.668227) 
# >>  user  system  total  real 
# >> plus 1.600000 0.000000 1.600000 ( 1.598551) 
# >> concat 1.690000 0.000000 1.690000 ( 1.682336) 
# >>  user  system  total  real 
# >> plus 1.590000 0.000000 1.590000 ( 1.593757) 
# >> concat 1.680000 0.000000 1.680000 ( 1.684128) 

Thông báo các thời điểm khác nhau. Chạy thử nghiệm một lần có thể dẫn đến kết quả gây hiểu lầm, do đó hãy chạy thử nhiều lần.Ngoài ra, hãy đảm bảo rằng các vòng lặp của bạn dẫn đến thời gian không bị vướng vào tiếng ồn nền do quá trình khởi động.

+0

Bao gồm thời gian thực hiện thực tế chỉ trên đầu vào được chọn là tương đối gây hiểu nhầm vì nó là một giá trị rất nhỏ của n mang lại sự khác biệt rất 'thuần hóa'. – user2864740

+0

(Các thử nghiệm ban đầu thực sự cho thấy sự khác biệt hiệu suất tốt hơn bởi 'tác dụng phụ không mong muốn'.) – user2864740

1

Câu hỏi của OP, như đã nêu trong các câu trả lời khác, là so sánh hai toán tử thực hiện các mục đích khác nhau. Một, concat, phá hủy (biến đổi) mảng ban đầu và + không phá hủy (chức năng thuần túy, không có đột biến).

Tôi đến đây tìm kiếm một bài kiểm tra có thể so sánh hơn, không nhận ra vào thời điểm đó, concat đó là phá hoại. Trong trường hợp nó hữu ích cho những người khác tìm cách so sánh hai hoạt động thuần túy, không phá hủy, đây là điểm chuẩn của việc thêm mảng (array1 + array2) và mở rộng mảng ([*array1, *array2]). Cả hai, theo như tôi biết, kết quả trong 3 mảng được tạo ra: 2 mảng đầu vào, 1 mảng kết quả mới.

Gợi ý: + thắng.

# a1 is a function producing a random array to avoid caching 
a1 = ->(){ [rand(10)] } 
a2 = [1,2,3] 
n = 10_000_000 
Benchmark.bm do |b| 
    b.report('expand'){ n.times{ [*a1[], *a2] } } 
    b.report('add'){ n.times{ a1[]+a2 } } 
end 

quả

user  system  total  real 
expand 9.970000 0.170000 10.140000 (10.151718) 
add 7.760000 0.020000 7.780000 ( 7.792146)