2016年03月09日

Python 実行スクリプトのプロファイル

Python 実行スクリプトのプロファイルを取るには cProfile モジュールを -m を使って実行する。

$ python -m cProfile -s tottime spam.py 42

すると、こんな出力が出る。

  1794060 function calls (1791259 primitive calls) in 7.057 seconds

   Ordered by: internal time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     9715    3.492    0.000    5.249    0.000 hoge.py:7(heavy_function)
    30910    0.413    0.000    0.413    0.000 {method 'fuga' of '_foo.bar' objects}
    30910    0.282    0.000    5.509    0.000 foo.py:365(execute)
(以下略)

最初の行は総計でどれだけ関数呼び出しがあったか、何秒掛かったかが表示されている。 -s tottime を指定したので、下の表は tottime の大きい順にソートされて表示される。 tottime はその関数自体で何秒費やしたかという合計時間(total time)。 ここでは hoge.py の7行目にある heavy_function が9715回呼ばれて3.492秒掛かったのが一番。 heavy_function というわりには1回あたりはせいぜい0.3ミリ秒だが、まあここが速くなればプログラムの実行が早くなるだろうという部分ではある。 ちなみに上の「1回あたりはせいぜい0.3ミリ秒」という情報は tottime の隣の percall に表示されていることになっているのだが、単位が秒のため表示桁の制約上 0.000 としか表示されていない。 大概の場合この percall 欄は役に立たない。

2行目の関数名の欄が {method 'fuga' of '_foo.bar' objects} という表示で行番号が出ていないのは、C のライブラリが呼び出されている部分。 3行目と呼び出し回数が同じなので、おそらく3行目の foo.py にある execute 関数がこのライブラリを呼び出しているのだろう。

これで大体出力の読み方も解ったと思うが、もう一つ cumtime が気になる人もいるかもしれない。 これは cumulative time つまり累積時間のことで関数から更に呼ばれた関数での時間もこみこみで出した消費時間のことを意味する。 場合によっては tottime より cumtime を見たいこともあるかもしれない。 そうしたときは -s cumtime-s tottime の代わりに指定すれば cumtime 順で出力される。

参考:

posted by mft at 15:15| Comment(0) | TrackBack(0) | 日記 | このブログの読者になる | 更新情報をチェックする

2015年11月26日

リストの +=

Python の挙動でびっくりしたことを書いておく。

リストとタプルを足し算すると TypeError になる。

>>> [] + ()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can only concatenate list (not "tuple") to list
>>>

至極当然。

ところが += では エラーにならない

>>> a = []
>>> a += (1,)
>>> a
[1]
>>> 

完全に虚を突かれた。

ちなみに += を使うと文字列も足せるし、実は iterable ならなんでもいけるっぽい。

>>> a += {2: None}
>>> a
[1, 2]
>>> a += "345"
>>> a
[1, 2, '3', '4', '5']
>>> def iterable():
...   yield 6
... 
>>> a += iterable()
>>> a
[1, 2, '3', '4', '5', 6]
>>>

ライブラリリファレンスとかからはこういう挙動は読み取れないけど、 どこを見たら書いてあるんですかね。

ラベル:Python
posted by mft at 17:32| Comment(0) | TrackBack(0) | 日記 | このブログの読者になる | 更新情報をチェックする

2015年06月21日

MySQL で datadir 内のディレクトリを無視する

あるシステムでディスク容量が足りなくなって MySQL のデータディレクトリを別ディスクに追い出すことにした。 新しく追加するディスクはもうデータディレクトリだけ置くつもりなのでマウントした場所がすぐデータディレクトリということにした。

普通に動くのだが、一つ考えていなかった問題が発生した。 /var/log/mysqld に毎日数回こんなメッセージが書き出される。

150621 15:32:14 [ERROR] Invalid (old?) table or database name 'lost+found'

マウントした直下に自動で作られる lost+found ディレクトリがテーブルだかデータベースだかに見えないという。 それはそうだ。

階層一つ深くする、というのがひとつの確実な解決策なのは判る。 が、lost+found 自体を無視できればその方が楽。

ということで調べたところ、方法があった。

mysqld のオプションに --ignore-db-dir=〈無視したいディレクトリ〉 を付加するか、実質的に同じことだが、設定ファイル my.cnf に ignore_db_dirs=〈無視したいディレクトリ〉 を追加する

ただし、一つ制限があって、MySQL のバージョン 5.6.3 以降でしか使えない。 当初使いたかったシステムには適用できなかった(まあディスク容量を食い過ぎるような状態になるぐらい使い続けているシステムなので若干古い)。

余談その1: RDS に移すことを考えたほうが良かったかもしれない。

余談その2: datadir 内の lost+found を無視する方法 みたいなそのものズバリなブログ(英語)も見つかったが、ignore_db_dirs に言及しているページ自体がグーグル検索でわずか数百件だったので枯れ木も山の賑わいとちょっと書いてみた。

ラベル:AWS MySQL
posted by mft at 16:17| Comment(0) | TrackBack(0) | 日記 | このブログの読者になる | 更新情報をチェックする