byebugやpry-byebugを使った後の挙動を10倍高速にしました

byebugとpry-byebugのbundle updateをしましょう

byebugはRails標準でインストールされるRuby 2.1, 2.2向けのデバッガで、pry-byebugはpry *1 にデバッガの機能を追加するpryのプラグインです。 一昨日から今日にかけて、以下のパフォーマンス改善を含む byebug v8.0.0 と pry-byebug v3.3.0 がリリースされました。

github.com github.com

byebugとpry-byebugには、一度byebugbinding.pryを叩くとそれ以降ずっとアプリケーションの挙動が10倍遅くなるという問題がありました。 これが上記の変更により改善されたので、 Railsアプリやgemのデバッグにbyebugやpry-byebugを利用している方はそれらのbundle updateをおすすめします。

binding.pryすると10倍遅くなる問題

Byebugは行の移動やブレークポイントで処理を止めたりするため、Ruby 2.0から導入されたTracePoint *2 *3 というRubyのVMで起きるイベントに処理をフックさせるためのAPIを使っています。

byebugとpry-byebugはbyebugbinding.pryを叩いたときにByebug.startというメソッドの中で必要なTracePointを設定することによってデバッグを可能にしているのですが、 いままでのbyebugやpry-byebugはデバッグ終了後にデバッグ用のイベントフックを無効にしていなかった ため、一度byebugbinding.pryをするとそれ以降どんなアプリでも10倍程度遅くなってしまう状態でした。

github.com

なぜそんな状態だったのか

byebugとpry-byebugの作者はこの問題とどうしたら直るかを認識していたのですが、以下の理由により簡単には解決できない状態でした。

  • TracePointを無効にするためのByebug.stopがバグっていて、使うと大量にテストがfailする
  • TracePointを複数設定した後それらを無効にすると、freeしたTracePointを参照してしまうRuby自体のバグがありランダムにSEGVする

これらの問題をgdbを使って地道にデバッグを行って原因を特定し冒頭に載せたパッチによって問題が解決したため、デバッグ終了時にByebug.stopを呼ぶことが可能になりパフォーマンスが大幅に改善されました。なお、Ruby自体のSEGVに関しても原因を特定しパッチを投げています。

github.com

注意点

byebugやpry-byebugにはブレークポイントを設定する機能があるのですが、それを設定している間はByebug.stopしない(したらブレークポイントが動かなくなる)ためブレークポイントを使っていると遅いままになります。解除すればまた速くなります。

なお、Byebug.stopされているかはアプリケーション上でByebug.started?falseを返すかを見ることで確認することができます。 falseが返れば高速に動作しています。

所感

直すの結構大変だったので是非最新のbyebugとpry-byebugを使っていただきたい気持ちです。