こんにちは、X。
公開日 : 2018-12-03 19:05:44
再構築処理や管理画面の応答速度に課題があったのははっきりしていたので、新しいバージョンではそこをなんとかしなければ将来はない、というのが新バージョン開発の時の大前提でした。小手先の改良はなく。そこで PowerCMS X について、主に、高速に動かすために行った工夫などを紹介します。
- Perl => PHP
- テンプレートエンジン。テンプレートのパースを libxml2(DomDocument)にやらせる。
- テンプレートは毎回パースせず、常にテンプレート保存時にコンパイルしたものを使う。
- ORマッパ。SELECT * をやめる。
- ORマッパ。トランザクション処理、バルクINSERT/UPDATE/DELETE
- Memcachedの活用
- テンプレートのキャッシュ
- 小さな工夫の積み重ね
1. Perl => PHP
どちらが速いとかってのを論じるとアレげなので、個人の感想を。
- PSGI Plack環境で動作させるなど、高速に動かすための設定は、なかなかに面倒。PHPと比較すると、環境を用意するのに一手間かかる。
- Perl の開発者をアサインするのがなかなかに大変。
- PHP7.xは、PHP5.xの2倍近く高速化されている。
2. テンプレートエンジン
libxml2とは、XML/HTMLを解析・操作するC言語のライブラリです。
正規表現でのパースと比較して、とても高速に動作します。
例えば有名なテンプレートエンジン Smartyと比較した結果ですが、コンパイル済みテンプレートの実行速度はほぼ差が出ません。Smartyは遅いってのは誤解ですね。相当速いです。
ただ、コンパイル速度は libxml2でのコンパイルと比較して5倍から10倍の差が出ます。MTでは、Builder.pm の Bulder::compile メソッドがその役割を担っています。
PowerCMS Xでは、libxml2(DomDocument)でテンプレートを解析してPHPのネイティブコードに変換する軽量、1ファイルのテンプレートエンジン「PAML」を作成し、それを使っています。高速です。
3. テンプレートは毎回コンパイルしない
PAMLでは、Smartyなどと同様に、テンプレートをPHPのネイティブコードに一旦変換します。
MTや PowerCMSでは、テンプレートを管理画面から登録してデータベースに保存しますから、コンパイルはテンプレートの保存時に行って、データベースにその時に保存してしまえばいい。
ということで、 PowerCMS Xでは、データベースレコードに compiledカラムを作成し、変換済みのネイティブコードを保存します。
つまり、保存時に少しだけ余計に待たされますが、再構築時にはこの処理自体スキップされるというわけです。なので、2.は意味ないんじゃないか? とも思えますが、そもそも管理画面では毎回コンパイルしていますから、高速コンパイルの恩恵はちゃんとあります。
4. ORマッパ。SELECT * をやめる
例えば、mt:entriesというブロックを呼び出すと、SELECT * from mt_emtry 〜というSQL文が発行されます。
プログラムから呼び出すときは、こういう感じです。
MT->model('entry')->load( { blog_id => 1, status => 2 }, { limit=> 1 } );
# argsに { fetchonly => ['id'] } を指定できるようになっていますが、利用されているのは限定的な箇所。
さらに、カスタムフィールドなどのメタカラムは自動的にロードされるため、プログラムやテンプレートを書く側はそのあたりを意識せずに済むようになっています。
PowerCMS Xでは、以下のように書くことができます。3つめと4つめの引数はオプションで、3つめの引数に、SELECT対象のカラム、4つ目の引数にSQLのオプション。
$app->db->model('entry')->load( [ workspace_id => 1 ], ['limit' => 1], 'title,published_on', 'AND entry_status=2' );
カスタムフィールドなどは「利用する時」に初めてクエリを発行します。
テンプレートタグでは、次のように書くことができます。
<mt:entries cols="title,published_on" load_only_ids="1" limit="10">
</mt:entries>
SELECT id from ...で一旦ロードし、ループの中で利用するのために title,published_on カラムを読み込む。
load_only_ids 指定のないときは、最初から SELECT entry_title,entry_published_on from 〜となる
5. ORマッパ。トランザクション処理、バルクINSERT/UPDATE/DELETE
例えば、オブジェクトのインポート処理などにおいて、トランザクションを貼っておいて、最後に纏めてコミットすることで、処理速度が大きく向上します。
複数のオブジェクトを処理する場合は、以下のように書けるようになっています。
$app->db->model('entry')->update_multi( $entries ); // INSERT / UPDATE
$app->db->model('entry')->remove_multi( $entries ); // DELETE
カスタムフィールドが100個ある案件でインポートが終わらなくて困ってしまっていた過去に、さよならです。
6. Memcached の活用
動的に登録されるカスタムフィールドのような、カスタム・テンプレート・タグや、翻訳フレーズ、システム全体の設定などは、Mamcacnedもしくはファイルキャッシュに保存して、それを使うことができます。
これによって初期化処理が高速化されます。
7. テンプレートのキャッシュ
従来と同様、モジュール単位でのキャッシュを指定できます。キャッシュは作成・利用するより、クリアするタイミングが難しい。そこで、PowerCMS Xでは、割り切って一連の再構築処理が完了したらキャッシュはクリアされるようにしています。
そして、新たに、<mt:cacheblock key="cache_key"></mt:cacheblock>タグを利用できるようになっています。
8. 小さな工夫の積み重ね
ファイルを書き出す際の content_is_updated(ファイルの比較処理)で作成したハッシュ値を保存しておいて次回の比較に使うとか、markdownの処理結果を memcachedに保存してしまうとか、このあたりは、個々の効果は些少ですが、こういうのは、要するに積み重ねです。PowerCMS X は、高速に動作させるために、これまでの経験を活かし、設計部分からまったく新しいものになっています。
これからは、PowerCMS Xをよろしくお願いします。