Jekyll と pygments.rb で Auto-regeneration が遅い

  • 2
    Like
  • 0
    Comment
More than 1 year has passed since last update.

2年ぶりに Jekyll を 0.11 から 2.4 へアップグレードしてみました。表題の通り、Pygments を利用した場合のビルドで問題があり、調査してみました。

利用バージョンは以下の通りです。

  • jekyll-2.4.0
  • pygments.rb-0.6.0

pygments.rb

Jekyll ではシンタックスハイライトに Pygments が選択できます。エンジンには pygments.rb が採用されています。

_config.yml
highlighter: pygments

pygments.rb は Python-2.x 系さえ入っていれば、Pygments をインストールしなくても、ローカル gem 内の vendor/pygments-main にビルドして、それを利用してくれます。

% ls vendor/bundle/ruby/1.9.1/gems/pygments.rb-0.6.0/vendor/pygments-main/
...
ez_setup.py
pygmentize
pygments
...

pygments.rb は、python プロセスを立ち上げたままにしておき、プロセス間でやりとりして変換を行っています。これにより変換の度に外部コマンドとして pygmentize を起動する場合と比較して、高速に変換できることが特徴です。あたかも Ruby 単体で動作しているように使えます。

html = Pygments.highlight(code, :lexer => lang)

Auto-regeneration での問題

Jekyll では --watch オプションにより、ファイル更新を検知して自動再生成 (= Auto-regeneration) を行なってくれます。ソースを更新する度にコマンドを叩く必要がないので、非常に便利なのですが、pygments.rb を経由すると、初回以降の再構築がとても遅いことに気づきました。

利用している pygments.rb-0.6.0 のソースを追って計測してみたところ Pygments::Popen.alive? というメゾッドで、プロセスが生きているかをチェックする際に Process.kill を利用しており、これがボトルネックになっていました。

計測すると、一回につき 0.1sec 程度かかっており、100 個のコードブロックを変換するためには、最低 10sec はかかってしまうことになります。Pygments モジュール内で、python 子プロセス ID 値を保持して死活監視をしているので、Jekyll 側で改善をすることは難しそうです。

かなり強引ですが、保持しているプロセスID値が nil でなければ、プロセスは生きているものと信頼し、Process.kill をスキップするように Pygments::Popen にパッチを当てれば、速度面での問題は解決します。Jekyll プラグインとして _plugins/converter.rb 等にパッチを記述しておけば、jekyll コマンド実行時に読み込んでくれます。

_plugins/converter.rb
require 'pygments'

module Pygments
  module Popen
    def alive?
      !!@pid
    end
  end
end

子プロセスが終了していたり、pygments.rb 自体の実装が変わってしまえば、この方法は使えませんが、開発時の暫定的なものであれば許容できると思います。

Auto-regeneration を無効にする

Jekyll は、都度すべてのソースから作り直します。更新により影響のあるファイルのみをビルドする仕組みではないので、更新を検知して再生成する制作サイクルは、リソースの無駄使いの面もあります。

無理にパッチを当てても、ファイル数が多ければ、結果的には足かせになってしまうので、手動コマンドでビルドするほうが無難だと思います。

開発サーバを起動する jekyll serve は、デフォルトで Auto-regeneration が有効です。無効にするには --no-watch を指定します。

% jekyll serve --no-watch