2010年03月15日

GAE/PのDatastoreについてもっと詳しく

Datastoreはシンプルな仕組みなので、新たに使い方を覚えるのは、それほど難しくはないと思います。しかし、一般的なRDBMSとはだいぶ違うので、別モノだと思った方が良いかもしれません。

ここでは主に一般的なRDBMSとの比較で、Datastoreがどのようなものかを説明していきます。

一般的なRDBMSと大きく違うのは

  • 主キーを自由に選べない(文字列か自動採番のIDの二択)
  • 複数のテーブルをクエリで自由にJOINできない
  • フィルタ条件の不等号は同時に1つのプロパティに対してしか使えない
  • 不等号のフィルタ条件がある場合、並べ替えの1番目には同じプロパティしか使えない

JOINが使えないので、テーブル設計は正規化しすぎない方が良いようです。

主キー(ID/key_name)

Datastoreでは、主キーはシステムが自動採番する数値型のIDか、ユーザが指定する文字列型のkey_nameの二者択一となっています。Modelクラスのインスタンス作成時にkey_nameを指定すると文字列型の主キーが使われ、省略すると数値型のIDが使われます。

この主キーは、フィルタ条件や並べ替えには使うことができません。主キーで並べ替え等をする場合は、同じ値を保存するプロパティを用意する必要があります。

IDは連番ではなく、後から追加したデータが大きな数字になるとも限らないようです。

テーブル設計

Datastoreでのテーブル設計は特別な管理ツール等は使わずに、Pythonコードでdb.Modelクラスを継承したクラスを定義するだけで出来ます。基本的にDatastoreへのアクセスはこのクラスを経由して行います。一部のメンバだけを抜き出して読み書きすることはできず、Datastoreへのアクセスは一行分まるごとになります。

恐ろしいことに、エンティティごとにメンバの数や型が異なるデータモデルも定義でき、その場合はdb.Expandoクラスを継承します。このExpandoモデルは、私の感覚では気持ち悪過ぎて、いまいち有用な使い方が分かりません。

日付けと時刻

日付けと時刻を両方記録する場合には、datetime型を使います。

Pythonのdatetime型は、タイムゾーン情報を持っている場合はaware、持っていない場合はnaiveと区別され、両者が混在すると比較や演算ができないようになっています。

datetime型プロパティをDatastoreに保存すると、元がawareであってもタイムゾーン情報が失われnaiveになってしまいます。naiveなdatetimeからawareに変換するには、読み出した後にreplaceメソッドを使ってtzinfoを置き換えます。(replaceは時刻の再計算等をせずに一部の要素だけを置きかえるメソッド)

entry.datetime = entry.datetime.replace(tzinfo=UTC())

※具体的なタイムゾーンを表すクラスは提供されないので、UTC等のクラスを自分で定義する必要がある。

日本のユーザにしかサービスを提供するつもりがなければ、Datastoreには日本時間で記録しておくと、レスポンス生成時に変換が不要になるので良いと思います。

メンテナンス

GAEでは、アプリケーションを実行するサーバとDatastoreを担当するサーバが分かれているので、アプリケーションは実行できるがDatastoreに書きこめないという状況が起こります。(メンテナンス中でもサービスを続行できるように、読み出しだけは出来るようになっている)

Datastoreがメンテナンス中に書きこもうとするとCapabilityDisabledErrorという例外が発生します。しかし、掲示板などでユーザが書き込みボタンを押してからエラーを出すよりも、最初からメンテナンス中の案内が出来た方がユーザビリティが向上します。そのような目的のためにCapabilities APIが用意されています。

Capabilities APIは今のところドキュメント化されていないので、次の記事を参考にしてください。

Handling downtime: The capabilities API and testing - Nick's Blog

Capabilities APIは、Javaではまだ提供されていないようです。

2010年03月15日 【プログラミング】 | コメント(0) |
2010年03月12日

Google App Engine特有の仕様

GAE上で実行されるプログラムは、基本的に普通のCGIと同じインターフェースとなっています。Pythonのモジュールもセキュリティ的に問題のないものはほとんど使えるので、PythonでCGIを作ったことがあれば、簡単に始めることができます。

しかし、GAE特有のものもあるので、ここで紹介しておきます。

Datastore

GAE上のアプリケーションがデータを保存する際には、通常のファイルは使えず、Datastoreと呼ばれるデータベースを使う必要があります。

データベースといえば、OracleやSQL ServerのようなRDBMSが有名ですが、DatastoreはRDBMSではありません。RDBMSよりもスケーラビリティを重視したデータベースです。

RDBMSでないデータベースを総称して「NoSQL」と呼ばれています。RDBMSよりも負荷に強く、可用性の高いシステムが作れるということで、最近の流行です。他にはAmazonが提供するSimpleDBなどが有名です。

RDBMSに慣れた人にとって、Datastoreは機能が制限されすぎていて使いにくいものかもしれません。例えばGQL(DatastoreのSQLモドキ言語)では、WHERE句に不等号を使った条件を複数指定する際は、1つのプロパティに対してしか使えません。「明日以降に発売される400円以上の雑誌」という条件を指定しようとすると、発売日と金額で不等号が必要になるので駄目です。「400円以上500円未満」のように同じプロパティに対する不等号ならば複数指定することもできます。

しかし、Pythonでdb.Modelクラスを継承してメンバを定義するだけでテーブルが定義できる等、とても手軽な面もあるので、データベースに馴染みのない人にとっては使いやすいと思います。

cron

GAEで使えるcronは、指定された時間に指定されたURLをHTTPのGETメソッドで開くという動作になります。そのため、実行するスクリプトはURLにマッピングされている必要があります。

動作テストの際には、cronジョブに割り当てられたURLをブラウザで開くだけでテストすることができます。

ちなみに、30秒制限はcronにも適用されます。

Task Queue

Task Queueを一言で説明すると、プログラム上から任意に登録できる1回限りのcronジョブです。まだ実験的な機能で日本語ドキュメントにも記述がありませんが、非常に便利な機能です。

もっとも一般的な使い方は、不定期に実行される処理です。例えばF1ニュース配信botでは、決勝レース開始15分前にアナウンスを流しますが、レース開始時刻はグランプリによってまちまちで、だいたい2週間に1回程度しか出番がありません。ここでTask Queueが活躍し、1日1回実行されるcronジョブでその日にレースがある場合にアナウンス用のTaskを登録しています。これをcronだけで処理しようとすると15分間隔で常に監視する必要があるので、ほとんどの処理時間が無駄になってしまいます。

また、変わった応用例として、30秒で終わらない処理を複数のTaskに分け、Task Queueを使って一気に処理させるという使い方もあります。

2010年03月12日 【プログラミング】 | コメント(0) |
2010年03月11日

WindowsでGoogle App Engineを始める

Google App Engineは運営が始まってから1年以上経っているので、既に紹介記事や入門記事はいっぱいありますが、私なりに気付いた点を簡単に書いておきます。

まず、私のように普段からPythonを使っている人間にとって問題になるのが、GAE/PはPython2.5上で動くという点です。最新の2.6と比べると言語的にはWith構文がオプション扱いになっている程度で問題ないのですが、動作テストのために自分のPCにも2.5をインストールする必要があります。

Python2.6でGAEを動かす方法というのも見かけましたが、簡単なスクリプトは動いたものの、GAEのサービスをフルに使うようなものは動きませんでした。

Google App Engine Launcherには、Pythonの実行ファイルのパスの設定があるので、これを設定することで同じPC上にPython2.5と2.6を同居させても問題なく使えるようになっています。

インストールの手順は、Windowsのインストーラ形式でPythonをインストールする場合*.pyファイルの関連付けの問題があるので、先に2.5をインストールし、後から2.6をインストールします。これで2.5と2.6が同居しつつ、*.pyは2.6に関連付けされるという環境になります。

Google App Engine Launcherを起動したら、[Edit]-[Preferences...]のPython Pathの欄にPython2.5の実行ファイルを設定します。これでGAEではPython2.5が使われるようになります。

Google App Engine Launcher

GAE Launcherのツールバーには次のようなボタンが並んでいます。

  • Run
    簡易的なWebサーバが稼働しアプリケーションをローカルでテスト実行する。
  • Stop
    Runで開始したアプリケーションを停止する。
  • Browse
    テスト中のアプリケーションをブラウザで開く。
  • Logs
    テスト中のアプリケーションのログを見る。
  • SDK Console
    テスト中のアプリケーションのデータベースの管理等をする。
  • Edit
    アプリケーションの構成を定義するapp.yamlファイルを編集する。
  • Deploy
    アプリケーションを本番サーバに配置する。
  • Dashboard
    本番サーバでの管理画面を開く。

SDK Consoleではデータベースの管理をしたりする以外に、Pythonのコードを書いて実行させるInteractive Consoleという機能があります。GAEのサービスに依存する操作などは通常のPythonのインタラクティブシェルからはテストできないので、データベースに対してちょっとしたバッチ処理を行いたいとき等にこの機能を使うと便利です。ただし、本番のサーバにはInteractive Consoleの機能はないので、本番でも必要な処理はスクリプトとして保存しておく必要があります。

DeployとDashboardは、先に本番サーバにアプリケーションを登録しておく必要があります。Deployの際にSSLモジュールがないと注意されますが、とりあえず無くても問題はありません。

loggingモジュールを利用して出力したログは、ローカルのテスト環境では日本語が使えません。日本語を含むログは、ログ自体が表示されないか稀に表示されても文字化けしてしまいます。本番サーバでは、日本語ログも問題ありません。

cronやTask Queueは、テスト環境では自動実行されません。SDK Consoleの中にリンクがあるので、それをクリックすることで手動で実行できます。

2010年03月11日 【プログラミング】 | コメント(0) |
2010年03月10日

0円から使えるWebアプリケーションプラットフォーム Google App Engine

前回紹介したF1ニュース配信botは、Google App Engine上で動いています。

Google App Engine(GAE)は、Googleが提供するWebアプリケーションのための開発/実行環境で、自作のアプリケーションをGoogleのインフラ上で動かすことができるサービスです。いわゆるPaaS(Platform as a Service)でクラウドコンピューティングの一形態です。

通常のWebサーバと大きく違うのは、アプリケーションの負荷状況に応じて自動的にサーバ数が増減して分散処理されるので、アクセス数が極端に増えても大丈夫という点です。実際の利用例として挙げられるオバマ大統領の投票サイトの事例では、瞬間的には秒間700アクセスという負荷にも耐えたそうです。

同じようなサービスのWindows AzureやAmazon EC2と比べると、次のような特徴があります。

  • 0円から使える
  • サーバの設定・管理は必要ない
  • 高度に冗長化されておりサーバが故障してもアプリケーションが消える等の事故がない
  • 今のところPythonとJavaしか使えない(一応JRuby等も使える?)
  • Webリクエストやcronジョブに対する応答としてのみ実行でき、30秒以内に処理を完了しないといけない(常駐ソフトは作れない)

また、よくある無料ウェブスペースと比べると、次のような違いがあります。

  • 広告は入らない
  • cronによる定期的な実行ができる
  • 無料で使える範囲を超えてしまっても、お金を払えばそのまま運用を続けられる
  • 無料で自前CGIを利用できるサーバにしては軽い

高い負荷に耐えられるWebアプリケーションプラットフォームというのが売りですが、私のようにPythonが使える無料のWebサーバを探している人にとっても魅力的なサービスです。

ちなみに前回のtwitter botは、基本的にGAE上のコンテンツにアクセスがあるわけではないので、無料の範囲内でも数十個は動かす余裕があります。

2010年03月10日 【プログラミング】 | コメント(0) |
2009年08月14日

B-Splineフィルタが軽い理由

真・東方縦画面化ツールに搭載したフィルタの中で、(3次の)B-Splineフィルタがもっとも軽くなっています。

しかし、実際に3次のB-Splineとバイキュービックを書いたことがある人なら分かると思いますが、この2つは各項にかかる係数が違うだけで、それ以外は全く同じ式です。では、なぜB-Splineフィルタが軽いのか…というのが今回の話。

各フィルタの補間関数をグラフ化すると次のようになります。

plot

Bi-Cubic1はシャープネス0、Bi-Cubic2はシャープネス1に設定したときのバイキュービックフィルタです。(シャープネスの値は一般的なものではなく、私が実用的と判断した範囲に設定した値です)

この補間関数は、フィルタのインパルス応答に相当し、入力信号が出力にどのように影響するかを表しています。例えばバイキュービックフィルタでは、入力画素値と同じ位置では1.0(元の値と全く同じ)で、そこから離れるにしたがって影響力が減っていき、ちょうど1画素分離れた位置では影響が0になります。更にそこから2画素分離れた位置まではマイナスの影響をします。

※ここでの説明は全て1次元ですが、今回使用しているフィルタは全て「分離型フィルタ」というもので、2次元のフィルタをX方向とY方向の1次元フィルタに分けることができます。このようなフィルタでは、X方向に1次元フィルタをかけた後に、Y方向に1次元フィルタをかけることで、2次元のフィルタと同じ結果を得ることができます。

ただし、この概念は入力信号中心の考え方ですが、実際のプログラムでは出力信号中心の考え方が必要になります。そのためには、「畳み込み」という計算を行います。

畳み込みは、フィルタ関数の定義域の全ての値を取得し、フィルタ関数を掛けて足し合わせるという処理になります。今回のフィルタは全て定義域が-2〜2で幅が4なので、入力画像から4つのピクセルをサンプリングすることになります。連続する4点をサンプリングし、補間関数のちょうど1ずつ離れた位置の値を掛けて足し合わせます。

さて、この4つの曲線のうち、B-Splineだけがマイナスの領域がないことが分かると思います。B-Splineの軽さの秘密は、ここにあります。

GPUには基本機能としてバイリニアフィルタが搭載されています。バイリニアフィルタはX方向とY方向にそれぞれ2点ずつ合計4つのピクセルを、距離に応じた比例配分で混合します。比例配分は、p1*a+p2*(1.0-a) のような計算で、必ず係数の合計が1.0になります。この処理はよく使うので、テクスチャユニットに組み込まれていて、非常に少ないコストで利用することができます。(ちなみにバイリニアフィルタも分離型フィルタ)

畳み込み処理で2つのピクセルに注目したとき、例えばそれぞれに掛ける係数が0.1と0.4だったとすると、次のように変形できます。

p1*0.1+p2*0.4

= (p1*0.2+p2*0.8)*0.5

= (p1*0.2+p2*(1.0-0.2))*0.5

= (p1*a+p2*(1.0-a))*0.5 [a=0.2]

これはバイリニアフィルタでサンプリングした結果に、係数の合計値を掛ければ畳み込み処理の2点分を処理できることを表します。

現在のGPUは、計算は速いがテクスチャの読み込みは遅いという性質があります。そのため、多少計算が複雑になっても、テクスチャ読み込み回数を減らした方が速くなります。B-Splineフィルタはこのテクニックを使って、16点フィルタを4回のテクスチャフェッチで処理しています。

ただし、バイリニアフィルタの係数は0.0〜1.0に限定されるので、負の係数が混ざっているとこの方法は使えません。

3次のB-SplineフィルタのX/Yフィルタの符号を表にすると次のようになります。

+ / +

+ / +

+ / +

+ / +

+ / +

+ / +

+ / +

+ / +

+ / +

+ / +

+ / +

+ / +

+ / +

+ / +

+ / +

+ / +

全ての領域で符号が同じなので、この手法が使えることが分かると思います。

一方、バイキュービックフィルタやLanczos2は次のようになります。

- / -

+ / -

+ / -

- / -

- / +

+ / +

+ / +

- / +

- / +

+ / +

+ / +

- / +

- / -

+ / -

+ / -

- / -

正負の符号が混ざっているために、この手法が使えません。

2点に掛ける係数がどちらも負の値の場合は、係数の符号を反転してバイリニアフィルタで読み込み、その結果を再び反転することで同じ結果が得られます。四隅を省略して12点補間とした簡易版バイキュービックフィルタではこの方法を使い、符号が揃っている中央の4点、上下左右の2点ずつをそれぞれバイリニアフィルタで読み込み、計5回のテクスチャフェッチで処理しています。

簡易版バイキュービックフィルタがこの方法で軽量化に成功してしまったために、軽いフィルタが作れると思って実装したB-Splineとの差がほとんど無くなってしまいました。

ちなみに、修正版バイキュービックフィルタではこのような最適化は行っておらず、16回テクスチャを読み込んでいます。

ところで、上のグラフでBi-Cubic1とLanzos2がほとんど同じ曲線ですよね。もう少しパラメータを変えるともっと近くなります。出力画像を見てこの2つのフィルタの違いがよく分からないと思っていたのですが、グラフを描いてみて納得しました。

Lanzosを使うなら2ではなく3以上を使わないと意味がなさそうです。Lanczos3になると36点補間となり、一気に倍以上の処理量になるので、私のツールで採用する気はありません。(費用対効果が悪過ぎる)

2009年08月14日 【プログラミング】 | コメント(0) |
×

この広告は1年以上新しい記事の投稿がないブログに表示されております。