@ledsun blog

Hのキーがhellで、Sのキーがslaveだ、と彼は思った。そしてYのキーがyouだ。

RactorでWoker pool

Ruby 並行・並列くらべ 2 - @ledsun blog でRactorの並列化とプロセスの並列化が大体同じような性能がでることがわかりました。 Ractorに絞ってもっと性能が出ないか試してみます。 Ractorを作る数を制限していないので、物理コアをこえる数のスレッドを作っています。 それで非効率になっているのでしょうか?

https://github.com/ruby/ruby/blob/master/doc/ractor.md#worker-pool を参考にしてWorker poolを実装して確認してみましょう。

    Ractor.make_shareable(TextAlignment::CHAR_MAPPING)
    Ractor.make_shareable( TextAlignment::LCSMin::PLACEHOLDER_CHAR)
    pipe = Ractor.new do
      loop do
        Ractor.yield Ractor.receive
      end
    end

    workers = (1..4).map do
      Ractor.new pipe do |pipe|
        while msg = pipe.take
          a, d, o = msg
          m = Annotation.prepare_annotations!(a, d, o)
          Ractor.yield [m, a]
        end
      end
    end

    annotations_collection_with_doc = annotations_collection_with_doc.collect do |annotations, doc|
      pipe << [annotations, doc.dup, options]
      doc
    end.map do |doc|
      _r, (error_messages, annotations) = Ractor.select(*workers)
      messages += error_messages
      [annotations, doc]
    end

Worker poolをいれなかったときは次のソースコードでした。

    Ractor.make_shareable(TextAlignment::CHAR_MAPPING)
    Ractor.make_shareable( TextAlignment::LCSMin::PLACEHOLDER_CHAR)
    ractors = annotations_collection_with_doc.collect do |annotations, doc|
      r = Ractor.new do
        a, d, o = Ractor.receive
        m = Annotation.prepare_annotations!(a, d, o)
        Ractor.yield [m, a]
      end
      r.send [annotations, doc.dup, options]
      [r, doc]
    end

    annotations_collection_with_doc = ractors.map do |r, doc|
      error_messages, annotations = r.take
      messages += error_messages

      [annotations, doc]
    end

なかなか複雑になってきました。 実行してみます。

実行結果

1m 26sです。 Worker pool導入前は1m 32sでした。 7%位の差です。 他に何か原因がありそうです。