親子丼、おかわり

所用で田舎へ行ってきた。実りの秋でお土産いっぱい。

色々な種類の葡萄、りんご、プラム、栗、ブルーベリー。胡桃も有ったけどまだ今年のもの ではないので、次回の楽しみにした。

後、蜂蜜も、りんごの花のやつや桃のやつ、レンゲの花のやつとか、いろいろあったけど 食べきれそうになかったので、遠慮してしまった。

蜂蜜もいろいろな種類のやつを並べて見ると、色あいが薄いの濃いのとあって、見てるだけ で楽しめる。これも蜂さんの努力の賜物。

そう言えば、実家の軒先に、蜂が巣を作ってしまい大変だったとか。やっとの思いで 巣を撤去し、以後、蜂が嫌いな薬を散布したとか。

おいらが子供の頃は、学校サボって裏山へ地蜂を取りに行き、蜂の子をかすめてきたなあ。 蜂の子は、理科の実験室にあった、アルコールランプで炙って醤油垂らして食べたっけ。 古き良き思い出。

EHアンテナとか、スーパーラドアンテナ

今月号のCQ誌の表紙は、EHアンテナが誇らしげに飾っていた。135KHz帯がアマチュアに 公開されたのはいいんだけど、アンテナどうすべ?

ハーフラムダの大ポールで、アンテナ長さが1100mにもなると、そんなの張れる敷地無い よね。そこで、登場したのがEHアンテナ 極めて小さいアンテナなので、アパマン無線家の注目を浴びている。

また、小ささから言うとスーパーラドアンテナ も、負けてはいない。このアンテナは日本人の方が開発者 であり、第二の「八木宇田アンテナ」として、世界制覇なるかな? 注目しましょう。

親子丼、おかわり

前回は、chicken + egg の、一番美味しい所まで食べつくしていなかった。今回は、FreeBSDに 舞台を移して、じっくりと味わってみる事にする。

まずは、インタープリタ対コンパイラの対決だな。csi と言うのが、インタープリタ。cscは コンパイラ(ドライバー)だ。コンパイラの本体は、chicken。こちらは、素で使う事は無いだろう。

ソースを展開した中に、ベンチマーク用のコードが有ったので、試してみる。

[sakae@fb ~/chicken-4.1.0/benchmarks]$ cat fib.scm
;;; fib.scm

(define (fib n)
  (if (< n 2)
      n
      (+ (fib (- n 1)) (fib (- n 2))) ) )

(time (pp (fib 35)))

オリジナルでは、(fib 30)を計算してるんだけど、ちょっと多めに変更した。 まずは、インタプリタでやってみる。

[sakae@fb ~/chicken-4.1.0/benchmarks]$ csi

CHICKEN
(c)2008-2009 The Chicken Team
(c)2000-2007 Felix L. Winkelmann
Version 4.1.0 - SVN rev. 15292
freebsd-unix-gnu-x86 [ manyargs dload ptables applyhook ]
compiled 2009-09-17 on fb.kuma.net (FreeBSD)

#;1> (define (fib n)
  (if (< n 2)
      n
      (+ (fib (- n 1)) (fib (- n 2))) ) )
#;2> (time (pp (fib 35)))
9227465
   93.04 seconds elapsed
   1.947 seconds in (major) GC
      10 mutations
     170 minor GCs
     631 major GCs

これだけじゃ、速いのか遅いのか、比べる対象が無いので、我らがgoshに登場して貰おう。

[sakae@fb ~/chicken-4.1.0/benchmarks]$ gosh-rl
gosh> (define (fib n)
.....   (if (< n 2)
.....       n
.....       (+ (fib (- n 1)) (fib (- n 2))) ) )
fib
gosh> (time (fib 35))
;(time (fib 35))
; real  17.613
; user  17.242
; sys    0.023
9227465

この速さは、VM用にコンパイルしている裏方さんのおかげでしょう。 それでは、chickenの売り、コンパイラ君の登場です。

[sakae@fb ~/chicken-4.1.0/benchmarks]$ csc fib.scm
[sakae@fb ~/chicken-4.1.0/benchmarks]$ ./fib
9227465
  11.918 seconds elapsed
   0.074 seconds in (major) GC
       0 mutations
     941 minor GCs
      29 major GCs

大分早くなりましたねえ。更に爆速モードも用意されてます。

[sakae@fb ~/chicken-4.1.0/benchmarks]$ csc -Ob fib.scm
[sakae@fb ~/chicken-4.1.0/benchmarks]$ ./fib
9227465
   0.697 seconds elapsed
       0 seconds in (major) GC
       0 mutations
       0 minor GCs
       0 major GCs

参考までに、Cでもfibをやってみた。

[sakae@fb ~/tmp]$ cat fibc.c
#include <stdio.h>

long fib(long n){
  if ( n < 2){
       return n;
  } else {
       return fib(n - 1) + fib(n - 2);
   }
}

main(){
  printf("%ld\n", fib(35L));
}

[sakae@fb ~/tmp]$ gcc -O3 -fomit-frame-pointer fibc.c
[sakae@fb ~/tmp]$ time ./a.out
9227465

real    0m0.695s
user    0m0.679s
sys     0m0.000s

素晴らしすぎますよ。出来たバイナリーも程々にコンパクトです。ここから言える事は、インタプリタ を使って十分に虫取りをしておいて(その為に、step実行や、trace機能が搭載されてる)、 実際に使う時は、コンパイルしたのをどうぞって方針のようだ。

goshは使い捨て(でなくてもいいけど)のスクリプトをさっと実行すると言うフットワークの 軽さを求めてるのに対し、chickenは長く使うアプリ用って事かな。

[sakae@fb ~/chicken-4.1.0/benchmarks]$ ls -l fib
-rwxr-xr-x  1 sakae  kuma  9636 Sep 30 02:33 fib*
[sakae@fb ~/chicken-4.1.0/benchmarks]$ strip fib
[sakae@fb ~/chicken-4.1.0/benchmarks]$ ls -l fib
-rwxr-xr-x  1 sakae  kuma  6540 Sep 30 02:35 fib*

但し、このバイナリーは、chickenのライブラリーに依存してますので、何処に 持って行っても動く訳ではありません。chickenのシステムが有る事を前提としてます。

[sakae@fb ~/chicken-4.1.0/benchmarks]$ ldd fib
fib:
        libuchicken.so => /usr/local/lib/libuchicken.so (0x33c84000)
        libm.so.4 => /lib/libm.so.4 (0x33fa7000)
        libc.so.6 => /lib/libc.so.6 (0x33fbd000)

chickenの独立

コンパイラーの章の「生成された C ファイルを配布する」に、chickenシステムから分家する 方法が書いてある。例に倣ってやってみた。

[sakae@fb ~/work]$ chicken hello.scm -optimize-level 3 -output-file hello.c
[sakae@fb ~/work]$ gcc -static -Os -fomit-frame-pointer runtime.c library.c eval.c \
> extras.c hello.c -o hello -lm
/var/tmp//ccvaxwPt.o(.text+0x2ccb): In function `C_eval_toplevel':
: undefined reference to `C_expand_toplevel'
/var/tmp//cc9CCYm4.o(.text+0xf3d): In function `C_extras_toplevel':
: undefined reference to `C_data_structures_toplevel'
/var/tmp//cc9CCYm4.o(.text+0x1aac): In function `f_1288':
: undefined reference to `C_ports_toplevel'
/var/tmp//cc7SQ0eC.o(.text+0x250): In function `f_16':
: undefined reference to `C_data_structures_toplevel'
/var/tmp//cc7SQ0eC.o(.text+0x2bc): In function `f_19':
: undefined reference to `C_ports_toplevel'
/var/tmp//cc7SQ0eC.o(.text+0x394): In function `f_25':
: undefined reference to `C_srfi_69_toplevel'

何よこれ。見事にエラーじゃん。でも、的確なエラーが出てくるだけまし。エラーをヒントに ひたすら定義箇所を grepを使って探す。

結局、expand.c data-structures.c ports.c srfi-69.c を追加した所で、無事にコンパイルを 完了した。

[sakae@fb ~/work]$ ldd hello
ldd: hello: not a dynamic ELF executable
[sakae@fb ~/work]$ file hello
hello: ELF 32-bit LSB executable, Intel 80386, version 1 (FreeBSD), for FreeBSD 6.4 (604100), statically linked, FreeBSD-style, stripped
[sakae@fb ~/work]$ ls -l hello
-rwxr-xr-x  1 sakae  kuma  894084 Sep 30 03:05 hello*

コンパイラーフラグからも想像出来るけど、静的リンクで実行環境を一切喝采抱え込んだ、巨大な バイナリーが出来た。巨大になる事を開発者は気にしてて、サイズを小さくしてねと、gccに お願いしてる所が微笑ましい。おいらは、そこまでするなら、stripして更に贅肉を落とす 小細工をしちゃったよ。

で、先ほど grep してて気が付いたんだけど

[sakae@fb ~/chicken-4.1.0]$ ls -l *eval*
-rw-r--r--  1 sakae  kuma  752253 Jul 31 22:33 eval.c
-rw-r--r--  1 sakae  kuma   62630 Jul 31 22:31 eval.scm
-rw-r--r--  1 sakae  kuma  687305 Jul 31 22:35 ueval.c

cscコマンドは、*.scm をコンパイルして *.c を出力してくれるのはいいんだけど、上の場合の ueval.c は、何物よ? ちょいとソースを開いてみたら

[sakae@fb ~/chicken-4.1.0]$ head eval.c ueval.c
==> eval.c <==
/* Generated from eval.scm by the CHICKEN compiler
   http://www.call-with-current-continuation.org
   2009-08-01 00:33
   Version 4.0.7 - SVN rev. 15292
   linux-unix-gnu-x86 [ manyargs ptables applyhook ]
   compiled 2009-08-01 on x (Linux)
   command line: eval.scm -no-trace -optimize-level 2 -include-path . -include-path ./ -explicit-use -output-file eval.c
   unit: eval
*/


==> ueval.c <==
/* Generated from eval.scm by the CHICKEN compiler
   http://www.call-with-current-continuation.org
   2009-08-01 00:35
   Version 4.0.7 - SVN rev. 15292
   linux-unix-gnu-x86 [ manyargs ptables applyhook ]
   compiled 2009-08-01 on x (Linux)
   command line: eval.scm -no-trace -optimize-level 2 -include-path . -include-path ./ -explicit-use -unsafe -no-lambda-info -output-file ueval.c
   unit: eval
*/

っつう事で、cscに与えたオプションが違うって事ね。頭に"u"が付くほうは、向こう見ずな、 ぶっとび野郎仕様にしてまっせって事だな。ちなみに、どんなぶっ飛び野郎が居るかというと

[sakae@fb ~/chicken-4.1.0]$ ls u*.c
udata-structures.c      uposixunix.c            usrfi-4.c
ueval.c                 uposixwin.c             usrfi-69.c
uextras.c               uregex.c                utcp.c
ufiles.c                usrfi-1.c               utils.c
ulibrary.c              usrfi-13.c              utils.import.c
ulolevel.c              usrfi-14.c              uutils.c
uports.c                usrfi-18.c

重たい処理用に、用意されてるのね。納得しました。

そんじゃ、hello.scmから作ったhello.cも何か、ヒントっぽい事が書かれているかな? ただで見られるんだから、見なけりゃ損だよ!

[sakae@fb ~/work]$ head hello.c
/* Generated from hello.scm by the CHICKEN compiler
   http://www.call-with-current-continuation.org
   2009-09-30 02:56
   Version 4.1.0 - SVN rev. 15292
   freebsd-unix-gnu-x86 [ manyargs dload ptables applyhook ]
   compiled 2009-09-17 on fb.kuma.net (FreeBSD)
   command line: hello.scm -optimize-level 3 -output-file hello.c
   used units: library eval data_structures ports extras srfi_69
*/

見といて正解だね。used units: に注目。これって、先ほど、苦労して関数の定義場所を 見つけて、コンパイルの対象にしたファイルじゃん。最初から、ここを見れば良かったのね。

と言う事で、汎用のMakefileをでっち上げてみました。このMakefileを使って、fib.scmの 本家独立版を作ってみます。

# For standalone from chicken

.SUFFIXES:      .scm .c

SRC  = fib.c

OBJS = runtime.o library.o eval.o expand.o extras.o data-structures.o ports.o srfi-69.o $(SRC)

CFLGS = -static -Os -fomit-frame-pointer

RUNME:  $(OBJS)
        gcc $(CFLGS) -o $@ $(OBJS) -lm
        strip $@

.c.o:
        gcc $(CFLGS) -c $<

.scm.c:
        chicken $< -optimize-level 3 -output-file $@

コンパイルして、実行します。

[sakae@fb ~/work]$ make
gcc -static -Os -fomit-frame-pointer -c runtime.c
gcc -static -Os -fomit-frame-pointer -c library.c
gcc -static -Os -fomit-frame-pointer -c eval.c
gcc -static -Os -fomit-frame-pointer -c expand.c
gcc -static -Os -fomit-frame-pointer -c extras.c
gcc -static -Os -fomit-frame-pointer -c data-structures.c
gcc -static -Os -fomit-frame-pointer -c ports.c
gcc -static -Os -fomit-frame-pointer -c srfi-69.c
chicken fib.scm -optimize-level 3 -output-file fib.c
gcc -static -Os -fomit-frame-pointer -o RUNME runtime.o library.o eval.o expand.o extras.o data-structures.o ports.o srfi-69.o fib.c -lm
strip RUNME
[sakae@fb ~/work]$ ./RUNME
9227465
   9.366 seconds elapsed
   0.063 seconds in (major) GC
       0 mutations
     614 minor GCs
      29 major GCs

普通にcscでコンパイルしたよりは速くなりました。爆速モードへとコンパイル出来るのだろうか? ちょいと調べた限りでは、分家させちゃうと、本家の威光が及ばないっぽいので、そんなの無理 が、今の所の結論。

で、ふとした事からomit frame pointerなんて 言うページに行き着いた。chickenの出力がCのソースなら後残された道は、(g)ccに頼るしか 無い。オプションを見直そうと言う戦略。真面目にやると糞CPUの糞さ加減に付き合うはめに なりそうなので、何事も控えめに では、ありますが。

それより、目下の悩み所は、MinGW上で作ったchickenで、上のスタテック版を実行すると、M$へ 言いつけちゃうぞアラートが出てしまって、どうしようもない事です。例の「XXはreadに なりませんでした」と言う、役に立たないエラーが出てくるんだな、こうなったら、ブラジルの 開発者に通報する鹿。

奴らラテン系だから、「ヘィ、日本のおにーちゃん、まだ、糞のOSでごにょごにょかい! そん なの早く捨ててしまいな! パラダイスはLinuxだぜぃ」とか、軽く言われそうだな。