Google App Engine上でcProfileを使う

2009年7月2日

先ほどのエントリでプロファイラを使用しましたが、ハマりましたので、メモ書きです。


ドキュメント
に書いてる通りにすれば良いので、普通はハマらないのかもしれません。以下のような使い方になります。元のコードと比較すると解り易いかと思います。
元々エントリポイントでmainが呼ばれていたところを、profile_mainを噛ませて、その中からreal_main(元々のmain)を呼び出しています。ほとんどソースコードの修正が不要で使いやすいですね。

元のコード

def main():
  run_wsgi_app(application) # アプリケーション開始
 
# エントリポイント
if __name__ == "__main__":
  main()

プロファイル用に修正したコード

#def main():
def real_main():
  run_wsgi_app(application) # アプリケーション開始
 
# プロファイル用のコード
def profile_main():
  import cProfile, pstats, StringIO
 
  # ココでreal_mainメソッドを呼び出して、計測している
  prof = cProfile.Profile().runctx("real_main()", globals(), locals())
 
  # 下記は結果の出力
  stream = StringIO.StringIO()
  stats = pstats.Stats(prof, stream=stream).strip_dirs()
  stats.sort_stats("cumulative")
  stats.print_stats('from*')
  logging.info("Profile data:\n%s", stream.getvalue())
 
# エントリポイント
if __name__ == "__main__":
  # main()
  profile_main()

で、何が問題だったかと言うと、単にprofile_mainを噛ませるだけなら、
別にmainはreal_mainにしなくてもいいだろうと思って、ハマりました。

  # これだと駄目
  prof = cProfile.Profile().runctx("main()", globals(), locals())

この場合、デプロイして1回目は普通にプロファイル結果が出力されるのですが、2回目以降は結果が出力されなくなります。中々解決できずに、ハマり倒したのですが、結局ドキュメントに書いてました。

問題はメソッド名でした。引数を取らないmain()というメソッドが定義されていた場合、
このメソッドはキャッシュされて、2回目の呼出し以降は、キャッシュから呼ばれたmain()がエントリポイントとなるため、
profile_mainが呼ばれず、結果が出力されないという事が発生していました。うーむ。これは気付かなかったです。忘れずにmainをreal_mainにせねばなりません。

そしてドキュメントによると、このキャッシュによってかなりパフォーマンスは良くなると書いているので、実際に本番環境で運用を始める時には、忘れずにreal_mainをmainにせねばなりません。

これはミスが多発しそうな雰囲気ですね。。僕だけですか。そうですか。

Google App Engineのmemcacheを試してみた

2009年7月2日

練習もかねてGoogle App Engine上のmemcacheを試してみました。
この投稿の続きを読む »

python-modeの時は2タブにしたい

2009年6月29日

フックに引っ掛ければいいっぽいですね。

(add-hook 'python-mode-hook
 '(lambda()
	(setq indent-tabs-mode nil)
	(setq indent-level 2)
	(setq tab-width 2)))

DomDocument::loadXMLでエンコードの設定が消える件

2009年6月24日

さっきのjson2xmlで、phpのDomDocumentのloadXMLにハマりました。

以下がはまったサンプルコードと出力になります。

< ?php
$dom = new DomDocument('1.0', 'utf-8');
$dom->loadXML("<root><name>taichino< /name><age>25</age></name></root>");
$dom->formatOutput = true;
echo $dom->saveXML();
?>
<!-- 期待する出力 -->
< ?xml version="1.0" encoding="utf-8" ?>
<root>
  <name>taichino</name>
  <age>25</age>
</root>
 
<!-- 実際の出力 -->
< ?xml version="1.0" ?>
<root>
  <name>taichino</name>
  <age>25</age>
</root>

何故かエンコーディングの設定が消えてしまいます。環境の問題だと思い込んで、phpinfoやらphp.iniやら色々確認してみたものの何が原因か解りません。

そこで念の為以下のコードを走らせると正常の挙動を示しました。

$dom = new DomDocument('1.0', 'utf-8');
echo $dom->saveXML();
<!-- ヘッダだけだと正常に出力される -->
< ?xml version="1.0" encoding="utf-8" ?>

まさかと思いましたが、結局loadXML後にencodingを指定したら動きました。
コンストラクタでパラメータ設定できないので、loadXMLを静的呼出に合わせて変更するのが正解でしょうか。

< ?php
$dom = DomDocument::loadXML("<root><name>taichino< /name><age>25< /age>< /root>");
$dom->encoding = 'utf-8'; // ロードしてからエンコーディング設定。釈然としないけども。。
$dom->formatOutput = true;
echo $dom->saveXML();
?>
</age></name>

今回ハマった理由はphpマニュアルのloadXMLの説明に以下の記述があったからです。

このメソッドをスタティックにコールすると、読み込んだ内容をもとに DOMDocument オブジェクトを作成します。読み込み前に DOMDocument のプロパティを 設定する必要がない場合に、スタティックに実行することがあるでしょう。

いやいや、コレは読み込み前にプロパティ設定できるって思いますやん。マニュアルに騙された!

というわけで何が正しいか常に意識しながら生きていかねばなりません。思い込みで選択肢を消してはなりませぬ!

試したのはphp5.1.6と5.2.8です。既になおってたらごめんなさい。

phpでjson2xml

2009年6月24日

phpでjsonからxmlに変換したかったのですが、何故かちょっと調べた感じで出てこなかったので書いてみました。でもJSONの仕様をあんまり把握してないので、間違ってたらすいません。

< ?php
require_once('XML/Util.php');
 
/**
 * @param $obj      変換する連想配列
 * @param $root     補完するルートタグ
 * @param $encoding 出力するXMLのエンコーディング
 * @return          DOMオブジェクト
 */
function json2xml($obj, $root=null, $encoding="") {
  $body = $root ? _tag($root, _json2xml($obj, $encoding)) : _json2xml($obj, $encoding);
  $dom  = DomDocument::loadXML($body);
  if ($encoding) $dom->encoding = $encoding;
  return $dom;
}
 
function _json2xml($obj, $encoding="") {
  $result = "";
 
  while (list($key, $val) = each($obj)) {
    $tagName = preg_match('/^\d+$/', $key) ? 'array_item' : $key;
    switch (gettype($val)) {
    case "object":
    case "array":
      $result .= _tag($tagName, _json2xml($val, $encoding));
      break;
    default:
#      $result .= _tag($tagName, htmlentities($val, ENT_QUOTES, $encoding));
      $result .= _tag($tagName, XML_Util::replaceEntities($val));
      break;
    }
  }
 
  return $result;
}
 
function _tag($name, $value) {
  return "< $name>$value < /$name>";
}
 
?>

使い方はこんな感じでしょうか。
jsonっていうか、連想配列がxmlになるのは良いですね。

< ?php
require_once('json2xml.php');
 
$obj = array(
  "name"  => "taichino ",
  "yomi"  => "たいちーの",
  "age"   => 25,
  "skill" => array(
               "php"    => 4,
               "perl"   => 3,
               "python" => 1),
  "favorite" => array("carbon emacs", "tramp", "cent os")
);
$dom = json2xml($obj, 'root', "utf-8");
$dom->formatOutput = true;
echo $dom->saveXML();
?>

[追記]2009/06/24
encodeの受け渡しが出来ておらず、日本語を上手く処理できてなかったので、修正しました。本文修正済みです。

[追記]2009/06/29
文字参照の置換にhtmlentitiesを使用していましたが、標準でDomDocumentが対応していない文字まで置換されて、その結果エラーが発生していましたので、XML_Util::replaceEntitiesを用いた置換に修正しました。本文修正済みです。コメント化して元のコードは残してあります。修正に伴い、pearパッケージのXML_Utilのインストールが必要になりました。