Ractor化する範囲を小さくしたら遅くなった - @ledsun blog で、Ractor化する範囲を小さくしたら処理が遅くなって混乱しました。 よくよくソースコードを確認したところ、変わっている場所がありました。
aligner = TextAlignment::TextAlignment.new(msg[:ref_text], msg[:options]) aligner.align(msg[:text], msg[:denotations] + msg[:blocks])
ここです。 元は次でした。
aligner = TextAlignment::TextAlignment.new(ref_text, o) copy_a = Marshal.load(Marshal.dump(a)) m = copy_a.map do |annotation| Annotation.align_annotations!(annotation, ref_text, aligner) end.flatten
TextAlignment::TextAlignment.new
がループの中に入っています。
このインスタンス生成が遅くなった原因のようです。
確認のために次のようにソースコードを修正しました。
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 aligner = TextAlignment::TextAlignment.new(msg[:ref_text], msg[:options]) results = msg[:data].map do |datum| begin aligner.align(datum[:text], datum[:denotations] + datum[:blocks]) { denotations: aligner.transform_hdenotations(datum[:denotations]), blocks: aligner.transform_hdenotations(datum[:blocks]), lost_annotations: aligner.lost_annotations, block_alignment: aligner.lost_annotations.present? ? aligner.block_alignment : nil } rescue => e { error: e } break end end Ractor.yield(Ractor.make_shareable({ index: msg[:index], results: results }), move: true) end end end annotations_collection_with_doc.each_with_index do |a_and_d, index| annotations, doc = a_and_d ref_text = doc&.original_body || doc.body results = {} targets = annotations.filter {|a| a[:denotations].present? || a[:blocks].present? } data = targets.map do |annotation| # align_hdenotations text = annotation[:text] denotations = annotation[:denotations] || [] blocks = annotation[:blocks] || [] { text: text, denotations: denotations, blocks: blocks } end pipe.send(Ractor.make_shareable({ index: index, ref_text: ref_text, options: options, data:data })) end.each do |annotations, doc| _r, results = Ractor.select(*workers) annotations, doc = annotations_collection_with_doc[results[:index]] ref_text = doc&.original_body || doc.body targets = annotations.filter {|a| a[:denotations].present? || a[:blocks].present? } messages << results[:results].map.with_index do |result, i| if result[:error] raise "[#{annotation[:sourcedb]}:#{annotation[:sourceid]}] #{result[:error].message}" else annotation = targets[i] annotation[:denotations] = result[:denotations] annotation[:blocks] = result[:blocks] annotation[:text] = ref_text annotation.delete_if{|k,v| !v.present?} if result[:lost_annotations].present? { sourcedb: annotation[:sourcedb], sourceid: annotation[:sourceid], body:"Alignment failed. Invalid denotations found after transformation", data:{ block_alignment: result[:block_alignment], lost_annotations: result[:lost_annotations] } } else nil end end end.compact end
計測してみます。
比較のため非Ractor版と修正前のRactor版も載せます。
Ractor化する前よりは速くなりました。 修正前よりは遅いです。 まだ、なにか見落としている差分がありそうです。
とはいえ、オブジェクトのディープコピーを減らしてもめざましい効果がないことがわかりました。 「Ractor間のコピーに時間が掛かる」という説は間違っていそうです。 もしかするとRactor間でデータを受け渡すための入れ物として配列やハッシュを作っている部分がコストになっているのでしょうか?