Octopress(Jekyll)はビルドするのに物凄く時間がかかるので 特に記事数が増えてくると嫌になってしまって 他に移った、と言う話が最近良く見かける様になりました。
サイドバーのランダムリストのアップデート
でさらに大幅に時間がかかるようになってしまったりして
ちょっとどうしようかと思いましたが、
Octopressのgenerate
タスクを大幅にスピードアップできたのでそれについて。
Octopressで時間がかかっている部分
以前、ちょっとOctopressのビルドが流石に時間かかって困るな、となってきた時、 どの辺が時間かかってるのか調べてみました。
結局の所、実際にマークダウンをレンダリングしてさらにLiquidタグの変換とかをしているrender
の部分に時間がかかっていて、この部分はJekyll内、さらには
MarkdownのパーサーとLiquidの仕様の問題なのでどうしようもないかな、
という点がありました。
他のMarkdownで作るブログツールなんかだともっと高速なものがいくらでもあるので、 どちらかと言うとLiquidの方だと思います。
今回、 ランダムリストを埋め込んだ 上でもう一度測ってみました。
この前作ってた計測用のパッチがあれだったので 以下の様な感じのパッチを作って pluginsディレクトリに突っ込んで計測しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
|
追記: 2015/06/28
render
メソッドにパッチを宛てる場合には少し注意が必要で、
Octopressでは
Octopress Hooks
というツールを使ってJekyllのサイト生成コマンドの各段階への
Hookを入れられる様な機能を追加しているのですが、
この中でrender
メソッドに手を加えています。
上の様にパッチを宛ててしまうと plugins側の方が後から読み込まれるので Octopress Hooksでの上書きが無視されHookが効かなくなります。
これを避けるために
if defined?(Octopress::Hooks) and defined?(old_render)
の部分を追加しました。
ただ、これもOctopress Hooksの中の上書き方法をトレースしてる形なので、 Octopress Hooksのアップデートにも注意して使う必要があります。
追記ここまで
これで測ってみると
## GEnerating Site with Jekyll
Configuration file: /pipeline/build/_config.yml
Source: source
Destination: ./public
Generating...
reset: 00:00:00
read: 00:00:00
generate::GoogleAnalytics: 00:00:03
generate::ShareNumbers: 00:01:53
generate::RelatedPostsGenerator: 00:00:00
generate::GemojiGenerator: 00:00:00
generate::GenerateTags: 00:00:13
generate::GenerateCategories: 00:00:03
generate::GenerateMontly: 00:00:05
generate::PopularPosts: 00:00:00
generate::JekyllSitemap: 00:00:00
generate::Pagination: 00:00:00
generate::JekyllVarToJs: 00:00:00
generate: 00:02:20
render::posts-pages Time: 00:18:28 |===========================| 100%
render: 00:18:28
cleanup: 00:00:00
write: 00:00:00
done.
こんな感じになっています。 wercker で行った結果です。
ソーシャルボタンの数をビルド時に取ってくる
の所で書いたとおり、
generate::ShareNumbers:
の所は8つのスレッドで実行してこの時間です。
(シングルスレッドだとこれも15分以上かかったりします。。。)
やはり一番時間がかかっているのはrender
の部分で、
各ページを変換してる部分です。
ちなみにこの部分もParallel
を使って行けるかと思ったんですが、
octopress/plugins/include_array.rb:44: warning: conflicting chdir during another chdir block
octopress/plugins/include_array.rb:44: warning: conflicting chdir during another chdir block
みたいなwarningが出てたので取り敢えずやめておきます。 (ちょっと注意すればなんとかなりそうなものですが。)
ランダムポストを入れる前はここが5分位になっていましたので 4倍位にはなってます。
前回書いた通りwerckerは1つのステップで60分まで使える様になったので まだその点では問題無いですが、 やはり毎回これだけかかると困りますし、 ローカルでは絶対ビルドしたくありません。
改善策
取り敢えずレンダリングしてる所が遅いわけですが、 JekyllやLiquidの仕様の中まで除いて色々するのはまだ手が出ないところなので もっと簡単に出来そうな事をやってみます。
ランダムリストを入れて時間がかかったのは、 各ページに全てのポストの一覧を埋め込もうとしたからです。
この一覧は全てのページに対して共通なので、これを 1回だけレンダリングして付け加えられないか、と考えます。
よく考えてみると現状サイドバーはページ固有の物は載ってないので、 サイドバー全体を別にレンダリングして最後に各ページに貼り付ける、みたいなことをしてみます。
まず、サイドバー用のページとして source/common/common_sidebar.htmlというファイルを
1 2 3 4 5 6 7 8 |
|
こんな感じで作ります。 普通のサイドバー部分の内容です。
追記: 2015/06/21
このままだとこの消すページもsitemap.xmlに載ってしまうので、
それを避けるためにYAMLブロックに
上の様にsitemap: false
を足しておきます1。
追記ここまで
これで普通にjekyll build
するとサイドバー部分だけが
public/common/common_sidebar.htmlに出来ます。
次に、埋め込む部分として、source/_layouts/post.htmlを
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
という感じに変更します。
ここでdefault_asides
を使ってる部分を先ほどの
common_sidebar.htmlに移して
そこをCOMMON_SIDEBAR
で置き換えた形になっています。
ページの方も同様に
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
もし、ページとポストで別々のサイドバーを使ってたら それぞれcommon_page_sidebar.htmlとか作って 同様にpage.htmlとかに埋め込んでください。
これでjekyll build
を下直後はサイドバーは現れず、
COMMON_SIDEBAR
という文字列だけが出ます。
同じような事をヘッダーに関しても行います。
1 2 3 4 5 6 |
|
こんな感じの共通パーツを用意して、 source/_layouts/default.htmlを
1 2 3 |
|
こんな感じで変更。
次に、Rakefileに以下の様なタスクを加えます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
さらにRakefileの先頭の方に
common_words = ["COMMON_HEADER", "COMMON_SIDEBAR"]
common_dir = "#{public_dir}/common"
と言う行を加えます。
これでgenerate
タスクのjekyll build
の後に
system "jekyll build"
Rake::Task[:common].execute
とやってあげると全部置き換えてくれます。
これで実際ビルドしてみると
render::posts-pages Time: 00:01:15 |===========================| 100%
render: 00:01:15
と、一分ちょっとになりました!
commmon
タスク自体は450ページほどあっても1秒もかからず終わります。
これはランダムポストを入れる前でも5分位かかってたので
普通の使い方をしていても
ヘッダーとサイドバーが共通なものであれば
五分の一位までgenerate
時間を短縮出来る事になります。
1分程度であれば手元で行ってもまあ許せる程度でもあります。
ということで原理的にこういったやり方でOctopressのビルドを劇的に スピードアップできます。
今回はサイドバーとヘッダーだけでしたが、 他にもまとめられる物は特に中でLiquidタグを多用している部分はまとめたら速くなります。
その他の改善案
タグを減らす(特にinclude
)
Octopress (Liquid)のfor文内等でタグを使う時の注意
で見ましたが、どうもJekyll(というかLiquid)の仕様として、
include
タグで他のファイルを埋め込むとき、
全てを埋め込んでからそのページをレンダリングするのではなく、
include
まで行くと、その埋め込むファイルを開いてそのファイルをレンダリングして、
さらにその中にinclude
があればそれをまた開いてレンダリングして。。。
みたいな事をやってるみたいなので、
include
の入れ子のしすぎは遅くなりそうです。(きちんと見てないので勝手な予測ですが)
ただ、少なくともLiquidタグの負担は結構大きいみたいなのでなるべく減らした方が良く、 source/_includes内の整理をすることでもっと速くすることは出来そうです。
posts-pages
でParalleslを使う
関係ない別のページをレンダリングしている際には並列で走らせてもLiquidタグの変換自体は大丈夫そうなもんですが、
上でParallel
がposts-pages
の所で使えないことを書きました。
どうもinclude_array.rbの中でDir.chdir
してる所があるので
ここで引っかかってるみたいですが、このプラグインはOctopressの物なので
なんとかなりそうな感じはあります。
追記: 2015/06/18
plugins/include_array.rbをこんな感じで変えてやることで 上で出てたwarningを出さないように出来ました。
1 2 3 4 5 6 7 8 9 |
|
ただ、手元の環境だとこれで上手く行くのですが、 werckerだと
Liquid Exception: lexical error: invalid string in json text. n ID."} 00000000000000000000000 (right here) ------^ in _posts/y_2013/2013-03-07-setup-octopress.md/#excerpt | 0%
Liquid Exception: Pygments can't parse unknown language: html. in _posts/y_2013/2013-03-10-pages.md/#excerpt
/pipeline/build/plugins/pygments_code.rb:27:in `rescue in pygments': Pygments can't parse unknown language: html. (RuntimeError)
...
みたいなエラーが出ました。 ちょっと手元の環境との違いをきちんとみて調べてみます。。。
追記ここまで
COMMONをJekyllの中に埋め込む
今回COMMON_SIDEBAR
みたいな単なる文字列の置き換えは
上にも書き換えましたがこの文字列を実際他の所で一切書けなくなる、
という問題があるのでちょっと考えないといけません。
なるべく使わないような長い名前にする、というのも一つの手ですが余りすまーとではないです。
Octopressで’ダブルハイフン’をそのまま残す
の追記に書きましたが、
OctopressではJekyllの各段階の前後にタスクを挟み込む
octopress/hooks
というツールを用意しているので、
これを使ってrender
の後に上手く入れ込める様な事が出来ないかな、とも。
それもタグを上手いこと使って他と区別できるような感じで。
まとめ
まあまだ色々と改善の余地はありそうですが、
取り敢えずCOMMON
な差し込みだけでも劇的に速くなったので大分満足。
取り敢えずこれで手元でもちょっと気になったら全部をビルドしてみる、 ということも気軽にできる様になりました。