Лучше использовать EM.next_tick или EM.defer для длительных вычислений с помощью Eventmachine?

Я пытаюсь понять, как использовать deferrables, когда дело доходит до длительных вычислений, которые я должен реализовать самостоятельно. В моем примере я хочу вычислить первые 200 000 чисел Фибоначчи, но вернуть только определенное.

Моя первая попытка deferrable выглядела так:

class FibA
  include EM::Deferrable

  def calc m, n
    fibs = [0,1]
    i = 0

    do_work = proc{
      puts "Deferred Thread: #{Thread.current}"
      if i < m
        fibs.push(fibs[-1] + fibs[-2])
        i += 1
        EM.next_tick &do_work
      else
        self.succeed fibs[n]
      end
    }
    EM.next_tick &do_work
  end
end

EM.run do
  puts "Main Thread: #{Thread.current}"
  puts "#{Time.now.to_i}\n"

  EM.add_periodic_timer(1) do
    puts "#{Time.now.to_i}\n"
  end

  # calculating in reactor thread
  fib_a = FibA.new
  fib_a.callback do |x|
    puts "A - Result: #{x}"
    EM.stop
  end
  fib_a.calc(150000, 21)
end

Только чтобы понять, что все, казалось, работает довольно хорошо, но поток, в котором работает отложенный, такой же, как поток реактора (зная, что все выполняется внутри одного системного потока, если не используются rbx или jruby). Поэтому я придумал вторую попытку, которая кажется мне более приятной, особенно из-за другого механизма привязки обратного вызова и использования разных потоков.

class FibB
  include EM::Deferrable

  def initialize
    @callbacks = []
  end

  def calc m, n
    work = Proc.new do
      puts "Deferred Thread: #{Thread.current}"
      @fibs = 1.upto(m).inject([0,1]){ |a, v| a.push(a[-1]+a[-2]); a }
    end

    done = Proc.new do
      @callbacks.each{ |cb| cb.call @fibs[n]}
    end

    EM.defer work, done
  end

  def on_done &cb
    @callbacks << cb
  end
end

EM.run do
  puts "Main Thread: #{Thread.current}"
  puts "#{Time.now.to_i}\n"

  EM.add_periodic_timer(1) do
    puts "#{Time.now.to_i}\n"
  end

  # calculating in external thread
  fib_b = FibB.new
  fib_b.on_done do |res|
    puts "B - Result: #{res}"
  end
  fib_b.on_done do
    EM.stop
  end
  fib_b.calc(150000, 22)
end

Какую реализацию мне следует предпочесть? Оба неправы? Есть ли другой, лучший?

Еще более интересно: является ли вторая попытка идеальным способом реализовать все, что я хочу (кроме операций ввода-вывода) без блокировки реактора?


person GeorgieF    schedule 19.11.2013    source источник
comment
Зачем вам EventLoop для вычислений? EM — хороший выбор для неблокирующего сетевого ввода-вывода, но здесь он не нужен   -  person fl00r    schedule 19.11.2013
comment
Мое финальное приложение выполняет тяжелые операции ввода-вывода с Redis, MongoDB, сервером WebSocket, диском и несколькими конечными точками HTTP, используя соответствующие библиотеки em-library. К сожалению, во время перетасовки данных между ними мне приходится выполнять некоторые интенсивные вычисления с процессором/памятью, и я ищу способ реализовать их, не блокируя ни одного тика реактора. Но в любом случае, спасибо за ваш комментарий.   -  person GeorgieF    schedule 19.11.2013
comment
defer заблокирует ваше приложение, но не более чем на 10 мс каждый раз, когда планировщик потоков будет разблокировать его. defer — хороший выбор для блокировки ввода-вывода (т. е. системных вызовов), потому что они освобождают GIL, а ваши вычисления — нет. next_tick реализация вообще не должна блокироваться надолго.   -  person fl00r    schedule 19.11.2013


Ответы (1)


Определенно EM.defer (или, я полагаю, Thread.new), выполняющий длительные вычисления в EM.next_tick, заблокирует ваш реактор для других целей.

Как правило, вы не хотите, чтобы ЛЮБОЙ блок, работающий внутри реактора, работал долго, независимо от того, является ли он блокировкой ввода-вывода или нет, или все приложение останавливается, пока это происходит.

person bbozo    schedule 06.12.2013
comment
Спасибо за то, что приняли ответ, также, если вы все еще находитесь на ранней стадии своего проекта, подумайте о переходе на целлулоид :: io, поскольку eventmachine не разрабатывается активно уже 2 года, и, похоже, у вас возникнут проблемы с этим. , вы будете сами по себе (это особенно важно, если вы хотите использовать jruby, поскольку eventmachine + jruby в значительной степени находится в бета-версии) - person bbozo; 14.12.2013
comment
Есть ли целлулоид? - person Donato; 16.09.2016
comment
@Donato, Celluloid - это библиотека актеров, в некоторой степени похожая на Akka, у нее есть гибридное ответвление актера + события, называемое Celluloid IO - github.com/celluloid/celluloid-io. Одного целлулоида обычно достаточно, но сейчас я использую Concurrent Ruby, github.com/ruby-concurrency/ concurrent-ruby, с некоторыми вариантами дизайна в Celluloid было трудно жить, поэтому я пробую другое. - person bbozo; 16.09.2016
comment
Честно говоря, evented переоценен, особенно когда остальная часть экосистемы его не поддерживает, в конечном итоге вы все равно бросаете все в EM.defer блочные эквиваленты - если это история, вы могли бы вместо этого использовать актеры/фьючерсы/и т. д. - person bbozo; 16.09.2016
comment
Я планировал использовать sidekiq для создания клиента imap, который опрашивал пользователей ActiveRecord. Эти операции ввода-вывода могут вызвать значительную блокировку. Следовательно, автор sidekiq порекомендовал мне вместо этого использовать EventMachine (stackoverflow.com/questions/39459985/). Сейчас читаю ваши посты. И вы не рекомендуете eventmachine. Вы все еще рекомендуете Concurrent Ruby для моих нужд? Я уже потратил несколько дней на eventmachine. - person Donato; 16.09.2016
comment
Много хорошего программного обеспечения было написано с помощью EM, мои усилия не означают, что вы не можете сделать простой компонент, который будет делать одну вещь, и делать это хорошо, и не беспокоиться. Однако, если ваша работа будет расти, внедрять все больше и больше бизнес-логики и т. д., я бы подумал о других решениях, один из вариантов — использовать EM, Node или что-то еще, включая concurrent-ruby для создания супер простого коммуникационного компонента. который проходит через imap на одном конце и отправляет HTTP-запросы на другой сервис, который обрабатывает бизнес-логику. - person bbozo; 16.09.2016
comment
Создание сложных вещей с помощью EM — сука, позже я узнал, что создание сложных вещей с целлулоидом — тоже сука, теперь я возился с concurrent-ruby, суть в том, что эти вещи сложны и хитры, и их нужно извлекать, и сложные аспекты делегированная работа. Если вы можете - person bbozo; 16.09.2016