2010-03-13 30 views
12

Một đồng nghiệp cần sắp xếp một mảng các đối tượng ActiveRecord trong ứng dụng Rails. Ông đã cố gắng rõ ràng Array.sort! nhưng nó dường như đáng ngạc nhiên chậm, lấy 32s cho một mảng của 3700 đối tượng. Vì vậy, trong trường hợp nó là những vật thể béo lớn làm chậm mọi thứ, anh ta reimplemented sắp xếp bằng cách sắp xếp một mảng các đối tượng nhỏ, sau đó sắp xếp lại mảng ban đầu của các đối tượng ActiveRecord để khớp - như được hiển thị trong mã bên dưới. Tada! Các loại bây giờ mất 700ms.Ruby: Tại sao Array.sort lại làm chậm đối tượng lớn?

Điều đó thực sự làm tôi ngạc nhiên. Phương pháp sắp xếp của Ruby có kết thúc sao chép các đối tượng về địa điểm thay vì chỉ tham chiếu không? Anh ấy đang sử dụng Ruby 1.8.6/7.

def self.sort_events(events) 
    event_sorters = Array.new(events.length) {|i| EventSorter.new(i, events[i])} 
    event_sorters.sort! 
    event_sorters.collect {|es| events[es.index]} 
end 

private 

# Class used by sort_events 
class EventSorter 
    attr_reader :sqn 
    attr_reader :time 
    attr_reader :index 

    def initialize(index, event) 
    @index = index 
    @sqn = event.sqn 
    @time = event.time 
    end 

    def <=>(b) 
    @time != b.time ? @time <=> b.time : @sqn <=> b.sqn 
    end 
end 
+1

của bạn '' <=> phương pháp cũng có thể được viết như sau: '(@time <=> b.time) .nonzero? hoặc @sqn <=> b.sqn' –

+2

Nhật ký ghi lại hoạt động có hiển thị bất kỳ điều gì thú vị xảy ra trong quá trình sắp xếp không? Hãy chắc chắn rằng nó được cấu hình để đăng nhập truy vấn cơ sở dữ liệu. –

+0

Glenn - Cảm ơn bạn đã có mẹo trên <=>. Wayne - Tôi nghĩ bạn có thể có câu trả lời. Sau khi không nhận được bất kỳ câu trả lời dứt khoát nào ở đây, tôi đã giả lập một kịch bản thử nghiệm nhỏ để sắp xếp một số đối tượng ActiveRecord lớn (được đệm bằng một số chuỗi ngẫu nhiên) và sau đó lặp lại sắp xếp bằng cách sử dụng kỹ thuật ở trên. Không có cải thiện chút nào. Vì vậy, vào thứ hai tôi sẽ đề nghị với đồng nghiệp của tôi rằng anh ta có một cái nhìn cho các tác dụng phụ trong quá trình sắp xếp. –

Trả lời

6

sort chắc chắn không sao chép các đối tượng. Một sự khác biệt mà tôi có thể tưởng tượng giữa mã sử dụng EventSorter và mã mà không có nó (mà bạn không cung cấp, vì vậy tôi phải đoán) là EventSorter gọi event.sqnevent.time chính xác một lần và lưu trữ kết quả trong biến. Trong quá trình phân loại chỉ các biến cần phải được truy cập. Phiên bản gốc có lẽ được gọi là sqntime mỗi lần chặn phân loại được gọi.

Nếu trường hợp này xảy ra, nó có thể được sửa bằng cách sử dụng sort_by thay vì sắp xếp. sort_by chỉ gọi khối một lần cho mỗi đối tượng và sau đó sử dụng kết quả được lưu trong bộ nhớ cache của khối để so sánh thêm.

+0

Bạn đoán đúng - Sự kiện có phương thức gần giống hệt nhau <=> cho EventSorter, nhưng trong trường hợp Sự kiện, sqn và thời gian là tên của các cột trong cơ sở dữ liệu. Điều đó có nghĩa là Rails/ActiveRecord cung cấp các phương thức sqn và time, có vẻ như phân tích cú pháp các giá trị trong thuộc tính của hàm băm ActiveRecord mỗi khi chúng được gọi. Vì vậy, mỗi lần tổ chức sự kiện. <=> được gọi là ActiveRecord đã phân tích chuỗi thời gian thành đối tượng Thời gian Ruby, do đó hiệu suất khủng khiếp. Bí ẩn đã được giải quyết! Cảm ơn bạn. –

0

Không có câu hỏi nào về câu trả lời như thế này tốt hơn mã nguồn ngôn ngữ thực tế. Mảng # sắp xếp! sử dụng sort_internal() được định nghĩa trong array.c:

sort_internal()

(Vâng, tôi biết đó là nguồn cho 1.8.4 nhưng tôi không thể tìm thấy những cái đúng 1.8.6 trực tuyến và khá chắc chắn này đã không thay đổi.)

+1

Tiếp tục - cho tôi manh mối! Tôi không đủ thông thạo trong C để làm được điều này. –

+0

Ồ, xin lỗi vì điều đó! Về cơ bản nó sử dụng sắp xếp nhanh chóng, đó là giữa O (N^2) (trường hợp xấu nhất) và O (N log N) (trường hợp tốt nhất). –

+3

Nhưng điều đó dường như không giải thích tại sao nó sắp xếp chậm hơn một mảng các đối tượng lớn hơn là một mảng các đối tượng nhỏ.Việc thực hiện yêu cầu sao chép các đối tượng xung quanh heap hơn là chỉ đơn giản là sắp xếp lại con trỏ? –

2

Cũng giống như giải thích về những gì có thể xảy ra và làm thế nào để đối phó với nó ...

Sorting có xu hướng nhìn vào một yếu tố nhiều lần vì vậy một tra cứu tốn kém vào đối tượng hoặc cấu trúc sẽ trở nên rất tốn kém rất nhanh chóng .

Biến đổi Schwartzian thường được sử dụng khi sắp xếp mảng các đối tượng hoặc cấu trúc phức tạp. Ý tưởng cơ bản là tính toán trước một giá trị đơn giản phản ánh chính xác cấu trúc hoặc đối tượng lớn, sau đó sắp xếp các giá trị, sau đó sử dụng mảng được sắp xếp kết quả để tham chiếu đến thứ mà chúng xuất phát.

http://en.wikipedia.org/wiki/Schwartzian_transform

Các vấn đề liên quan