2015で「Hamlは遅いしメンテされてないので使わない方がいい」と言ったところ、じゃあ自分でメンテして速くしろということになりコミッターになった*1。当時から2年ごしなのは、当時のHamlのオーナーがあまりアクティブではなく、最近a_matsudaさんがオーナーになったため。HamlのTemple化・高速化を行ったTempleというのは、テンプレートエンジンをパイプライン的に構築するためのフレームワークで、テンプレートエンジン用の中間表現とその最適化エンジンを持つ。実装をTempleベースにすると、SlimやHamlitに使われているような中間表現を使った最適化を適用しやすくなる。2015でマージされないと言っていたパッチを自分でマージし、コード生成とattributeのコンパイルをTemple化しながら高速化した*2。それ以外にもいろいろ遅い原因を調べて改善した。5.0.0.beta.2がリリースされた。同じgem同士であるHaml4とHaml5を同時に比較するためのリポジトリを作っていて、これはslim-template/slimに入っているベンチマークからHTMLエスケープの回数が違う問題を修正したもの。4.0.7と現在のHamlのmasterを比較すると以下のようになる。slowerというわけで、masterだとHaml4の3.71倍速くなっている*3。本当はmasterじゃなくて5.0.0.beta.2にしたいんだけど、あるバグ修正の際に誤って遅くしてしまったケースがあり、その対応を入れたリビジョンにしているので注意。5.0.0.beta.2だと3.28倍とかになる。Erubi...もともとslim-template/slimのベンチマークがテンプレート言語を超えて比較するベンチマークだったため、Slimなどとの比較もでき、以下のような結果になる*4。(Travisでの結果)まあまあ縮まったけどまだ遅いですねという感じ。Erubiとはこれは完全に余談だけど、ErubiというERB実装をご存知だろうか。Railsで長らく使われていたErubisは、今年Erubiに置き替わった。literalが使われていなかったりして遅いんだけど、それがErubiでは解消され*5、ついうっかり僕もErubiのHTMLエスケープを速くしてしまったため、Hamlitとほぼ変わらないパフォーマンスが出るERB実装になっている。やってHamlを速くしたのか僕は自分のHaml実装を持っているので正直Haml使わないのに何故高速化をやっているかというと、何が原因でHamlが遅いのかに興味があったからである。いうわけで、同じ興味を持っている人向けに、どうやって速くしたかを書いておく。2015で話したので、以下では個別の話だけ書く。以下はベンチマークの結果を見て効果があったと思われる順になっている。haml/haml#904このパッチが最も高速化に貢献しており、その分書くのも一番大変だった。false,)以下のようにコンパイルされるようにした、ということである。id='bar'></div>\n".freeze_hamlout.attributesって何、と思うかもしれないが、これはとにかく遅いメソッドであり、これを呼ばなくすると速くなる*6。3の部分)の結果がHashじゃない限りはこの遅いメソッドが呼ばれず、また他の場所では可能な限り事前に連結された状態でバッファにconcatされるので、速い。attributeの仕様を正確に把握している必要があり大変だった。みんなもこの記事を5秒くらい見て欲しい。このパッチで追加したHaml::AttributeCompilerは、互換性を一切崩さずに高速なコードを生成しようとしてものすごく色々なことを考えて書いてあるので語りたいことがたくさんあるんだけど、長くなるので何かのLTのネタとしてとっておく。haml/haml#897Hamlのレンダリングコードには必ず以下のコードが最初に入っていた。1個を生成していることになる。毎回同じならこんなに渡す必要はないので、デフォルトとは違うオプションのみ渡されるようにした。そのため、普通に使っていれば、以下のようなコードが生成される。{})余計なオブジェクトが作られなくなるので速くなる。data='foo&#x000A;bar'></p>Haml5では以下のようになるようにした。data='foobar'></p>これに関しては完全に仕様を変えているので全く褒められた改善ではないのだが、このpreserveと呼ばれる機能はとても遅い。遅いのは、ただでさえgsubが遅いのにそれ以外にもいろいろやっているからである。Hamlitは後者の挙動で問題なく動いているし、入った時のコミットを見ても何で必要なのかよくわからん仕様なので、遅くなるデメリットの方が大きいと判断し削った。何か意見のある識者は声をかけてほしい。haml/haml#893あまり解説する必要はなさそうだけど、静的な文字列をバッファに渡す時に必ず.freezeがついた状態で文字列を作るため、レンダリング時に文字列オブジェクトが生成しなくて済むというもの。Temple化をすると静的な文字列は全て:staticという中間表現になるため、これに全部.freezeをつけていくのが簡単になる。2.3でRuby本体のHTMLエスケープメソッドを高速化したので、gsubではなくそっちを活用するようにした。その際、古いHamlのものすごく複雑なエスケープの挙動が邪魔になるので、FamlやHamlitと同じごく普通の挙動にした。そこは後方互換性のない変更になってしまうが、流石にこれで困る人はいない気がしている。github.comHamlitよりHamlを使った方がよくなる?ならない。Hamlitが速いのはHaml::Bufferが必要な機能を諦めているのとC拡張があるからで、少なくとも前者をやるのはHaml::Helpersを消せない都合かなり難しいし、後者はまだボトルネックではないのでやらなそう。5の想定ユーザーになるが、何らかの理由でFamlやHamlitへの移行を諦めた人には普通に嬉しいリリースなんじゃないだろうか。僕も最終的にはみんなhaml.gemを使えばいい状態にはしたいが、そう簡単に後方互換性は切れるものじゃないと思うので、結構先になる気がする。5.0.0.beta.2を試してみて欲しい。*1:これは冗談で、a_matsudaさんからお誘いがあり、僕も興味があったのでやりますと言い、メンテをさせていただけることになった。*2:余談だけど、HamlのTemple化をやりながらTempleを直していたところ、Templeの方のコミット権もいただいた。月別アーカイブ検索はてなブログをはじめよう!k0kubunさんは、はてなブログを使っています。ブログを報告するスターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。