2008年12月29日月曜日

call/cc

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
yieldしてgetresultするスタイルは、どういった部類の考え方かというと、call/ccだ。

Scheme:使いたい人のための継続入門が参考になるであろう。

ところでここを見ると、

注目してもらいたい関数はcall-with-input-fileだ
call-with-input-fileの処理はキモだけを抽出すると、次のコードになる。

;; とんでもなく杜撰なcall-with-input-fileの実装(コアのみ)
(define (call-with-input-file file-name proc)
(proc (open-input-file file-name)))
つまり、call-with-input-fileは ファイルから入力ポートを作り出し、それを渡してprocをcallする という関数なのだ。 call procedure with input fileという感じだろうか。

これってpythonのwithとやりたいことは同じだ。()を使わずにかつよく使う使い方のみをうまく使えるようにするのがpythonの言語としての方向性なんだろう。

rubyではcall/ccやめるとかなんとか??(このへん)。古いんで2009年はどうなるかは興味ある人は自分で調べてください。

そもそもCall/CCがrubyに実装されている理由はただ一つで、「実装できてし まったから」である。最近のRuby関連メーリングリストの流れを見ていると 「Call/CCステ」の方向で あるのは間違いない。まあ、普通の人間が「おっ、ここはCall/CCを使えば カッコ良く実装できるね!」などと思いついたりすることはまずありえないので、 ある意味安心である。


LLの実装の構造上、実装することはそれほど難しいことではないが、使い手を選ぶといったところなのだろう。この点で言語の課題は「実装技術」ではなく「利用者負担」である以上、pythonの方針は正しいのだろう。

C/C++が低レベルの方向で切れ味がある分、指をきったり足を打ちぬいたりが簡単にできるように、lisp系の言語ではそれを抽象化の方向で同じように指をきったり足を打ちぬいたりできるといったところだろうか。

2008年12月28日日曜日

つづき

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
で、ruleがhandlerを登録してcallbackしてもらうのはごく普通なのだが、ruleを書く側の立場として生活しやすいtrickがほしい。どういうことかというと

def hanlder(self):
classmembers = self.guessClassmember(klass)
for func in klass.findall('./Stmt/Function/'):
if self.isClassMethod(func):
continue
for assattr in func.findall('.//Assign/AssAttr'):
if self.isClassMemberAssign(assattr, classmembers):
print 'defined at', classmembers[assattr.attrib['attrname']].attrib['lineno']
print 'substitute at', assattr.attrib['lineno']

と書いていたのを

def calc(self):
classmembers = self.guessClassmember(klass)
for func in self.wait('.//Class/Stmt/Function/'):
if self.isClassMethod(func):
continue
for assattr in self.wait('.//Class/Stmt/Function//Assign/AssAttr'):
if self.isClassMemberAssign(assattr, classmembers):
print 'defined at', classmembers[assattr.attrib['attrname']].attrib['lineno']
print 'substitute at', assattr.attrib['lineno']

とかしたい。self.waitは実際にはxpath文字列じゃなくてxpathを与えて作ったhandlerにしないと、methodの評価のタイミングが呼び出されたときなので、xpathがvisitにわたらないので駄目かな?あ~でもcalcに制御がわたってwaitを読んだときにcalcから抜けてhandlerのcallbackでyieldしてforに戻ってくる構造だからなぁ。

pollingするための口と、pollした結果どのhandlerをfireするか決めればいいのか。pollingするとほかのruleのcalcを呼ぶ可能性がある。ひとひねりしないとpollingされる関数が再帰してstack over flowだ。multithreadで書くほうが自然に思えてきた。実際にvisitするobjectがいるthreadがproducerで、handerを登録するruleがいる側がconsumerだ。といいつつも、次にvisitするものは何かをconsumerから教えてもらわないといけない。(ここがyieldになる。)

yieldが戻り先から値を返してくれるとwaitのかわりにyieldしてあげればらくに書けそう。yieldでxpathを受け取り、見つかったときにその関数(calc)を、見つかったnodeとともに返してあげればいい。すくなくとも2.4だとできないのでyieldから戻ってきたときに持たせるvalueを書き込むスペースを用意してあげることになる。美しくないが、複雑なコントロールフローを作るよりはましだ。しかしfor文が

while True:
yield ".//Stmt"
r = getresult()
#do something with r here.

こんな感じになってしまうだろう。かなり個人的な趣味をいうと、PEPで提案されているyieldの括弧のつけ方はきらいだなぁ。yield(42)とかのほうがいい。関数呼び出しっぽいが。しかしexception関係といい、2.4のgeneratorは微妙だね。

あとxpathに先読み否定(要はclassmethodをチェックさせたい)とかあるともうちょっとエレガントになる。まあ、蛇足ですが。

曖昧さ(対策案)

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
class methodじゃないとことでのclass memberへの代入は変ということ。

def guessClassmember(self, klass):
return dict([(n.attrib['name'], n) for n in klass.findall('./Stmt/Assign/AssName')])

def calc(self, klass):
classmembers = self.guessClassmember(klass)
for func in klass.findall('./Stmt/Function/'):
if self.isClassMethod(func):
continue
for assattr in func.findall('.//Assign/AssAttr'):
if self.isClassMemberAssign(assattr, classmembers):
print 'defined at', classmembers[assattr.attrib['attrname']].attrib['lineno']
print 'substitute at', assattr.attrib['lineno']

klass.findall('./Stmt/Assign/AssName')]
はクラスノード直下でStmtノードAssignノードAssNameノードのすべてと読むのですが、(つまりクラス変数の代入)のこと。

どーも綺麗にかけない。名前のscopeの関係上必要となるものを全部渡してあげないといけない。継承の処理やimportの関係の処理をちゃんとやると大変。
多分、継承やimport関係はもっといろいろなruleを考えると共通化できて、treeを作ったときにまず最初に処理して使いまわすのが筋だろうね。nodeの受け取る関数とそのnodeを指定するpathの組でやるruleのモデリングが駄目すぎ。もうちょっと考えないとruleの数が増えてくるとfindallの回数が多すぎて耐え難いスピードになるだろう。でも考えるにはruleがそれなりに手に入らないと駄目なんだけどね。

最初はobjectにすることすら避けてruleをmethod + docでやろうかと思ったのだ、それだとruleを増やしていくとクラスが馬鹿でかくなる。Naming Conventionのチェッカーとか。xpathとhandlerの集合を受け取って、最小の回数のvisitで処理をしてほしいのだが。xpathをマージしてhandlerを呼ぶような何かを作るのは自明でないし。

findallの中身は、多分visitをやっているときのstackの中身とxpathのマッチなので、大筋としては{xpath: handler}なものを渡してしまえばよいだろう。

handler同士での相対pathと絶対pathの問題とそのときのデータ共有が解決しないとくさいfor文が消えてくれない。
普通はclass変数を先に書くが、メソッドのあとに書いてもよく、名前がどこで出てくるかはnodeをvisitした瞬間には解決できず、あとでまとめてやらなきゃいけない。handlerをruleが生成して、handlerはruleにreportする、最後にruleが問題を確定させるというような構造にせざるを得ないだろう。もちろん、単にxpathにmatchするnodeをかえされてもどこの何かわからないので、rootからのnode listをもらうほうがうれしいだろう、まあstackのcopyそのものだ。reportされたものをグループ化する仕組みがないとはまりそう。諸所の作業量からして2009年に持ち越しです。まあ、デザインが絞れただけよかったか。

taintみたいなことはしないので、classmemberへの参照から変更をやられるともちろん駄目。そもそもにそんなものはstaticには解決できない。

さて夕飯でもつくるか。

曖昧さ

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

class Hoge(object):
bar = 1

class Piyo(Hoge):
def __init__(self):
self.bar = 2

Piyoのなかでbarというinstance変数を作ろうとして実際のところはclass変数への代入が生じて間違えているのだが、継承ツリーが深いと気づけないかもしれない。どうしたら安全に書けるのだろうか。

名前を探すときにinstanceみてなければclassをみる
という類の話。

属性アクセスのデフォルトの動作は、オブジェクトの辞書から値を取り出したり、 値を設定したり、削除したりするというものです。例えば、a.x による 属性の検索では、まず a.__dict__['x'] 、次に type(a).__dict__['x'] 、そしてtype(a) の基底クラスで メタクラスでないものに続く、といった具合に連鎖が起こります。


a.__dict__にエントリを追加するのか代入しているのか区別がつかないことが原因のひとつではある。
問題が起こらないが、気になるときはparserとかformatterの__init__のなかでresetを呼ぶと時とかは

C++でやられているm_みたいなダサい解決策はcls_をつけるとかがそう。しかしそのようなnaming conventionは、文法上selfを書かせるpythonとしては正しい解決策(pythonic)ではないだろう。
いまどきの人はIDEつかうから問題にならないのかな・・・。


class Hoge(object):
index = dict(...)
def __getattr__(self, name):
begin, end, validator = self.index[name]
assert validator
return validator.from_bitarray(self._data[begin:end])

def __setattr__(self, name, value):
begin, end, validator = self.index[name]
assert validator
validator.to_bitarray(value, self._data, begin, end)

def __init__(self, s=None):
self.__dict__['_data'] = BitArray(66, binary=s, endian='<')

みたいなことをすればclassを書いた時点でインスタンス変数の名前は固定されてしまうので回避はできる。
いいかどうかは別にしてね。動的にインスタンス変数を増やすコードが簡単に書けるという人もいるかもしれないが、それはどのくらいつかわれるかとか、dictを変数でもってそのdictへ転送する手間は大したことじゃないはずだ。

propertyになぞらえていうなら(変ですが)、set, get, delの組じゃなくて、new, set, get, delの組であってほしいのです。

property( [fget[, fset[, fdel[, doc]]]])
新しい形式のクラス (object から導出されたクラス) における プロパティ属性を返します。
fget は属性値を取得するための関数で、同様に fset は 属性値を設定するための関数です。また、fdel は属性を 削除するための関数です。以下に属性 x を扱う典型的な利用法を示します:

class C(object):
def getx(self): return self.__x
def setx(self, value): self.__x = value
def delx(self): del self.__x
x = property(getx, setx, delx, "I'm the 'x' property.")
バージョン 2.2 で 新たに追加 された仕様です。


構文的にシンプルでわかりやすい回答がない、現実的なのか、そこまで問題なのかといった話のほかにもpython本体の実装のシンプルさの問題があるのだろうなぁ・・・。

2008年12月24日水曜日

Python 3000

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
諸般の事情でWhat’s New In Python 3.0を読んでます。関係するコードはどこかとか探そうとするとなかなか大変だったり。どうgrepするのかはたまたは提供している機能から連想するのか。

Need to know basisでしらべることしか普段していないと脳みその負荷の上がり方に痺れたり。

2008年12月19日金曜日

cacheの効果

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
1つ100k byteのエントリーを10k個つくって10k回set/getしてみた。


  • Memcacheはlocalhostで-m 2048(要は2GByteメモリを割り当てた)

  • pythonのtime.time()を使って測定。(なんか問題があったような気がする)



結果を見る限り、dictが早いの当然としてmemcacheとdiskの差だが、データセットがでかいのでswapしているが、memcacheはデーモン側で遅延書き込みが行われている分、diskをpythonからたたくdiskより、set関数の戻りが早いので速いといったところだろうか?

それからgetがすべての要素を1づつ順番になめているので、実際に起こるだろうアクセスの分布とはまったく異なる。その点にも留意しなければならない。なにか適当な分布関数を用意してlong tailになる局所性のあるアクセスを作り出さないとweb siteのキャッシュへの応用としてはだめだろう。

========== PerfDisk ==========
set: 0.00299947600365
get: 0.000581149601936
========== PerfMemcached ==========
set: 0.00100942788124
get: 0.000538439702988
========== PerfDict ==========
set: 6.94487094879e-06
get: 2.11861133575e-06

2008年12月18日木曜日

Cacheの階層

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
cacheの階層に思いをはせてめまいがした。

たとえばHTTPでなんかpageをリクエストしたとする。

サーバ側:
CPUのレベルでL1cache ~ L3 Cache
Memoryのレベルで
applicationが計算結果をmemory上にcacheする
applicationがmemcachdの上にcacheする。
applicationがdisk上にcacheする
OSがdiskアクセスをcacheする
apacheやsquidがネットワーク上でcacheする。

client側:
browserがcacheする。

なんて深いんだ・・・。

2008年12月17日水曜日

memo化につかうストレージとスピードup率

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
compilerモジュールとelementtreeを使って、pythonのcodeをparseしelementtreeを作るconverterを実装したが、結構遅いのでメモ化してみた。ただやるだけだと芸がないので、複数の方法を試した。

計測に使ったデータ。かなり小さい

#!/usr/bin/env python
# -*- coding: us-ascii -*-
# vim: syntax=python
#
# Copyright 2008 Noriyuki Hosaka bgnori@gmail.com
#
'''
module level document.
'''
import sys
from compiler import parse
import elementtree.ElementTree as ET

class Hoge(object):
def foo(self):
def bar(x):
return self
return bar

if __name__ == '__main__':
print "Hello World!"
print lambda x : x



  • dictを使う方法: 約144倍早くなった

  • memcacheを使う方法: 10 倍早くなった

  • fileをつかう場合: 1.3~2.2倍早くなった


測定回数がぜんぜん足りていないので、disk seekが起こる場合はなんともいえないだろう。
また、fileが小さいのでdiskの速度の上がり方の小ささはdisk seekの占める時間によって支配されているだろう。
through putが出る状況で、処理するfileが大きければもっと向上するだろう。
memcacheはpickleの処理とmemcacheとの通信処理だろう。ファイルが大きくなれば相対的に向上率は同じか低下するかだろう。

実用ではまた別途計測が必要だ。(/usr/lib64/python2.4下を全部処理して計測するとしないとね)

今後の実装方針としては、キャッシュの構造を二層化してまずはdisk cacheして次にmemcacheするか、disk cacheしてdict cacheするとかするとよいかもです。それで起動のたびに生成するのではなく、disk cacheから読もうとすればいいのでしょう。

2008年12月15日月曜日

改行コード

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
標準のライブラリは大丈夫だと思うが、site-packageにいれているようなものはひどいものがある。

class XMLSchemaFake:^M
# This is temporary, for the benefit of WSDL until the real thing works.^M
def __init__(self, element):^M
self.targetNamespace = DOM.getAttr(element, 'targetNamespace')^M
self.element = element

とかなっていると、fileobj = ile(..., 'rU')で読み込んであげないと改行のところでcompile.parse(fileobj)でエラーになってしまう。

しかしrUで読んでもだめなものがいくつかあった。


  • /usr/lib/python2.4/test/badsyntax_nocaret.py
    そもそもに文法的に正しくないテストコード(failすることを確認するためだと思われる。中身はこれ。

    def f(x):
    [x for x in x]=x


  • ファイルの終わりにspaceだけからなる行が存在していて、おかしくなっている。vimで開くとnoeolとかいわれる。

    • /usr/lib/python2.4/site-packages/yumgui/__init__.py

    • /usr/lib/python2.4/site-packages/yumgui/callbacks.py

    • /usr/lib/python2.4/site-packages/MoinMoin/_tests/test_pysupport.py

    • /usr/lib/python2.4/site-packages/testgears/command.py

    • /usr/lib/python2.4/site-packages/testgears/tests/test_collection.py

    • /usr/lib/python2.4/site-packages/turbojson/tests/test_sqlalchemy.py



  • ファイルの終わりにtabだけからなる行が存在していて、おかしくなっている。

    • /usr/lib/python2.4/site-packages/sqlobject/include/pydispatch/robustapply.py





このへんかな?

next()
ファイルオブジェクトはそれ自身がイテレータです。すなわち、 iter(f) は (f が閉じられていない限り) f を返します。for ループ (例えば for line in f: print line) のようにファイルがイテレータとして 使われた場合、next() メソッドが繰り返し呼び出されます。 個のメソッドは次の入力行を返すか、または EOF に到達したときに StopIteration を送出します。ファイル内の各行に対する for ループ (非常によくある操作です) を効率的な方法で 行うために、next() メソッドは隠蔽された先読みバッファ を使います。先読みバッファを使った結果として、(readline() のような) 他のファイルメソッドと next() を組み合わせて使うと うまく動作しません。しかし、seek() を使ってファイル位置 を絶対指定しなおすと、先読みバッファはフラッシュされます。

rpm packageするまえにText::FixEOLを通すような元気はあまりないなぁ。作る側でケアしてほしいのだが。

もうちょっと丁寧にみないといけないだろうが、今日はもうここまで。

2008年12月13日土曜日

PythonのLambda

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

import compiler import parse
from metrica.ast2et import convert, dump

ast = parse("""lambda : 0""")
t = convert(ast)
dump(t)

の出力は

PythonProgram
Module
+ doc : None
+ lineno : None
Stmt
+ lineno : None
Discard
+ lineno : 1
Lambda
+ kwargs : None
+ argnames : ()
+ flags : 0
+ lineno : 1
+ defaults : ()
+ varargs : None
Const
+ lineno : 1
+ value : 0

となる。

よく見ると、defaultsとかkwargsとかがある。ひょっとしてlambdaにdefaultが持たせられる??!

In [2]: f = lambda x=1: x

In [3]: f
Out[3]: <function <lambda> at 0x7fcfaf42c668>

In [4]: f()
Out[4]: 1

In [5]: f()
Out[5]: 1

In [6]: f(2)
Out[6]: 2

In [7]: f()
Out[7]: 1

In [10]: g = lambda x=0, y=1, z=2: (x,y,z)

In [11]: g()
Out[11]: (0, 1, 2)

In [12]: g(y=5)
Out[12]: (0, 5, 2)

うげー。ものすごく変な感じがする。まあ、pythonのlambdaはlispのlambdaじゃないからなぁ。

2008年12月12日金曜日

PyMetric撲滅運動その1

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
attributeが扱えるようになった。つぎはこの情報を使って、メトリック計算を行ったり、評価を行う機構を書けばいい。

入力

#!/usr/bin/env python
# -*- coding: us-ascii -*-
# vim: syntax=python
#
# Copyright 2008 Noriyuki Hosaka bgnori@gmail.com
#

'''
module level document.
'''
import sys
from compiler import parse
import elementtree.ElementTree as ET

class Hoge(object):
def foo(self):
def bar(x):
return self
return bar

if __name__ == '__main__':
print "Hello World!"
print lambda x : x


出力
elementtreeを自作のdumpでdump(serializeの問題を回避するため)


PythonProgram
Module
doc
module level document.

lineno None
Stmt
lineno None
Import
names [('sys', None)]
lineno 11
From
modname compiler
names [('parse', None)]
lineno 12
Import
names [('elementtree.ElementTree', 'ET')]
lineno 13
Class
doc None
bases [Name('object')]
name Hoge
lineno 15
Name
name object
lineno 15
Stmt
lineno None
Function
name foo
doc None
varargs None
argnames ['self']
flags 0
lineno 16
defaults []
kwargs None
decorators None
Stmt
lineno None
Function
name bar
doc None
varargs None
argnames ['x']
flags 0
lineno 17
defaults []
kwargs None
decorators None
Stmt
lineno None
Return
lineno 18
Name
name self
lineno 18
Return
lineno 19
Name
name bar
lineno 19
If
tests [(Compare(Name('__name__'), [('==', Const('__main__'))]), Stmt([Printnl([Const('Hello World!')], None), Printnl([Lambda(['x'], [], 0, Name('x'))], None)]))]
else_ None
lineno 21
Compare
lineno 21
ops [('==', Const('__main__'))]
Name
name __name__
lineno 21
Const
lineno 21
value __main__
Stmt
lineno None
Printnl
dest None
lineno 22
Const
lineno 22
value Hello World!
Printnl
dest None
lineno 23
Lambda
kwargs None
argnames ['x']
flags 0
lineno 23
defaults []
varargs None
Name
name x
lineno 23

PyMetric撲滅に向けて

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

def test_HelloWorld(self):
ast = parse('print "Hello World!"\n')
t = convert(ast)
self.assert_(t.tag == 'Module')
self.assert_(t.find('Stmt'))
p = t.find('*/Printnl')
self.assert_(p)

なるconvertを実装した。attributeやtextも実装しないとね。
変換してしまえばXPathが使えるので、イケてないコードの条件の記述が宣言的になる。

2008年12月11日木曜日

PyMetric

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
Libraryとして使うことはあきらめたほうがよさそうだ。C++かなんかの悪い癖丸出しでまったくpythonicでない実装だ。読んでいて気が狂いそうだ。名前も滅茶苦茶だし。

class ComputeMetrics( object ):
""" Class used to compute basic metrics for given module."""
def __init__( self, metricInstance, context, runMetrics, metrics, pa, so, co ):
""" Initialize general computational object."""

paってparseargumentかなんかの略語で、素敵なことに標準のOptParseを使っていてくれてない。だいたいなんでmodelであるはずのComputeMetricにapplicationの引数のパース結果を丸ごと渡しているんだ?必要以上の情報をオブジェクトに渡すのは極悪です。不要な結合が発生して、切り出して使おうとすると困るじゃないか。コメントも糞の役にも立ちません。名前と関数名と引数の名前を見ればわかります。astかなんか使って実装するのが普通ですが、parserを自前しているようです。

マッチョコーディング丸出しでキモイです。死んでくれって感じです。metricがないとマトモなコードにならない人が書くからこういうことになるのだろうなぁ・・・。なんだろう、来ない電車を待つやつはいないじゃないけど。

どーすんべか。以下idea.

  • black boxなscriptとして使用する。必要に応じて出力をparse(涙)して使う

  • 仕様を抽出してMcCabeとslocの計算法がわかればいいかなぁ。

  • indentのcheck, defaultは4space、optionで2spaceを許容。tabは常に禁止。

  • PEP8に準拠かどうかをチェックする

  • spell/case/naming checker、できる範囲で品詞もチェックしたい。

  • 変数のscopeと変数の語長の関係をチェック。広いスコープには長い名前、短いスコープには短い名前

  • search and kill poor comments.

2008年12月10日水曜日

心理

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

  • ideaを考えているとき: うきうきする。

  • codeを書いているとき: なんにかが違うとおもう。

  • testを書いているとき: めんどくさいとおもう。

  • debugしているとき: 無能モノだと自分を呪う。

  • unittestが全部通ったとき: ほっとしてにっこりする。

2008年12月6日土曜日

規模から見た現状

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
自分が作っているものの規模を確認してみた。

backgammon用自作ライブラリ(bglib)

unit testが769case, 29 fail 15 error。テストを走らせるだけで1分掛かる。
16596 Line 98file
プロプラエタリ。20Kはいきそうな気がする。

汎用自作ライブラリ(tonic)

unit test 20 case, 1 fail 2 error
1687Line 22 file
まだまだ成長の余地がある。近日中に200Lineは増えるだろう。

packaging tool (未完成)


unit test 62 case, 4 fail 9 error
2644 Line 54 file


wxpygammon, FIBSのクライアントアプリ(未完成)

unit test 3 case 2 fail(そもそもかかれていない)
2192 Line 19 file
もちろん最初に出てきたLibraryがないと動かない。
UIがあるものを効率よくテストしながら作るのは工夫が必要。
現状のままでは力尽きる。

imageserver、稼動中、image.backgammonbase.com

unit test 16 case 14 fail(メンテされてないなぁ・・・)
1010 Line 13file
2つ自作Libraryに依存。機能がシンプル(ライブラリのレンダラーを呼び出すだけ)なので、TGによって自動生成されたコードが3割くらい(?)、残りでformの処理をしている。cacheのコントロールはdecoratorとしてライブラリに切り出してある。

bglogin、未稼働 backgammonbase.com

unittest 0
572 Line 11 file
自作Libraryに依存。
OpenIDのloginとusername/passwordによるログインの両方をサポート。
データベースの設計が7割だった。このモジュールがほかのモジュールにidentityを供給する義務を負っている。

bgwiki、未完成。wiki.backgammonbase.com


unit test 0
2368 Line 12 file
履歴を持つことができるwiki。wikiの文法自体はbglibで処理している。

今日ちょろっと書いたhandicapのあるトーナメントのシミュレータ。

281 Line 6 file
bglib/statにいれるか。



svnを止めてgitに移行するためにもツールを完成させないとなぁ。もう一度、探すかなぁ。
脱線だが、gitのtagじゃなくてlatest commit のdateをとるほうがよい気がしてきた。tag timeじゃなくてtag lastcommitとかいうコマンドを用意すればいいのだろう。拡張は簡単なはずで、実装の手間の問題だ。

大規模ソフトウェアの効率的な理解(その2)によると10万行までが小規模らしい。てか、10万行を超えたくない。2万行でも負担に感じる。(10万で10人なら一人1万行の倍の量なわけで・・・)
行数を減らすために努力しているつもりだ。書き換え込みの延べ行数だと倍は書いているだろう。
大規模なソースコード、何万行までいじれますか?@/.jpというのもあるが、ライブラリの場合は名前空間が鍵になる。もちろんアプリから共通部分を抜き出してライブラリ化する行為自体も大事だ。とくに私はbackgammonのコードばかり書いているので、それを扱うのに有利なライブラリを早期に取り出し、よいデザインにしないと自分が「何度も同じことを似たようなコードで書き、バグをあちこちに埋める」というひどい目にあうことになる。ちなみにbglibの下はこんな感じに分割されている。

drwxr-xr-x 3 nori nori 4096 Jul 18 13:12 protocol
drwxr-xr-x 5 nori nori 4096 Oct 29 14:09 image
drwxr-xr-x 3 nori nori 4096 Nov 17 18:14 doc
drwxr-xr-x 3 nori nori 4096 Nov 18 19:52 stat
drwxr-xr-x 3 nori nori 4096 Dec 6 23:08 depot
drwxr-xr-x 3 nori nori 4096 Dec 6 23:08 gui
drwxr-xr-x 3 nori nori 4096 Dec 6 23:09 model
drwxr-xr-x 3 nori nori 4096 Dec 6 23:16 encoding


参考までに普段使っているOSSの規模をあげておく。

gnubg
13万行、300file(.c, .h)

python 2.4
.h 164 file 55000行、.c 1014 file 38万行、.py 1620 file 40万行

apache httpd 2.2
.c 21万行 272 file, .h 2万4千行, 136file、apr等は含まない。

linux 2.6 stable 2.26.27.7
.c 235万行、11000file, .h 100万行 11000 file、(ドライバや自動生成されたコードを含む)



apacheはおもったより小さかった。機能に比してでかいのかもしれないがその辺はよくわからない。
ちなみにカーネルはconfig項目だけで1000を超えているらしい。パンピーにはまったく縁の変なハードのドライバに関する選択肢も含まれているだろうが。

pythonは.cがコア機能の実装codeなのかC実装なライブラリ.soを生むためのC codeなのかが不明。
pythonの方が間違いない区記述能力は高いので、ライブラリのほうが言語自体よりも規模が大きい(当然か)。

gnubgはgtkでUIを実装しており、その部分がかなり大きい。neural networkの部分はそれほど大きくは無いはず。
modelに相当するgnubg.cやeval.cはそれぞれ6000行ほど。

find . | egrep "gtk.*\.c$" | xargs wc

とかしてgtkのコードを数えると30000行という答えが得られる。


もしかしたら桁を読み間違えているかもしれないので鵜呑みにしないように。

個人でやっているのでマンパワーは当然無い。だから、こういうdeployを手動でやるようなことは避けたい。

アップロードした後、プログラム名の日付やサイズが変わっているか目視チェックを行います。一人で行うとミスが発生しますので、複数の目で行います。また本数でもチェックを行い二重、三重にミスを防ぎます。今回はこれができなかったのではないのでしょうか。


とくにgnubgやMaraDNSはcentosには含まれていないので、自分でpackageしなければならない。
kernelやapache、pythonもそういう方向にするだろう(セキュリティ上の理由から)。つうわけで、packageをある程度自動化したいのです。でないと開発時間が捻出できない。

長期的な観点からはunittestの件数不足、all green状態維持をどうするかということかな。
タスク管理やDBもやら無いといけないのでこれらは5000Lineから10Kくらいだろう。

2008年12月5日金曜日

update作業てんこ盛り?

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
maradns
あとapacheとpythonか。直接サービスに関係するものはup to dateなものにするdistroは使わない方針で。

ところでgoogle chromeはdigest認証をちゃんとこなせてないらしい。Tracでeditができない。作業環境がどんどん悪化している。svnとのsyncもできていないし。乗りかえどきかねぇ。

OpenID関連

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
TechCrunchによればGoogle Friend Connectが全ウェブサイトに開放とのこと。 にも書いたが、弱小・個人サーバにとってはありがたいことだ。

2008年12月4日木曜日

Server has been down

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
DNS + Http down has been for while.
Sorry for inconvenience who are using git repository, backgammonbase.com and so on.

2008年12月1日月曜日

5GByteのfileのmd5sum

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
かなり差がある。5GByteをなめるだけでもNotePCのHDDには負担だろう。readのbufsizeも10Kbyteとかに指定してたし。

  • おんぼろNotePC windows上でpythonのmd5を使って計算→10分?かなり待たされた。

  • Q6600 with RAID 10 4wayのnative md5sumを使って計算→1分ばかり?

deployのプロセス

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
こんな感じですかねぇ。

  • package支援ツール(gitage)
    • tag打ち→tagを指定してrpm生成→staging repositoryへup

  • staging環境でtest

  • staging repositoryからservice repositoryにcopy

  • service環境に投入

  • 動作を確認


後ろの部分はたいしたことないのですが、一番最初のプロセスが混沌とするとよろしくない。てか、さくさくやりたい。とくにcommitされたものだけをpackageに含めたいので、local modificationのあるfileが誤ってpackageされるとようなことは避けたい。

異常なこだわりかもしれない。別にはてなのクローンになりたいわけではない。

ただ間違いなくこうすることで事故後の復旧作業が楽になる。yumのrepositoryとrpm/yumのdbあとはドライバをバックアップしておけば、何もないところからほぼ自動で元のサーバを再現できるからだ。

次にやることはdatabaseのbackupと復旧、staging、data bisect testの実装だ。非常に初歩的なbisectは用意できたのだが。ああ、stagingと代替サーバを別imageにしなきゃ・・。もきゅう。

repositoryのupdate rss

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
とか作る方法ないのかなぁ?

changelogをblogとしてupしてrss配信。
自動的にRPM化してそのlogもup

repository検索

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
git grepはどこまで使えるのだろうか?gccをとり終わった調べてみよう。
とくに変更をさかのぼって調べられるのだろうか?
つぎの3つはかなり意味が違う。

  1. checkout したものをgrepできる。ただのgrep

  2. 指定したtag、objectをgrepできる。checkoutの手間がないgrep

  3. ヒストリをすべてさかのぼってgrepできる。これがほしい。時間軸でfilterできるとうれしい。


まあ、Linus様のことだからわかっていらっしゃって作っているとはおもいますが。

gccを全取得

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
「全」取得がおばかなのですが実行中。
http://git.tonic-water.com/mirror/gccにとろうとしているのですが、ぜんぜん終わりません。3日くらいとり続けています。途中でgit svnが停止するのでそのたびに再度fetchを実行している。エラーしたら自動的に再fetchするようにしないとダルいかも。

xenはhgでした。gitに変換はしていません。やっぱりプロトコル・フォーマットの変換がないと軽いです。多分、git-svn/git cvsimportがforkしてconnectionのopen/closeを繰り返すことが原因なんですけどね。sourceをチェックしたところで改修案を思いつくかどうか定かじゃないし、めんどくさいからなぁ。どーせ引数渡してpipeしているのだろうし。となると起動されたされた子プロセスが即終わるんじゃなくて再利用とかだろうね。httpのconnectionをkeep aliveするようにhackせにゃならん。そう簡単な事ではない。