Я - новичок, работающий в простом приложении для направляющих, которое переводит документ (длинная строка) от языка до другого. Словарь является таблицей условий (строка regexp, чтобы найти и занять место, и блок что ouputs занимающая место строка). Таблица является 1 миллионом записей долго.
Каждый запрос является документом, который хочет быть переведенным. В первом жестоком подходе силы я должен выполнить целый словарь против каждого запроса/документа.
Так как словарь будет работать целый каждый раз (от первой записи до последнего), вместо того, чтобы загрузить таблицу записей словаря с каждым документом, я думаю, что лучшее должно было бы иметь целый словарь как массив в памяти.
Я знаю, что это не является самым эффективным, но словарь должен работать целый в этой точке.
1. - Если никакая эффективность не может быть получена путем реструктуризации документа и словаря (значение его не возможно создать меньшие подмножества словаря). Каков лучший подход дизайна?
2. - Вы знаете подобных проектов, что я могу извлечь уроки из?
3. - Где должен, я надеюсь изучать, как загрузить такую большую таблицу в память (кэш?) при запуске направляющих?
Любой ответ на любой из поставленных вопросов будет значительно цениться.Большое спасибо!
Я не думаю, что ваш веб-хостер будет счастлив с таким решением. Этот скрипт
dict = {}
(0..1000_000).each do | num |
dict[/#{num}/] = "#{num}_subst"
end
потребляет гигабайт оперативной памяти на моем MBP для хранения хэш-стола. Другой подход будет хранить ваши замены, маршала в мемкачене, чтобы вы могли (по крайней мере) хранить их через машины.
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
Вам придется беспокоиться о том, чтобы держать ключи «горячими», поскольку Memcached истекает их, если они не нужны.
Однако наилучший подход к вашему делу было бы очень простым - напишите свои замены в файл (одна строка на замену) и сделайте поток-фильтр, который считывает строку файла по строке и заменяет из этого файла. Вы также можете распараллелизовать, что путем сопоставления работы по этому вопросу, скажем, за букву замены и замены маркеров.
Но это должно начать:
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"
, чтобы заполнить файл и загрузить каждое замещение, это требует меня:
real 0m21.262s
user 0m19.100s
sys 0m0.502s
, чтобы просто загрузить Regexp и строку из файла (все миллион, штуку по частям)
real 0m7.855s
user 0m7.645s
sys 0m0.105s
Итак, это 7 секунд IO накладных расходов, но вы не теряете никакой памяти (и есть огромный номер для улучшения) - Rsize составляет около 3 меев. Вы должны легко сделать его быстрее, если вы сделаете IO навалом или сделаете один файл за 10-50 замещений и загрузить их в целом. Поместите файлы на SSD или RAID, и у вас есть победитель, но вы можете сохранить свою оперативную память.
В режиме производства рельсы не будут перезагружать классы между запросами. Вы можете легко сохранить в памяти, надевая его в классную переменную.
Вы могли бы сделать что-то вроде:
class Dictionary < ActiveRecord::Base
@@cached = nil
mattr_accessor :cached
def self.cache_dict!
@@cached = Dictionary.all
end
end
, а затем в Production.RB:
Dictionary.cache_dict!
для ваших конкретных вопросов:
Если вы используете что-то вроде cache_fu, то вы можете использовать что-то вроде memcache, не выполняя при этом никакой работы самостоятельно. Если вы пытаетесь занести в память 1 ММ строку, то, вероятно, будет полезно использовать распределенную природу memcache.
Это не столько конкретный ответ на один из ваших вопросов, сколько рекомендация по процессу. Если у вас возникли (или вы ожидаете) проблемы с производительностью, вам следует использовать профилировщик с самого начала.
Ознакомьтесь с этим руководством: Как профилировать ваше приложение Rails .
Мой опыт работы с рядом платформ (ANSI C, C #, Ruby) показывает, что проблемы с производительностью очень трудно решать заранее ; скорее, вам лучше реализовать что-то, что выглядит так, как будто оно могло бы быть эффективным, а затем проводить нагрузочное тестирование с помощью профилировщика.
Затем, когда вы узнаете , на что тратите ваше время, вы можете приложить некоторые усилия для оптимизации.
Если бы мне пришлось угадывать, я бы сказал, что работа с регулярным выражением, которую вы будете выполнять, будет таким же узким местом производительности, как и любая работа с ActiveRecord. Но без проверки профилировщиком это предположение не имеет большого значения.