10

Mechanizeを使用してGoogleのインデックスページを取得するスクリプトが2つあります。EventMachineはRubyスレッドよりも高速になると思いましたが、そうではありません。

EventMachineコードのコスト: "0.24s user 0.08s system 2% cpu 12.682 total"

Rubyスレッドコードのコスト: "0.22s user 0.08s system 5% cpu 5.167 total "

EventMachineを間違った方法で使用していますか?

EventMachine:

require 'rubygems'
require 'mechanize'
require 'eventmachine'

trap("INT") {EM.stop}

EM.run do 
  num = 0
  operation = proc {
    agent = Mechanize.new
    sleep 1
    agent.get("http://google.com").body.to_s.size
  }
  callback = proc { |result|
    sleep 1
    puts result
    num+=1
    EM.stop if num == 9
  }

  10.times do 
    EventMachine.defer operation, callback
  end
end

Rubyスレッド:

require 'rubygems'
require 'mechanize'


threads = []
10.times do 
  threads << Thread.new do 
    agent = Mechanize.new
    sleep 1
    puts agent.get("http://google.com").body.to_s.size
    sleep 1
  end
end


threads.each do |aThread| 
  aThread.join
end
4

4 に答える 4

25

このスレッドのすべての回答には、1つの重要なポイントがありません。コールバックは、個別の遅延スレッドではなく、reactorスレッド内で実行されています。呼び出しでMechanizeリクエストを実行するdeferことは、ループをブロックしないようにする正しい方法ですが、コールバックがループをブロックしないように注意する必要があります。

を実行するEM.defer operation, callbackと、Rubyで生成されたスレッド内で操作が実行され、作業が実行されます。その後、メインループ内でコールバックが発行されます。したがって、sleep 1inoperationは並列で実行されますが、コールバックは順次実行されます。これは、実行時間の9秒近くの違いを説明しています。

実行しているコードの簡略版を次に示します。

EM.run {
  times = 0

  work = proc { sleep 1 }

  callback = proc {
    sleep 1
    EM.stop if (times += 1) >= 10
  }

  10.times { EM.defer work, callback }
}

これには約12秒かかります。これは、パラレルスリープの場合は1秒、シリアルスリープの場合は10秒、オーバーヘッドの場合は1秒です。

コールバックコードを並行して実行するには、次のように使用するプロキシコールバックを使用して新しいスレッドを生成する必要がありますEM.defer

EM.run {
  times = 0

  work = proc { sleep 1 }

  callback = proc {
    sleep 1
    EM.stop if (times += 1) >= 10
  }

  proxy_callback = proc { EM.defer callback }

  10.times { EM.defer work, proxy_callback }
}

ただし、コールバックがイベントループ内でコードを実行することになっている場合は、別の遅延スレッド内で実行されるため、これで問題が発生する可能性があります。これが発生した場合は、問題のコードをproxy_callbackプロシージャのコールバックに移動します。

EM.run {
  times = 0

  work = proc { sleep 1 }

  callback = proc {
    sleep 1
    EM.stop_event_loop if (times += 1) >= 5
  }

  proxy_callback = proc { EM.defer callback, proc { "do_eventmachine_stuff" } }

  10.times { EM.defer work, proxy_callback }
}

このバージョンは約3秒で実行されました。これは、並列操作のための1秒のスリープ、並列コールバックのための1秒のスリープ、およびオーバーヘッドのための1秒に相当します。

于 2012-08-02T13:36:00.867 に答える
9

うん、あなたはそれを間違って使用しています。EventMachineは、すぐに戻り、完了時に「reactor」(EM.runによって開始されるイベントループ)に通知する非同期IO呼び出しを行うことによって機能します。システムの目的であるsleepとMechanize.getを無効にする2つのブロッキング呼び出しがあります。EventMachineから値を取得するには、特別な非同期/非ブロッキングライブラリを使用する必要があります。

于 2010-06-18T01:40:29.420 に答える
7

em-http-requesthttp://github.com/igrigorik/em-http-requestのようなものを使用する必要があります

于 2010-08-19T18:08:03.663 に答える
2

EventMachineの「defer」は、実際には、リクエストを処理するために管理しているスレッドプールからRubyスレッドを生成します。はい、EventMachineは非ブロッキングIO操作用に設計されていますが、deferコマンドは例外です。これは、reactorをブロックせずに長時間実行する操作を実行できるように設計されています。

つまり、実際にはEventMachineのスレッドプールマネージャーのオーバーヘッドでスレッドを起動するだけなので、裸のスレッドよりも少し遅くなります。

延期について詳しくは、 http://eventmachine.rubyforge.org/EventMachine.html#M000486をご覧ください。

とはいえ、ページのフェッチはEventMachineの優れた使用法ですが、他の投稿者が述べているように、ブロックしないIOライブラリを使用してから、next_tickなどを使用してタスクを開始する必要があります。これにより、タスクが中断されます。リアクターループの。

于 2012-01-14T01:09:17.863 に答える