#ruby-on-rails #ruby #performance
Вопрос:
Я ищу наиболее эффективный способ (по скорости) преобразования огромного количества объектов (1 млн экземпляров) в другой тип объектов. К сожалению, у меня нет выбора того, что я получаю в качестве входных данных (объект «миллион»).
До сих пор я пробовал, each_slice
но это не показывает большого улучшения, когда дело доходит до скорости!
Это выглядит так:
expected_objects_of_type_2 = []
huge_array.each_slice(3000) do |batch|
batch.each do |object_type_1|
expected_objects_of_type_2 << NewType2.new(object_type_1)
end
end
Есть идеи?
Спасибо!
Комментарии:
1. Каков фактический сценарий для этого? Массовый импорт?
2. Партии обрабатываются последовательно. Вы должны обрабатывать каждый кусочек в потоке.
3.
each_slice
похоже, это не очень хорошая идея — у вас уже есть весь исходный массив в памяти, поэтому наиболее эффективный способ его перебора-просто повторить 🙂each_slice
создаст много дополнительных массивов, которые затем нужно будет собрать — это добавляет некоторые ненужные накладные расходы.4. улучшение : Какого рода «улучшение» вы ищете? Скорость?
Ответ №1:
Я провел быстрый тест с несколькими различными методами зацикливания массива и измерил время:
huge_array = Array.new(10000000){rand(1..1000)}
a = Time.now
string_array = huge_array.map{|x| x.to_s}
b = Time.now
puts b-a
То же самое с:
sa = []
huge_array.each do |x|
sa << x.to_s
end
и
sa = []
huge_array.each_slice(3000) do |batch|
batch.each do |x|
sa << x.to_s
end
end
Понятия не имею, что вы конвертируете, поэтому я сделал немного простого int в строку.
Тайминги
Map: 1.7
Each: 2.3
Slice: 3.2
Так что, очевидно, ваши накладные расходы на срез замедляют процесс. Карта кажется самой быстрой (которая внутренне является просто циклом for, но с нединамическим массивом длины в качестве вывода). Это <<
, кажется, немного замедляет процесс.
Поэтому, если каждый объект нуждается в индивидуальном преобразовании, вы застряли с O(n) сложностью и не можете сильно ускорить процесс. Просто заплатил сверху.
В зависимости от ваших данных, сортировка и использование эффектов кэширования могут помочь или избежать дублирования, если у вас много идентичных данных, но у нас нет возможности узнать, не знаем ли мы ваши фактические конверсии.
Комментарии:
1. спасибо за ответ! На самом деле дубликатов нет, так что я не могу там много сэкономить… Интересно посмотреть, как карта работает здесь более эффективно. Я буду иметь это в виду!
Ответ №2:
Я бы рассматривал каждый срез в отдельной теме:
huge_array.each_slice(3000) do |batch|
Thread.new do
batch.each do |object_type_1|
expected_objects_of_type_2 << NewType2.new(object_type_1)
end
end
end
Затем вам нужно дождаться завершения использования потоков join
. Они должны быть собраны в массив и объединены.
Комментарии:
1. Это может быть даже медленнее, чем первоначальная реализация: из-за GIL в любой конкретный момент будет выполняться только один из созданных потоков накладные расходы на машинное оборудование потоков (включая стоимость переключения контекста).
2. Ах да, я и забыл об этом. Тогда я бы попробовал ракторы Ruby 3.
3. @KonstantinStrukov : Будет ли это применимо даже к системе с несколькими более чем одним ядром?