処理を並列化したらデータベースアクセス速度が低下した謎 - @ledsun blog で「SidekiqのジョブをつかってCPUバウンドな処理を並列化したのに速度低下しておかしい。」みたいなことを書きました。 冷静に考えたら並列化されてません。 RubyのスレッドにはGlobal interpreter lock(GIL)*1があります。 ワーカーがスレッドで動くSidekiqでは、CPUバウンドな処理の並列化は出来ないのでした。 並列化するにはワーカーのプロセスをわける必要があります。
次の画像はhttps://sidekiq.org/products/enterprise.htmlにあるSidekiq EnterpriseのFeaturesです。
Multi-Core Processingが含まれています。 Sidekiqを使ってCPUバウンドな処理の並列化をしたい場合は課金すると良さそうです。
「Ruby並行並列大全」という大層な名前の技術同人誌を書いておきながら大前提に気がついていませんでした。 悔しいです。
なぜ気がついたのか、ふりかえっておきます。 冷静にというか、週末を挟んであらためて「DBへの保存処理をスキップ」したつもりの場所を見直しました。 実際はCPUバウンドな処理も一緒にスキップしてました。そら速いわけです。 で、再度「DBへの保存処理をスキップ」して計測したところ性能が全く変わりません。 これを見たときに、はたと気がつきました。 RubyにはGILがあります。 何でここで気がついたのかはよくわかりません。
7月20日「SidekiqのジョブをつかってCPUバウンドな処理を並列化」のアイデアを思いつきました。 それから20日近い期間全く気がついていませんでした。 つまり意識上にはGILの存在は欠片もありませんでした。
どうやら、次のような連想をしたように思います。
- 並列化処理からIOバウンドな処理を除いた
- CPUバウンドな処理だけ並列化したはず
- 速度低下がおきている
- CPUバウンドな処理の並列化が速度低下を起こしている
- GILじゃん
意識のどこかに「CPUバウンドな処理は高速化しているが、IOバウンドな処理がそれ以上に低速化している」という仮定が強くあったみたいです。 実験によりこの仮定が崩れたため、意識外にあったGILにたどり着いたみたいです。