2010-01-24 31 views
5

Tôi là một người mới làm việc trong một ứng dụng Rails đơn giản, dịch một tài liệu (chuỗi dài) từ ngôn ngữ này sang ngôn ngữ khác. Từ điển là một bảng các thuật ngữ (một chuỗi regexp để tìm và thay thế, và một khối mà ouputs một chuỗi thay thế). Bảng dài 1 triệu bản.Rails nghi ngờ thiết kế: Nên/tôi có thể tải toàn bộ từ điển/bảng vào bộ nhớ?

Mỗi yêu cầu là tài liệu muốn dịch. Trong lần đầu tiên lực lượng tàn bạo cách tiếp cận tôi cần phải chạy toàn bộ từ điển đối với mỗi yêu cầu/tài liệu.

Vì từ điển sẽ chạy toàn bộ mọi thời gian (từ bản ghi đầu tiên đến lần cuối), thay vì tải bảng bản ghi từ điển bằng mỗi tài liệu, tôi nghĩ tốt nhất là có toàn bộ từ điển làm mảng trong trí nhớ.

Tôi biết nó không phải là hiệu quả nhất, nhưng từ điển phải chạy toàn bộ tại thời điểm này.

1.- Nếu không có hiệu quả có thể đạt được bằng cách tái cơ cấu tài liệu và từ điển (nghĩa là không thể tạo các tập con nhỏ hơn của từ điển). Cách tiếp cận thiết kế tốt nhất là gì?

2.- Bạn có biết các dự án tương tự mà tôi có thể học hỏi không?

3.- Tôi nên tìm cách tải bảng lớn như thế vào bộ nhớ (cache?) Khi khởi động đường ray ở đâu?

Mọi câu trả lời cho bất kỳ câu hỏi đặt ra nào sẽ được đánh giá cao. Cảm ơn nhiều!

Trả lời

2

Tôi không nghĩ rằng trình duyệt web của bạn sẽ hài lòng với giải pháp như thế này. Tập lệnh này

dict = {} 
(0..1000_000).each do | num | 
    dict[/#{num}/] = "#{num}_subst" 
end 

tiêu thụ một gigabyte RAM trên MBP để lưu trữ bảng băm. Một cách tiếp cận khác sẽ được lưu trữ thay thế của bạn marshaled trong memcached để bạn có thể (ít nhất) lưu trữ chúng trên máy.

require 'rubygems' 
require 'memcached' 
@table = Memcached.new("localhost:11211") 

retained_keys = (0..1000_000).each do | num | 
    stored_blob = Marshal.dump([/#{num}/, "#{num}_subst"]) 
    @table.set("p#{num}", stored_blob) 
end 

Bạn sẽ phải lo lắng về việc giữ gìn các phím "nóng" kể từ memcached sẽ hết hạn họ nếu họ không cần thiết. Tuy nhiên, cách tốt nhất là viết các tập tin thay thế của bạn vào một tập tin (một dòng cho mỗi lần thay thế) và tạo một bộ lọc luồng đọc từng dòng một, và thay thế từ tập tin này. Bạn cũng có thể song song điều đó bằng cách ánh xạ công việc trên điều này, ví dụ, mỗi chữ cái thay thế và đánh dấu thay thế.

Nhưng điều này sẽ giúp bạn bắt đầu:

require "base64" 

    File.open("./dict.marshal", "wb") do | file | 
    (0..1000_000).each do | num | 
     stored_blob = Base64.encode64(Marshal.dump([/#{num}/, "#{num}_subst"])) 
     file.puts(stored_blob) 
    end 
    end 

    puts "Table populated (should be a 35 meg file), now let's run substitutions" 

    File.open("./dict.marshal", "r") do | f | 
    until f.eof? 
     pattern, replacement = Marshal.load(Base64.decode64(f.gets)) 
    end 
    end 

    puts "All replacements out" 

Để cư tập tin và tải mỗi thay, điều này đưa tôi:

real 0m21.262s 
user 0m19.100s 
sys  0m0.502s 

Để chỉ nạp regexp và chuỗi từ tập tin (tất cả triệu, từng mảnh)

real 0m7.855s 
user 0m7.645s 
sys  0m0.105s 

Vì vậy, đây là 7 giây IO overhead, nhưng bạn không mất bất kỳ bản ghi nhớ nào ry (và có rất nhiều chỗ để cải tiến) - RSIZE là khoảng 3 meg. Bạn sẽ dễ dàng có thể làm cho nó đi nhanh hơn nếu bạn làm IO với số lượng lớn, hoặc làm cho một tập tin cho 10-50 thay thế và tải chúng như một toàn thể. Đặt các tập tin trên một SSD hoặc RAID và bạn có một người chiến thắng, nhưng bạn có thể giữ RAM của bạn.

+0

Cảm ơn bạn rất nhiều. Đó là một ý tưởng tuyệt vời mà tôi đã được mã hóa trong ứng dụng của tôi với kết quả tốt. – fjs6

1

Trong chế độ sản xuất, Rails sẽ không tải lại các lớp giữa các yêu cầu. Bạn có thể giữ một cái gì đó trong bộ nhớ một cách dễ dàng bằng cách đặt nó vào một biến lớp.

Bạn có thể làm một cái gì đó như:

class Dictionary < ActiveRecord::Base 
    @@cached = nil 
    mattr_accessor :cached 

    def self.cache_dict! 
    @@cached = Dictionary.all 
    end 
end 

Và sau đó trong production.rb:

Dictionary.cache_dict! 

Đối với câu hỏi cụ thể của bạn:

  1. Có thể viết một phần đó là không hiệu quả trong C hoặc Java hoặc ngôn ngữ nhanh hơn
  2. Không, xin lỗi. Có lẽ bạn có thể làm một thuật toán MapReduce để phân phối tải trên các máy chủ.
  3. Xem ở trên.
+0

Cảm ơn bạn, tôi đang suy nghĩ về một giải pháp hỗn hợp giữa một tệp được sắp xếp và phần đệm của nó khi bạn chỉ ra. – fjs6

0

Nếu bạn sử dụng thứ gì đó như cache_fu, bạn có thể tận dụng thứ gì đó như memcache mà không tự mình làm bất kỳ công việc nào. Nếu bạn đang cố gắng đưa 1 hàng MM vào bộ nhớ, việc có thể tận dụng bản chất phân tán của memcache có lẽ sẽ hữu ích.

+0

Cảm ơn. Tôi sẽ xem xét cache_fu – fjs6

1

Đây không phải là câu trả lời cụ thể cho một trong các câu hỏi của bạn như một đề xuất quy trình. Nếu bạn đang gặp phải (hoặc dự đoán) các vấn đề về hiệu suất, bạn nên sử dụng một trình thu thập thông tin từ phần khởi động.

Khám phá hướng dẫn này: How to Profile Your Rails Application.

Trải nghiệm của tôi về một số nền tảng (ANSI C, C#, Ruby) là vấn đề hiệu suất rất khó để xử lý trước trước; thay vào đó, bạn nên triển khai một cái gì đó trông giống như nó có thể được thực hiện sau đó tải thử nghiệm nó thông qua một hồ sơ.

Sau đó, khi bạn biết nơi thời gian của bạn đang được chi tiêu, bạn có thể dành một số nỗ lực để tối ưu hóa.

Nếu tôi phải đoán, tôi muốn nói rằng công việc regex bạn sẽ thực hiện sẽ là một nút cổ chai hiệu suất cao như bất kỳ ActiveRecord nào hoạt động. Nhưng mà không xác minh rằng với một hồ sơ, đoán đó là ít giá trị.

+0

Lời khuyên tuyệt vời. Cảm ơn bạn! – fjs6

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