概要
Ruby は GVL の関係で基本はスレッド化してもシングルコアでしか動きません
てっとり早くマルチコア化した場合は parallel を使います
環境
- macOS 15.1.1
- Ruby 3.3.5
- parallel 1.26.3
Thread 化する前のコード
配列を単純に順番に処理する関数です
スレッド化することを前提にコードを作成しており add はインスタンス変数の配列にデータを登録するよういになっています
またインスタンス変数は基本的に順番を保証しないことを考慮した作りにしたほうがいいです (Thread でどんどん処理されるので)
def parse_and_add_tweet(full_text)
tweet = Tweet.new(full_text, @mecab)
tweet.analyze
add(tweet)
end
def exec
@data.each do |d|
parse_and_add_tweet(d['tweet']['full_text'])
end
end
通常の Thread
配列のデータをスライスしてスライスしたデータをスレッドに渡して処理させるようなコードに変更します
thread_batch_size は作成するスレッド数ごとにデータが均等に割り当たるようにしています
def thread_batch_size
(@data.size / thread_count.to_f).ceil
end
def thread_count
32
end
def parse_and_add_tweet(full_text)
tweet = Tweet.new(full_text, @mecab)
tweet.analyze
add(tweet)
end
def analyze
threads = []
@data.each_slice(thread_batch_size) do |sliced_data|
threads << Thread.new do
sliced_data.each do |d|
parse_and_add_tweet(d['tweet']['full_text'])
end
end
end
threads.each(&:join)
end
これで実行するとわかりますがシングルコアの CPU を100%使用しますが残りのコアは何もしていないことが確認できます
parallel を使う
parallel_process_count は CPU コア数になります
こちらの方が単純に書くことができます
また実行するとすべての CPU が使われ 100% になっていることが確認できると思います
def parallel_process_count
Parallel.processor_count
end
def parse_and_add_tweet(full_text)
tweet = Tweet.new(full_text)
tweet.analyze
add(tweet)
end
def analyze
@tweets = Parallel.map(@data, in_processes: parallel_process_count) do |d|
parse_and_add_tweet(d['tweet']['full_text'])
end
end
parallel を使う際は少し工夫が必要なのでそれに関しては後述のトラブルシューティングに記載しています
結果比較
通常 の Thread
1775.98s user 2.64s system 99% cpu 29:40.44 total
parallel を使う
3552.51s user 269.73s system 701% cpu 9:04.50 total
3-4倍くらい速くなりました
トラブルシューティング
'dump': no _dump_data is def ined for class FFI::Pointer (TypeError)
parallel にわたすブロックに Marshal.dump できない値は渡せません
今回で言うと mecab オブジェクトが FFI::Pointer をクラスを使っており渡せないので mecab オブジェクトの生成は Tweet クラス側の initialize ではなく各種関数側で行うように修正しました
最後に
- そんなに処理時間がかからない -> 通常のループ
- シングルコアは MAX に使いたい -> Thread を使ったループ
- マルチコアであるだけリソースを使いたい -> parallel を使ったループ
という感じで使い分けるといいかなと思います
parallel の場合ほぼリソースを食い尽くすことができてしまうので他のプロセスの邪魔にならないように注意しましょう
0 件のコメント:
コメントを投稿