転職のお知らせ

世間は地震により慌ただしい。震源地近くの被災者の方にはお見舞いを申し上げる。 この最中に、個人的な報告を投下するのはちょっと憚られるものもあるのだけれども、あまり時期を外しても書く機会を逸するので思い切って書くことにしよう。

1月一杯で約3年務めた 株式会社スケールアウト を辞め、3月1日から Google Japan で働き始めた。twitterでは大勢の方に祝っていただき有り難い限りである。

Googleについては特に語ることはない。みんなが知っているし、私もまだみんなが知っているようなことの他は僅かしか知らない。だからスケールアウトについて語ることにしよう。

株式会社スケールアウト

スケールアウトは広告配信エンジン/広告在庫管理システム/広告入稿システム/広告案件管理システムをフルスタックの製品「ScaleAds」を提供するスタートアップである。

創業

創業者の山崎大輔さんは、元はYahoo! Japanの広告配信システムを構築した人である。一部では「 最速配信研究会のid:yamaz 」と言ったほうが通りがよいかも知れない。 まだ日本のネット業界が成立するかしないかの頃、彼はまだそれほど大きくなかったYahoo!に入って、今の巨大なサイトで高速にアクセスごとに最も適切な広告を選択して配信しているシステムの構築に携わった。

山崎さんはその実績もあってYahoo! Japanではそれなりに地位を得ることができたが、ある日自分の伸び悩みを感じて「このまま会社にしがみついて生き残ることは可能かもしれないが、それでは老害と化してしまう」と判断し、社外に挑戦の場を求めて武者修行に出ることを決心したそうである。そうして、スケールアウトを創業した。

そのスケールアウト社が作ったのがScaleAdsである。Webコンテンツを持っている会社が自社サイトに載せる広告を管理することを主に想定しているが、それに限らない。実際、メールマガジンでの広告の管理にも対応しているし、汎用のコンテンツ配信管理システムとして設計されている。 月間10億インプレッション程度であれば鼻歌混じりに捌き、サボらずにまともに設定すれば僅かなサーバー数で100億インプレッションを処理する。理論的には1000億PVもOKだ。

参加

現在のWeb業界にとって広告はオフラインとWebを繋いで経済を循環させる太い血管である。この血管の構築に携わりたいと思って、私もScaleAdsの開発に途中から参加した。そして、在庫管理システム/広告入稿・案件管理システムの継続的な改善に取り組んだ。

山崎さんの知識に基づく強力な配信エンジンを持つScaleAdsであるが、一方フロントエンド側や中間処理は私が参加した時点ではスパゲティであった。このレガシーコードとの戦いが私の仕事であった。あれを見たときにはコードベースを捨てることを提案したが、さすがにそれは山崎さんによって却下された。

レガシーコードを動いたまま移行させるのは書き直すことに比べて遥かに高く付く。それでも、そのコストに組織やメンバーが耐えることができたなら、危険な大きなジャンプをすることなく、機能のデグレードにも見舞われることもなく持続的なメンテナンスの可能なコードベースに移行することができる。 振り返って考えるに、恐らくコードベースを捨てて作業すれば半年で移行できたであろうが、代わりに一時的に大きな障害を引き起こした可能性もあったろう。Webシステムにあって最も金銭に近い *1 部分の1つである広告システムでは、資金的余裕の少ないスタートアップにはそのような失敗は許されない。こうして我々は3年間にわたる茨の道を歩くことになった。

十分にモジュール化されていないシステムを切り開き、テストハーネスを取り付けた。移行用のテストを書いてはモジュールを移行させ、テストを捨てた。いや、捨てるべきテストを捨てきれずに失敗した部分もある。幾つかのテスト戦略を試しては、幾つかは失敗した。これによりシステムから少しずつ腐ったコードが無くなっていった。拡張可能性を手に入れたシステムに、幾つか大きな機能追加を施した。

この時期この部分の開発に携わったメンバーを聞けば、東京のWeb開発者コミュニティ(特にRubyScala)にいる人ならば、この製品がどういうものであるのか分かるだろう。

一応、会社のサイトで公開している(いた)メンバーだけリストしたけれども、他にも一時期手伝ってくれた人は大勢いる。

転職

スケールアウトの長い戦いは今収束しつつある。安定性を更に増すために継続的インテグレーションで処理するテストケースはもっと増やすべきだが、それにしても主なモジュールのスパゲティは解消した。コードベースは継続的なメンテナンスが可能なものになった。継続的インテグレーションやその他諸々開発インフラも整えた。事業的にも顧客を増やして成功してきている。

一仕事終えた私はここでスケールアウトを一時離れることにした。これには2つ理由がある。1つには苦闘の日々が一時収束したので、次の大きな機能拡張の時期が来るまでは他でもっと面白いことをしていたかった。ただ、退職を決めた後になってから「次の大きな機能拡張」が思ったより早く来そうなことが判明したので、これについてはちょっと早まった判断だったかも知れない。

もう1つは、先に述べたようにスケールアウトはYahoo! Japanからのスピンアウト組を含んでいるということが関係ある。彼らが持っている大きな組織での開発者の政治的力学の経験や異分野の尖った専門家との繋がりというものを見て、興味を持った。そしてそれを体験してみたくなったのだ。 このため今回の転職ではチームでそれなりに大きな規模で開発している会社というのを一つのコンセプトとした。

このコンセプトに沿い、かつ 私が望むweb に貢献する会社を探した。そしてたまたまGoogleとご縁があった。こうして転職したのである。

*1:「金銭そのもの」であるオンラインバンキングやトレーディングを除けば

Understanding Go compiler tools (2)

Today I read memory management in go compiler.

There are go.y , y.tab.[ch] , lex.c and other files in src/cmd/gc/ directory. It seems that the go compiler has a lexical analyzer written by hand and a syntax parser generated by yacc ( bison ). lex.c also contains the main function for the compiler.

Here are the head lines of main function:

int
main(int argc, char *argv[])
{
        int i, c;
        NodeList *l;
        char *p;

        signal(SIGBUS, fault);
        signal(SIGSEGV, fault);

        localpkg = mkpkg(strlit(""));
        localpkg->prefix = "\"\"";

        builtinpkg = mkpkg(strlit("go.builtin"));

        gostringpkg = mkpkg(strlit("go.string"));
        gostringpkg->name = "go.string";
        gostringpkg->prefix = "go.string";        // not go%2estring

        runtimepkg = mkpkg(strlit("runtime"));
        runtimepkg->name = "runtime";

....

It is easy to guess what they do. They initializes signal handlers and bootstrap some standard packages.

Also we can see the declaration of NodeList *l . The parser must be going to store the result into the list.

memory management

I guessed that strlit function converts a C string into a String representation for the compiler. It was correct. The definition of strlit is in subr.c and it returns a pointer to Strlit . Strlit in go.h is commented as:

/*
 * note this is the representation
 * of the compilers string literals,
 * it is not the runtime representation
 */

Here I wondered how the go compiler manages heap memory for Strlit and others.

In general, memory management is an important part, although not a core topic. This is because there are many object to allocate during parsing sources and compiling. A parser must try to parse any input. It allocates some memory whenever it reads a term, and little by little constructs a syntax tree.

I heard that gcc uses garbage collection to mange memory on parsing and compiling. Ruby interpreter (CRuby) uses Ruby's garbage collector in order to manage heap memory for parser.

In the case of Go compiler, memory management seems naive.

Strlit*
strlit(char *s)
{
        Strlit *t;

        t = mal(sizeof *t + strlen(s));
        strcpy(t->s, s);
        t->len = strlen(s);
        return t;
}

strlit just calls mal and copies data into the allocated memory. mal must be a wrapper of malloc .

Here is the source of mal :

void*
mal(int32 n)
{
        void *p;

        if(n >= NHUNK) {
                p = malloc(n);
                if(p == nil) {
                        flusherrors();
                        yyerror("out of memory");
                        errorexit();
                }
                memset(p, 0, n);
                return p;
        }

        while((uintptr)hunk & MAXALIGN) {
                hunk++;
                nhunk--;
        }
        if(nhunk < n)
                gethunk();

        p = hunk;
        nhunk -= n;
        hunk += n;
        memset(p, 0, n);
        return p;
}

For large n it just calls malloc . For small n it adjusts alignment and returns a part of memory pointed by hunk .

This is the implementation of gethunk . There is nothing special.

static void
gethunk(void)
{
        char *h;
        int32 nh;

        nh = NHUNK;
        if(thunk >= 10L*NHUNK)
                nh = 10L*NHUNK;
        h = (char*)malloc(nh);
        if(h == nil) {
                flusherrors();
                yyerror("out of memory");
                errorexit();
        }
        hunk = h;
        nhunk = nh;
        thunk += nh;
}

Here is the implementation of remal , you know, a realloc equivalent.

void*
remal(void *p, int32 on, int32 n)
{
        void *q;

        q = (uchar*)p + on;
        if(q != hunk || nhunk < n) {
                if(on+n >= NHUNK) {
                        q = mal(on+n);
                        memmove(q, p, on);
                        return q;
                }
                if(nhunk < on+n)
                        gethunk();
                memmove(hunk, p, on);
                p = hunk;
                hunk += on;
                nhunk -= on;
        }
        hunk += n;
        nhunk -= n;
        return p;
}

It cares the case of extending the last part of hunk , however, that's all. It leaves heap memory leaked. The heap memory is collected by the end of process.

The go compiler seems to manage heap memory really naively and generously.

Here is another example. struct Node , which is the representation of a node in syntax tree, is just a concatenation of members which are necessary for each type of node. It is even not an union.

struct Node
{

....

        // if-body
        NodeList*        nelse;

        // cases
        Node*        ncase;

        // func
        Node*        nname;
        Node*        shortname;
....
}

Conslusion

The go team does not seem to care about this kind part of the implementation. They prerfer keeping the source simple to strictly implementing it.

I think they have a plan to make the go compiler self-hosted. Go have a garbage collector, a parser generator and even a parser for Go. So they can replace the go compiler with one written in Go itself.

Understanding Go compiler tools (1)

Recently I read Go lang . I began to understand its structure.

How to build

You can build the go compiler and tools as documented in the official documentation .

It is quite easy. But it was confusing for me that I must run ./all.bash instead of usual make (1). I wonder why they don't simply use make . Anyway the bash script internally calls make as usual.

After building, I got the following executables in bin directory.

  • 6a: assembler
  • 6c: C compiler
  • 6g: Go compiler
  • 6l: linker
  • 6nm: same as nm(1)?
  • cgo
  • ebnflint
  • godefs
  • godoc
  • gofmt
  • goinstall
  • gomake
  • gopack
  • gopprof
  • gotest
  • gotry
  • govet
  • goyacc
  • hgpatch
  • quietgcc: gcc wrapper which is little slienter than native gcc

The oddish *1 prefix "6" is because I built the tools on amd64 architecture. The prefix varies according to architecture.

There are only three architectures which is implemented in the source:

This convention came from Plan 9 . I found the definitions for other architectures in include/mach.h :

2c(1) in Plan 9 describes one more architecture:

Directory structure

  • bin

    where executables will be generated into.

  • doc

    documentation

  • include

    C header files.

  • lib

    where some libraries will be generated into.

  • misc

    misc

  • pkg

    where Go standard packages (libraries) will be compiled into.

  • src

    source codes

  • test

    test cases

Let's dive into src :

  • cmd

    source codes for the Go tool chain

  • lib9

    Plan 9 C library?

  • libbio

    buffered IO library

  • libcgo

    ?

  • libmach

    library which contains architecture dependent codes

  • pkg

    source codes of Go standard packages

commands

Let's dive into cmd directory.

  • 5a/
  • 5c/
  • 5g/
  • 5l/
  • 6a/
  • 6c/
  • 6g/
  • 6l/
  • 8a/
  • 8c/
  • 8g/
  • 8l/
  • cc/
  • cgo/
  • cov/
  • ebnflint/
  • gc/
  • godefs/
  • godoc/
  • gofmt/
  • goinstall/
  • gomake/
  • gopack/
  • gotest/
  • govet/
  • goyacc/
  • hgpatch/
  • ld/
  • nm/
  • prof/

[568][acgl] directories contain architecture dependent part of the sources. Architecture independent parts of the Go compiler are in gc directory.

I will read the detail of gc in the next post.

*1:oddish, at least for me, who grew with GNU/Linux environment.

Localizing irb messages

Do you know rubyists in Japan use Irb in Japanese?

% irb --help
Usage:  irb.rb [options] [programfile] [arguments]
  -f            ~/.irbrc を読み込まない.
  -m            bcモード(分数, 行列の計算ができる)
  -d                $DEBUG をtrueにする(ruby -d と同じ)
  -r load-module    ruby -r と同じ.

Irb has had ability to localize the help message and some error messages, and has been shipped with Japanese localization by historical reason. Also I improved this localization mechanism so that messages are converted correctly with String#encode and that Irb can load a localization from a gem. But this fact is not well known. I could not find any other localization with Google search.

Recent events

Recently Abinoam Jr. wrote Portugese localization for IRB . He sent it to me and I suggested that he should distribute it as a gem.

At this time I found that the mechanism that load a localization from a gem is broken on Ruby 1.9.2. Anyway, I fixed the problem at r30448 and the next patchlevel release of Ruby 1.9.2 will be able to load a localization from a gem again.

How to write a localization for Irb

It is quite easy. Suppose that we are writing a localization for zh_JP.UTF-8@ancient locale and let /path/to/somewhere the working directory.

First, copy error.rb and help-message from $(rubylibdir)/irb/lc into /path/to/somewhere/irb/lc/zh_JP.UTF-8@ancient/ . *1

Second, translate the copies of error.rb and help-message into zh_JP.UTF-8 like this:

# -*- coding: UTF-8 -*-
律:  irb [選項] [簒譜] [参數]
-f          不讀~/.irbrc
-m          bc態(得簒分數行列)
....

Place the correct magic comments in both of the files.

Then, move the irb directory which contains the translated files into somewhere under $LOAD_PATH . I think $(sitelibdir)/ is preferred.

Finally you can distribute the irb/lc/<your-locale>/* as a gem instead of install it as a legacy style library. Ruby 1.9.1 and the next release of Ruby 1.9.2 can load a localization from a gem.

*1: Japanese localization has encoding_aliases.rb in addition to the two files. But encoding_aliases.rb is just for backward compatibility with Ruby 1.8. You don't have to implement it in your locale

コミケ79行

コミックマーケット79 に行ってきた。ずいぶん久しぶりの参加な気がする。昨日はPassengerの設定やらで明け方まで作業してしまったこともあるし、 昼頃からゆるゆると参加した。

ActiveWorksが復活して新刊出していたけどSymphony (PHP)ネタだったのでパス。代わりにSolaris本の前に逃したやつが今回は在庫があったので買ってきた。

途中、マイコン島に行くと、 イカ娘 の帽子を作ってるサークルがあって、新刊を買うついでに帽子も買った。実は、この手のイカ娘のなげやりコスはしたいなと思っていたのだけれども、制作が間に合わずに断念したので丁度良い。この帽子を被って他のサークルを回ったりした。

戦利品

システムをRuby 1.9に移行

このブログシステムはこれまでRuby 1.8で動いていた。これをRuby 1.9に移行するのは長い間の夢だったのだけれども、なかなか実現できなかった。

まず1.9.1が出た初めの頃にはライブラリの対応がまるで駄目であった。そこで手始めに postgresqlドライバ を移植して本家にパッチを送ってみた。あちこちで1.9対応を訴えて、ようやくライブラリが出そろった頃には今度は自分の開発時間がとれなくなっていた。この年末にようやく時間が取れたので、システムを1.9.2に移行した。

Railsのバグを踏んだり、とある古いライブラリは1.9対応していなかったのでちょっとだけ手を入れたり。以下、はまったこと(あとで詳しく書くかも)

  • application/www-form-urlencodedの中に入っている文字列データのエンコーディングは転送時に失われるので、決め打ちでforce_encodingするしかない。それか、エンコーディングを持つ為だけのparameterを暗黙にフレームワークが足すべきなのかも
  • RDtoolのfilter機構は一時ファイルに書き出した時点でデータのエンコーディングが失われる。期待するのはUTF-8なんだけど、仕方がないからdefault_externalがUTF-8になるように環境変数LANGを変えた。
  • 一部のライブラリ(この場合はbackground_fu)はrubyコマンドを"ruby"で決め打ちしてる。一方、1.8との共存のためにサーバー上ではRuby 1.9.2コマンドにはsuffixが付いている。仕方がないからrbconfigを使うように書き換える。
  • うっかりaptitudeでpassengerを入れたら、これはRuby 1.8とリンクしていたので実行時に落ちた。Ruby 1.9のgemから入れ直した。
  • US-ASCII文字列とUTF-8文字列の結合でIncompatibleEncoding例外が発生するのはよく分からないな。バグか? default_externalをUTF-8にして、外部由来のASCII文字列がUTF-8になるように修正して逃げた

テクノロジーの世界の女性のロールモデルについて考えてみた

最近、「テクノロジー(あるいはオープンソース)の世界で目立つ女性というのは珍しい」 というようなことを言われることが何件か重なった。「 Rubyがそろそろ一回終わってみるべき10の理由 」とか、その他何件かね。 その重なりは私に、何か色々なことを考えさせた。考えたことについて何とはなしに書き下してみようと思う。

前提と社会

何とは言ってもオープンソースの世界で活躍しようと思ったら、それが好きでなければならない。そりゃあ、今時はオープンソースを積極的に貢献し、それを利用しようとする企業も少なくない。しかし、開発コミュニティには沢山の、開発が好きで好きで仕方が無くてそれに時間を努力を惜しまない人々がいる。その中でなにがしかをなしとげようと思ったら、やっぱり「業務命令だから」じゃなく「好きだから」でなければやっていくのは難しいだろう。

で、ソフトウェア開発が好きで好きで仕方がない女性ってのはどれだけ居るんだろうね。ソフトウェア開発はとても人間的な活動で、いろいろな人と関わって、いろいろな人の考えに思いを巡らせる社会的な活動だ。でも、その入り口はとても無骨な技術の姿をしている。テクノロジー、コンピュータ。所謂"理系"の世界ってものだ。そもそも、"理系"とされる分野に進む女性が少ないしね。

何故、"理系"に女性が少ないのかは様々な要因が考えられて、それは既に多く語られていると同時にそれが完全な回答ってわけでもなさそうだ。ざっと思いつく限りでも、まずひょっとしたら生物学的要因に基づく向き不向きがあるのかも知れない。そういうのは女の子らしくないって考えられていて、進路のあちこちに無意識の抑圧があるのかも知れない。

で、こうして生まれた全体的な傾向はポジティブフィードバックする。つまり、ここにカーネルハッカーになる素質をもった女の子が居たとしよう。カーネルいじって喜んでいてもそれが同性の友達に通じる可能性は少ないし、通じる話題を持たなければつまはじきにされるし、あること無いこと噂を立てられる。幸運にして話の通じる「男の子」の友達が見つかったとしても、それは確かに友達が見つかったという点では良いことだけれども、しかし、女の子のネットワークの間では悪評の要因になったりする。だからカーネルをいじる時間はあったとしても少なくなる。だから、彼女はカーネルハッカーとして成長する上でハンディキャップを持つ。そこまで育たないかも知れない。もっと他のことを面白いと感じるようになって、例えば服飾の道にでも進むかも知れない。

また、野望や目標は状況により作られるものだ。たいていの人にとって。誰も思いつきもしないことを欲望することができるのは、新しい時代を切り開く天才だけだ。普通の人は、精々誰かが似たようなことをやっているのを見て、自分もそうしたいと思う程度のことしかできない。だから、テクノロジーの世界で女性が活躍するのをあまり知らずに育った女の子は、自分がテクノロジーの世界で活躍するという可能性自体が頭の中にない。そして、その可能性を欲望しない。それを面白そうな、素敵な将来だと思ったりしない。

ロールモデル

この状況を断ち切るにはどうしたら良いだろう。テクノロジーの世界で活躍する女性の姿が目立てば目立つほど、将来の世代が自分の1つの可能性を知る機会が増える。ロールモデルというものだ。沢山のロールモデルが提供されれば、いずれは「女の子らしくない」とかいうよく分からない幻想も崩壊する。無意識に共有されるそうした幻想はなかったことになる。

だから、誰かが「テクノロジーの世界で目立っている女性」になるのは良いことだ。

実は、そんなことを考えて先日 楽天テクノロジーアワード という賞を頂戴した。誰かが目立たなければならないなら、その1人として私は目立とうと思う。そういうわけで、賞は有り難く頂戴したし、私はできるだけインタビューやら講演やらを断らないようにしてる。まー、本当は、女性に対してという以上に トランスセクシュアルに対してのロールモデル を提供するためというのが一番の目的ではあるんだけど。

立場

さて、ここで考えてしまう。私はオープンソースの世界で活躍する女性のために何か役に立てたらいいなと思うのも動機の一つとして、目立とうともする。しかし、私は、私が役に立ちたいと思っている女性たちが置かれている困難を必ずしも共有はしていないんだな。

私は、シス・ジェンダーの女の子とも男の子とも決定的に違ったから、いつでも「男女」って分類と制約からはある程度の自由を持っていた。あるいはそれは、「男女」って分類を前提としたシステムから疎外されていたともいうけど、システムから疎外されればシステムの提供する膨大なメリットを失うと同時にシステムが課す制約からは逃れられる。名目上こそそのシステム内に居ることになっていたから、完全に自由ではなかったけど、少なくともそのシステムは私の形には合っていなかったから、留め具が部品に合わないみたいに、そこには部品が自由に動ける(動いてしまう)余地があった。 つまり、私は女の子がテクノロジーを身につける過程で受けるであろう意識的・無意識的な、他者からあるいは自身からの抑圧を、かなりの部分受けないで済んだ。

また、こんなこともあった。性別違和感や、自分を性同一性の指し示すところとは異なる存在であると思い込もうとすることは生活のすべてを灰色にする。普通ならば喜びであるはずの、ファッションやら家族とのやりとりやら、食べること話すこと歌うこと走ること、日常の様々なことが私には苦痛でしかなかったから、私はテクノロジーや理学分野 *1 に逃避した。だから、私はそれほど迷わずにテクノロジーに没頭できた。

ってな訳で、困難を共有していない私がモデルとなることはできるのであろうか、と考えたりした。 答えは出なかったし、「女性のためのロールモデル」は私の一番目の目標ではないし、別に私こそがなるという必要はないし、なれないならなれないで良いんだけれど。

蛇足

時期的に重なったので弾さんの「 好き嫌い以前の問題として - 書評 - 女ぎらい 」における私への言及も気にはなったけど、色々考えた結果、ここで語ったような内容とはあまり関係しなかった。

弾さんが言っているのは、まず生活のすべてにおいて何らかの性に関わる事項が立ち現れる訳ではないということだ。そりゃそうだ。また、弾さんの論に立ち現れるのは精々、性的自己認知や社会的関係性における性別だが、そのコンテキスト・「射影関数」の限りでは私は女性と断言して差し支えない。一方、 適切な射影関数の定義操作 をしなければ、弾さん自身についてさえ「性別は○○である」と短く述べることはできない。多くの人が自己の性別を単純に何かであると言い切ることができるのは、日常で用いられる範囲の射影関数の限りにおいて、概ね同値関係が成立するような性別「男・女」が存在し、その人がそれらの関数の限りにおいてはどちらかに移されるからに過ぎない。関数の選択を一般に広げるならば、私に限らず、誰もがその人でしかない。何人も、何か事前に共有された簡潔なもので指し示されることはできない。

*1:だけではないけど。小説書いたり生徒会やったり部活を率いたり、要するに、生活に密着すること以外の何かに。誰もが当たり前にする日常のことではないほど、私はそれが得意だった