並列数をかえても処理時間が変わらない謎 - @ledsun blog で「サチっているかもしれない」と仮説を立てました。 それを検証するために並列数を変えて計測してみました。
処理時間なので低いほど性能が高いです。 3どころか1並列でサチっています。 *1
1なのは予想外です。 予想外なので、この1がヒントになりそうです。 今回の並列処理の構成上1に該当する物があります。 並列数を管理するためにワーカープールがあります。 ワーカーへのデータの受け渡しにpipeを使っています。 ソースコードは次です。
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, ref_text, o = msg aligner = TextAlignment::TextAlignment.new(ref_text, o) m = a.map do |annotation| Annotation.align_annotations!(annotation, ref_text, aligner) end.flatten Ractor.yield [m, a] end end end annotations_collection_with_doc = annotations_collection_with_doc.collect do |annotations, doc| ref_text = doc.original_body.nil? ? doc.body : doc.original_body pipe.send([annotations, ref_text, options], move: true) doc end.map do |doc| _r, (error_messages, aligned_annotations) = Ractor.select(*workers) messages += error_messages [aligned_annotations, doc] end
つまりどのワーカーにデータを送るにしても、かならず1つのpipeを通ります。 pipeは1つなのです。 1は怪しい数字です。
pipeはボトルネックになり得るのでしょうか? RactorのsendはデフォルトではCopyです。
https://github.com/ruby/ruby/blob/master/doc/ractor.md#communication-between-ractors
You can choose "Copy" and "Move" by the move: keyword, Ractor#send(obj, move: true/false) and Ractor.yield(obj, move: true/false) (default is false (COPY)).
前述のソースコードのとおりオプションはつけていません。 Ractor間のデータのやりとりはCopyです。 CopyはMoveよりは遅いです。 「ワーカーがCopy待ちしている」という仮説はあり得そうです。
今回の処理では、ワーカーは受け取ったデータを変更しますが、メインRactorはワーカーの処理がおわってから、データを参照します。 Ractor間のすべてのデータの受け渡しをMoveにできるはずです。
もしかして、ついに突破口が見つかったのでしょうか?
*1:ちなみに0は並列化していない、直列処理です。比較しやすくするために0に置いてあります。