SAS5iR と SAS6iR の互換性とかについて

  • 同時挿しでは番号の若いスロットのカードの BIOS が認識するはず。
  • SAS5iR -> SAS6iR
    • パーティションやデータはそのまま使用できる。(全く適当に言うが多分ブロックデバイスとしてのディスクの先頭に固有ヘッダが付いてるだけだと思う)
      • RAIDバイスとしての構成情報はカードが保持しているみたい。SATA やら USB やらで接続すれば生のデータが見えた。
    • 別カードに取り付けるわけで RAID1 はリビルドされると思う (未確認) ので degrade 状態でデータをサルベージした方がいいんではないか。
    • SAS5iR のドライバーではブートできない。新しめのバージョンのドライバーではブートできるかもしれない。
  • EaseUS Todo Backup のレスキューディスクは Windows PE なので SAS5iR と SAS6iR を正常に扱える。Windows で運用してるなら用意しといても損にはならないだろう。

SAS5iR が逝ったので詳しくは調べられない。

ペットケージが異常に使いにくい

かねてからそう感じていたけどもう限界。多分他にも既にそう感じた人はいるだろうけどいらついたので書き殴る。
プログラミングのメモしか書かないつもりのブログに載せるくらい腹が立つ。

清掃がしにくい

金網におしっこをかけられたりすると分解して清掃しないと塗料が剥がれて錆びる。療養中は衛生面に特に気を付ける必要がある上に、排便排尿が困難なことも多く清掃は頻繁に行うことになるが、分解すれば当然隔離できなくなってしまう。しかもケージには一般的に金網が用いられることが多く、これは分解にも清掃にも時間がかるクソッタレなシロモノ。

入口が小さい

狭すぎて人間が中で作業するのは困難。そのくせロックはいい加減で、入口の隙間から逃げられたこともある。鎖で本体とつながれた簡単な錠を挿すタイプがあるがジャリジャリうるさいし扉の左右が反転できなかったり、錠も挿しにくかったりで最悪。

床面積が少ない

床は多くても 3-4 段しかなく、体積の割に床面積が少ない。床自体も狭く、最下段でなければ寝返りも打てない。その最下段もトイレを入れたら上部と同じ狭さになってしまう。

つまり市販されているケージは単に隔離空間として使用するならともかく療養や安静のためには全く適さない。わたしは動物同士の相性から隔離することはあっても、人間と動物の空間を分けることはしない。あんなモノは場所ふさぎにしかならないゴミで、要するにイニシエの「買ってはいけない」である。

そもそも療養を目的にした製品じゃない? ああそうかもね。動物病院にあるケージを参考にして DIY するからもういい。アイリスオーヤマ氏ね。

CoffeeScript

raise SyntaxError('do "python -c \'import this\'" first')
キモイ。関数定義は慣れてないだけかも知れないけど以下は受け入れられないレベル。

  • 関数呼び出しの () を省略できる。もちろん引数なしの呼び出しは参照と区別できないので省略できない。
  • オブジェクトの {} を省略できる。いきなり : が現れたらオブジェクト式と認識する。
  • 関数の最後の評価式が戻り値となる。return を明示してもよい。
  • 式が継続しているとみなされたときは改行は , の代わりとなる。このときに長い行を分割する方法が見つからなかった。

ubuntu-10.10-server-i386

いじってたらサービス起動の設定とか飛んだ気がしたので。インストール直後の状態。

# sysv-rc-conf -list
apparmor     S:on
atd
bootlogd
console-setu
cron
dmesg
dns-clean    1:on       2:on    3:on    4:on    5:on
grub-common  2:on       3:on    4:on    5:on
halt         0:on
hostname
hwclock
hwclock-save
irqbalance
killprocs    1:on
module-init-
network-inte
network-inte
networking   0:on       6:on
ondemand     2:on       3:on    4:on    5:on
plymouth
plymouth-log
plymouth-spl
plymouth-sto
pppd-dns     1:on       2:on    3:on    4:on    5:on
procps
rc.local     2:on       3:on    4:on    5:on
reboot       6:on
rsync        2:on       3:on    4:on    5:on
rsyslog
sendsigs     0:on       6:on
single       1:on
stop-bootlog
stop-bootlog
sudo         2:on       3:on    4:on    5:on
udev
udev-finish
udevmonitor
udevtrigger
ufw
umountfs     0:on       6:on
umountroot   0:on       6:on
urandom      0:on       6:on    S:on

tips

  • モジュールオブジェクトは sys.modules に束縛されている。グローバル変数を使う関数をインポートしてからその関数のあるモジュールを解放し、関数を実行するとエラーになる。関数は自分の存在するモジュールへの参照を持たないためである。

[Python] compile()

compile() によってソースコードをコードオブジェクト (types.CodeType) か AST (ast.AST) に変換できる。AST は再度 compile() することによってコードオブジェクトに変換できる。
最も身近なコードオブジェクトは関数オブジェクト (types.FunctionType) の __code__ 属性*1である。モジュールオブジェクト (types.ModuleType) からはコードオブジェクトを取得できないが、後記する方法で実行時に取得できる。
ソースコードの情報を取得したいときや改変したいときは AST に変換するとよい。AST については ast Abstract Syntax Trees と ast モジュールのソースコードを読むこと。

永続化

AST は pickle できる。また、コードオブジェクトは marshal できる。標準の状態でコードオブジェクトを永続化できることは重要である。.pyc ファイルの構造は次の関数 make_pyc のようになる。

import marshal, imp, os, struct, time

def make_pyc(filename):
    """see http://hg.python.org/cpython/file/2.7/Python/import.c"""
    suffix, mode, _ = \
        [i for i in imp.get_suffixes() if i[2] == imp.PY_COMPILED][0]
    with open(os.path.splitext(filename)[0] + suffix, 'wb') as output:
        output.write(imp.get_magic())
        output.write(struct.pack('I', os.stat(filename).st_mtime))
        with open(filename, 'U') as input:
            code = compile(input.read(), filename, 'exec', 0, True)
        marshal.dump(code, output)

行番号 (lineno)

ユーザーから入力されたソースコードを compile() するとき、それがただのスクリプトファイルとして扱える状態なら単に compile() すればよいが、テンプレートのように複数回 compile() を実行するときは位置の設定が必要となる。

  1. AST に変換し、ast.increment_lineno() を呼ぶ。
  2. ソースコードの先頭に '\n' * N を結合する。
  3. 一度コードオブジェクトに変換し、types.CodeType(argcount, nlocals, stacksize, flags, codestring, constants, names, varnames, filename, name, firstlineno, lnotab[, freevars[, cellvars]]) の引数 firstlineno を任意の値に設定し、コードオブジェクトを生成する。

また次のようにすれば埋め込み文字列の行番号を実際の位置にあわせることができる。文字列中の行の継続 (末尾に \) はエスケープしなくても SyntaxError にならないため、必ずエスケープするように注意する。

import sys

source = '\n' * (sys._getframe(0).f_lineno - 1) + """if 1:
    ...
    """
code = compile(source, __file__, 'exec')

AST からコードオブジェクトを作成するとき、CPython は lineno が前後で逆転するような AST でも変換するが、PyPy ではエラーになる。

列番号 (col_offset)

AST では列番号が存在し、0 から始まる。しかし、コードオブジェクトに変換するとこの情報は消滅する。SyntaxError のための情報と考えられる。

Traceback (most recent call last): ... のカスタマイズ

traceback.c の実装を参照すればわかるが、この表示をカスタマイズすることはできない。コードオブジェクトに変換するときに行番号とファイル名を設定すれば任意のファイルの任意の行を出力できる。

__doc__, __future__ を取得する

__future__ は複雑である。

  • compile() の引数 flags は __future__ の状態を設定できる。
  • compile() の引数 dont_inherit は 呼び出し元の __future__ フラグを継承するか設定できる。
  • compile() に与えるソースコードでも __future__ フラグを設定できる。
  • 連続する from __future__ import feature は許される。これは複数の ast.ImportFrom に変換される。
  • __builtins__.__dict__['print'] は常に存在する。
  • 次のコードが動作してしまう。
>>> import __future__, ast
>>> node = ast.parse('print 1')
>>> print ast.dump(node)
Module(body=[Print(dest=None, values=[Num(n=1)], nl=True)])
>>> exec compile(node, '<string>', 'exec', __future__.print_function.compiler_flag)
1

一回の実行中に複数回の compile() の呼び出しがあるときは、簡単にするため次のようなルールを定めた方がよい。

  • compile() の引数 dont_inherit は 常に False を指定する。
  • ユーザーのソースコードとライブラリのソースコードの __future__ フラグは分離する。
  • 最初のユーザーのソースコードの compile() で __future__ フラグを決定し、以降の compile() の flags に指定する。
  • ライブラリから __future__ フラグを追加しない。
    • AST オブジェクトを作成して追加しない。
    • ライブラリのソースコードを compile() するときは __future__ フラグを指定せず、フラグにかかわらず動作するようにする。

AST から __doc__ を取得する

ast.get_docstring() を使う。

AST から __future__ import を取得する

def iter_futureimport(node):
    assert isinstance(node, ast.Module)

    it = iter(node.body)
    try:
        i = it.next()

        # skip docstring, see ast.get_docstring()
        if isinstance(i, ast.Expr) and isinstance(i.value, ast.Str):
            i = it.next()

        while isinstance(i, ast.ImportFrom) and i.module == '__future__':
            yield i
            i = it.next()

    except StopIteration:
        pass

compile() に指定できる flags のビットマスク

コードオブジェクトの co_flags は余計なフラグが設定されていることがある。次の値でマスクする。

import __future__
PyCF_ALL = sum(getattr(__future__, i).compiler_flag
               for i in __future__.all_feature_names)

コードオブジェクトをモジュールオブジェクトに変換する

import sys, types

def module_from_code(name, code, filename=__file__):
    """see PyImport_ExecCodeModuleEx
    at http://svn.python.org/view/python/trunk/Python/import.c?view=markup
    """
    result = types.ModuleType(name)
    result.__file__ = filename
    result.__package__ = None
    #result.__builtins__ = __import__('__builtin__')
    sys.modules[result.__name__] = result
    exec code in result.__dict__, result.__dict__
    return result

実行中 (自分) のコードオブジェクトを得る

import sys
__code__ = sys._getframe(0).f_code

コードオブジェクトの最終行番号を取得する

import dis
__code__.co_firstlineno, list(dis.findlinestarts(__code__))[-1][1]

*1:PyPy には実装されていない。代わりに func_code を使う

Python の最適化について

テンプレートエンジンの最適化をした際のノウハウ。

  • 文字列操作がかなりのコストを占める。
    • basestring.join, basestring.encode, basestring.decode は遅い。簡単なテンプレートでは実行時間の過半をこれらが占めることもある。
    • str += str は list.append や list.extend より遅い。
    • 引数を多く取れれば確実に list.extend の方が list.append より早い。ただし list.append -> cStringIO.StringIO.write のような単純な置換はできなくなる。
  • 当然だが、__import__('xml.sax.saxutils').sax.saxutils.escape と呼ぶよりも from xml.sax.saxutils import escape してキャッシュした方がずっと速い。名前空間が汚染されると捉えるか、速度を優先するかはデザイン次第。
  • 関数呼び出しのコストは他の部分に比べればほぼ無視出来る。

テンプレートエンジンのベンチマークは上記のコストをいかに回避するかのテクニックを競っているようなもので、当てに出来るかは疑わしい。どんなに速くてもテンプレートの書き方次第で優位性は消えてしまう。PyPy とかの速い処理系に期待したほうがいい。

あと適当に文字列処理のコスト↓

import timeit
N = 1000

def bench(name, setup, stmt):
    bench = timeit.Timer(stmt, setup)
    print('|*%s|%.10f|' % (name, bench.timeit(N)))

import sys
print(sys.version)
bench('str +=', '_ = ""', '_ += " " * 1000')
bench('[] +=', '_ = []', '_ += [" " * 1000]')
bench('append', '_ = [].append', '_(" " * 1000)')
bench('extend', '_ = [].extend', '_((" " * 1000, ))')
bench('array.array("c").fromstring',
    'import array; _ = array.array("c").fromstring', '_(" " * 1000)')
bench('cStringIO.StringIO().write',
    'from cStringIO import StringIO; _ = StringIO().write', '_(" " * 1000)')
bench('"".join', '_ = [" " * 1000] * 1000', '"".join(_)')
bench('array.array("c").tostring',
    'import array; _ = array.array("c"); _.fromstring(" " * 1000 * 1000)',
    '_.tostring()')
bench('cStringIO.StringIO().getvalue',
    'from cStringIO import StringIO; _ = StringIO(); _.write(" " * 1000 * 1000)',
    '_.getvalue()')

>python bench.py
2.7.2 (default, Jun 12 2011, 15:08:59) [MSC v.1500 32 bit (Intel)]

str += 0.0596243883
[] += 0.0008418275
append 0.0006185779
extend 0.0006430312
array.array("c").fromstring 0.0503778714
cStringIO.StringIO().write 0.0015428220
"".join 0.5401027310
array.array("c").tostring 0.5463220201
cStringIO.StringIO().getvalue 0.5506574994

>pypy bench.py
2.7.1 (930f0bc4125a, Nov 27 2011, 11:58:57)
[PyPy 1.7.0 with MSC v.1500 32 bit]

str += 0.8409847253
[] += 0.0022026083
append 0.0011542863
extend 0.0013141384
array.array("c").fromstring 0.0230599138
cStringIO.StringIO().write 0.0046778255
"".join 1.4231829048
array.array("c").tostring 4.0303220907
cStringIO.StringIO().getvalue 0.0004813677

>pypy --jit off bench.py
2.7.1 (930f0bc4125a, Nov 27 2011, 11:58:57)
[PyPy 1.7.0 with MSC v.1500 32 bit]

str += 0.5654540095
[] += 0.0015179159
append 0.0012208536
extend 0.0011954946
array.array("c").fromstring 0.0187525104
cStringIO.StringIO().write 0.0043712536
"".join 1.0814593729
array.array("c").tostring 3.9519700981
cStringIO.StringIO().getvalue 0.0004741223