2008/05/26

Recent entries from same category

  1. Growl for Windowsで日本語が表示出来た Hatena
  2. Google App Engine Oilが素晴らしい Hatena
  3. Google App Engineで作れるアプリ数が3個から10個に増えた Hatena
  4. ゆの in Python Hatena
  5. pythonでコンソールが扱えるライブラリConsoleを試してみた Hatena

はてな
Tagtterをリリースして、初期は「CPU Quota」連発してしまい見苦しい状態でした。ごめんなさい。
さて、一時はどうなるかと思いましたが現在は「CPU Quota」も表示される事無く動いています。
今日はこの状況を乗り切る際に行った「Google App Engine」のパフォーマンスチューニングtipsを3つほどご紹介。

webapp.WSGIApplicationのdebugフラグはFalseにすべし

いきなり当たり前で申し訳ないですが、実はこのフラグがTrueかFalseかというだけで「CPU Quota」が出る頻度が極度に異なります。
実際に同じ症状の方もいらっしゃる様なので、おそらく間違いありません。
また公開する様なシステムでは、パスワード等も管理される事になるかと思いますが、debug=Trueだとスタックトレースに変数の値が表示されてしまい無茶苦茶危険です。気を付けましょう。
なお、Tagtterではユーザのパスワードは一切保存していません。

時間の掛かる既知な処理はキャッシュする

これはGoogle App Engineだけに言える話ではないですが、Tagtterではユーザの存在確認の為にtwitter.comへアクセスしています。
但し、タグが追加される度、voteされる度にこれを行ってしまうとtwitter.comにも負荷を掛ける事になります。
そこでTagtterではTagtter内に存在するユーザに対してはtwitter.comへのアクセスを行わないようになっています。
確かにGoogle App Engineの処理能力は良いのですが、いかんせん制限が厳しかったりします。余談になりますが現状Tagtterのデータ全件をXMLエクスポートする処理は「CPU Quota」で動きません。現在はデータの種別毎にバックアップしています。

Templateにはクラスオブジェクトは渡さない

Django Templateには、db.Modelのインスタンスを渡せて非常に便利なのですが、このDBインスタンスをTemplateの中から扱うと非常に遅くなります。
またdb.Modelに持ったgeneratorをTemplateから使うと更にパフォーマンスが落ちます。
例えばTagtterではユーザに対して付けられたタグをManyToManyで管理しており、モデルの一部を抜粋すると
class TagtterTag(db.Model):
  name = db.StringProperty(required=True)

  ... snip ...

class TagtterMembership(db.Model):
  user = db.ReferenceProperty(TagtterUser)
  tag = db.ReferenceProperty(TagtterTag)

  ... ship ...

class TagtterUser(db.Model):
  name = db.StringProperty(required=True)

  ... snip ...

  def tags(self):
    return (x.tag for x in self.tagttermembership_set)

この様なコードになるのですが、user.tagsをTemplate側から呼び出すとかなりパフォーマンスダウンになります。また、Google App EngineではCPU占有度により「CPU Quota」させる仕組みがあるのですが、実はこれはTemplate内の呼び出しにも適応される為
application → template → model procedure
という処理を行うと、1処理(1値参照)あたりのCPU使用回数が増える事になります。
つまり上記Tagtterのtagsページでは全てのタグが表示されますがこれを
    template_values = {
      'tags' : TagtterTag.all(),
    }
この様にしてしまうとTemplate側からModelのメソッドを呼び出す事になり、現状のデータ件数だと100%エラーが発生してしまいます。
Google App EngineではCPU占有状態が5秒程続くとエラーになる様で、このブリッジ状態だけでもパフォーマンスダウンに繋がるって事になります。
これを回避する為には、applicationにてTemplateで使う値を全て保持してしまう事が最も効果的でした。
つまり全てdictとして渡してあげるのです。変にTemplate側で「タグの数が0でない物を表示」とするのではなく
    all_tags = []
    for tag in TagtterTag.all():
      if tag.size():
        size = tag.size()
        all_tags.append({
          'name' : tag.name,
          'title' : tag.title,
          'size' : size,
          'fontsize' : font_size(size),
        })
    ... ship ...

    template_values = {
      ... ship ...

      'tags' : all_tags,

      ... ship ...
    }
この様にTemplate側からdb.Modelメソッドを呼び出させないようにするのです。 これで、現状エラーも出なくなりました。要するに極力db.Modelインスタンスにアクセスしない様にするってのが味噌ですね。
こんな品祖なパフォーマンスチューニングですが、皆さんの何かしらのお役に立てば幸いです。

他にもパフォーマンスチューニングの方法は幾らでもあるかと思います。Tagtterで言えばクライアント側の処理は殆どパフォーマンスチューニングされていないのが現状です。
不定期ですが時間を見付けてやって行きたいと思います。

何かご要望などあれば、Feature Requestまで。

WriteBacks

TrackBack ping me at
Post a comment

writeback message: Ready to post a comment.