@ledsun blog

無味の味は佳境に入らざればすなわち知れず

Ractor化する範囲を小さくしたら遅くなった

GCの影響は少なそう - @ledsun blog で、コピーする範囲を小さくすることで高速化できそうと考えました。 そこで、根本的にRactor化する範囲を小さくします。 そうすることでRactorで受け渡しするデータを最小化し、なんならmoveします。

次のようなソースコードにしました。

    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
          begin
            aligner = TextAlignment::TextAlignment.new(msg[:ref_text], msg[:options])
            aligner.align(msg[:text], msg[:denotations] + msg[:blocks])

            Ractor.yield(Ractor.make_shareable({
              index: msg[:index],
              denotations: aligner.transform_hdenotations(msg[:denotations]),
              blocks: aligner.transform_hdenotations(msg[:blocks]),
              lost_annotations: aligner.lost_annotations,
              block_alignment: aligner.block_alignment
            }), move: true)
          rescue => e
            Ractor.yield(Ractor.make_shareable({
              error: e
            }))
          end
        end
      end
    end

    annotations_collection_with_doc.each do |annotations, doc|
      ref_text = doc&.original_body || doc.body
      results = {}

      targets = annotations.filter {|a| a[:denotations].present? || a[:blocks].present? }
      targets.each_with_index do |annotation, index|
        # align_hdenotations
        text = annotation[:text]
        denotations = annotation[:denotations] || []
        blocks = annotation[:blocks] || []

        pipe.send(Ractor.make_shareable({
          index: index,
          ref_text: ref_text,
          text: text,
          denotations: denotations,
          blocks: blocks,
          options: options
        }), move: true)
      end.each do |annotation|
        _r, result = Ractor.select(*workers)

        if result[:error]
          raise "[#{annotation[:sourcedb]}:#{annotation[:sourceid]}] #{result[:error].message}"
        else
          results[result[:index]] = result
        end
      end

      messages << targets.map.with_index do |annotation, index|
        result = results[index]
        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.compact
    end

これで速くなるはずです。 うごかしてみます。

Ractor化する範囲を限定した時の計測結果

比較のためにRactorを使わない時の結果を載せます。

Ractor化する前の計測結果

2m14s -> 7m 41s です。 とても遅くなりました。

え?そんなことあるの?