Syntax highlighter

2016-05-25

File monitoring on OS X using FSEvents

(sagittarius filewatch) on OS X is using kqueue (2) currently. Using kqueue isn't bad just having couple of limitation such as no capability of directory monitoring (due to my laziness). It is OK on BSD environment since this is the only choice to do it. However, on OS X, there are FSEvents APIs which allow users to monitor filesystem. When I research it, it doesn't require loads of file descriptors nor limit file/directory only monitoring. So I thought this might be a good underlying implementation for OS X and implemented like this.

If it ends without any problem, as usual, I don't write any blog post. Yes, there's a huge problem. It doesn't allow me to write tail emulator. I first thought my implementation has an issue. So I've written this small piece of code to check if it works as my expect.
#include <CoreServices/CoreServices.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

static void callback(ConstFSEventStreamRef stream,
                     void *callbackInfo,
                     size_t numEvents,
                     void *evPaths,
                     const FSEventStreamEventFlags evFlags[],
                     const FSEventStreamEventId evIds[])
{
  FILE *fp = (FILE *)callbackInfo;
  char buf[1024];
  const char **paths = (const char **)evPaths;
  for (int i = 0; i < numEvents; i++) {
    while (1) {
      int n = fread(buf, 1, sizeof(buf), fp);
      fwrite(buf, 1, n, stdout);
      if (feof(fp)) break;
    }
    fflush(stdout);
  }
}

int main(int argc, char **args)
{
  if (argc != 2) {
    fputs("fstail file", stderr);
    exit(-1);
  }
  FILE *fp = fopen(args[1], "r");
  fseek(fp, 0, SEEK_END);
  
  CFStringRef s = CFStringCreateWithCString(kCFAllocatorDefault, args[1],
                                            kCFStringEncodingUTF8);
  CFArrayRef ar = CFArrayCreate(NULL, (const void **)&s, 1, NULL);
  FSEventStreamContext ctx = {0, fp, NULL, NULL, NULL};
  int flags = kFSEventStreamCreateFlagFileEvents;
  FSEventStreamRef stream = FSEventStreamCreate(NULL, &callback, &ctx, ar,
                                                kFSEventStreamEventIdSinceNow,
                                                0, flags);
  FSEventStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(),
                                   kCFRunLoopDefaultMode);
  FSEventStreamStart(stream);
  CFRunLoopRun();
  return 0;
}
This didn't work like tail command, unfortunately. It didn't receive any event after it got first event. If you get a technical problem, you probably search Google or Stack Overflow. Yes, I've found the very similar issue: No callback get called from FSEventStreamCreate with modifications created by self in watched file. It seems the question didn't get any useful answer. Feeling like I'm missing something, but I don't know what. I've also changed latency argument to non zero value but got no luck.

As long as this problem is not solved, I can't use FSEvents. So for now, I use kqueue, which works perfectly fine for my purpose, on OS X...

2016-05-14

equal?の挙動

二週間前くらいにバグの報告を受けた(それ自体は修正済み)。バグの原因を突き詰めた際に「これはブログのネタになる」と思っていたのだが、それから随分時間が経ってしまった。多少賞味期限が切れてしまった気がしないでもないが、ちょっとした後方互換を壊す修正でもあるし、適当に記録に残しておく。ちなみにバグの報告はこれ

バグの原因を要約すると以下の二つ:
  1. eqv?が循環構造のあるレコードを受け取るとSEGVを起こす。
  2. equal?がレコードの中身を比較する(R6RS的には規格違反)
随分前に(R7RSがまだドラフトだったときじゃないかなぁ)、レコードの中身をチェックするようにしていたのだが、それが噛み付いてきた感じである。eqv?がレコードの中身をチェックするのは、実はR6RS、R7RS両方で規格違反なのだが(明確にアドレス比較のみと書いてある)、equal?を変更した際にR6RSのテストスイートを通すために必要だったという経緯がある。(テストケースの意味もよく分からないんだけど、フィールドの存在しないレコードはコンストラクタが同一のオブジェクトを返してもいいってことなのかなぁ?)

1.に関してはレコード周りを取り除いてしまえば直るのは明白だったのだが、2.との兼ね合いでどうしようかなぁとい感じになるもの。そもそも、eqv?が中身を見るというのはいろいろおかしい感じがするので(リストの中身とか見ないわけだし)、取り除いてしまいたい感はあった。となると2.との兼ね合いだけなんだけど、これ自体はもともと便利だからという理由で入れてあっただけなので、正直取り除いてもそんなに影響ないだろうと思っていた。実際R7RS的には未規定なわけだし。が、SRFI-116に落とし穴が潜んでいた。

SRFI-116はイミュータブルなリストを定義しているんだけど、その参照実装のテストケースがequal?にレコードの中身を検査することを要求するコードになっていた。参照実装はChibiとChickenの2つの処理系で動くように実装されているのだが、どうもこの2つはレコードの中身を見るみたいである。新しいSRFIだからまだ使ってる人は少ないだろうし、暗黙的な要求なので無視しても問題ない気はしたのだが、人気処理系のうちの二つでやられているのなるとなぁという気持ちの方が大きかった。

じゃあどうしたか?実装をR6RSのequal?とそれ以外という風に分けた。ポータブルなコードを書くという上ではR6RSの方がより細かく規定してあるのでいいのだろうけど、これくらいの処理系拡張は許して欲しいという気持ちもある。そうすると実装を二つにする以外に方法が思いつかなかったのだ。ということで、(rnrs base)(scheme base)で定義されているequal?は別物になった。この動作に依存したコードを書くというのはあんまりないと思うけど、ハッシュテーブルをequal?で作ってキーにレコードを使用している場合は影響がでるという話。

2016-05-08

肉体改造部 第十七週

ほぼ月一になっている気がする。

計量結果:

  • 体重: 70.4kg (-0.5kg)
  • 体脂肪率: 21.6% (-0.8%)
  • 筋肉率:43.3% (+0.1%)
間で増減していたのは観測しているのだが、増えるときは筋トレサボったりしているのが主な原因な気がする(大体サボっていたので)。このくらいの体重を維持しつつ体脂肪率を15%以下くらいにしたいところである。

2016-04-20

言語レベル

(年に一回くらいこの手のことを書いてる気がしないでもないなぁ)

ある言語を話すことができるという理由で与えられるチャンスはそんなに多くないが、話せないから逃すチャンスというのは多々ある。これは英語に限ったことではなく、例えばここオランダでは募集要項にネイティブもしくはそれに準ずるオランダ語が話せること、ということが明確に書かれていることがある。逆に言うと、英語が話せればいいという職も多数ある。これが日本だと日本語は大前提になるのでExpatの多い国の特徴とも言えるのだろう。自分自身がどれくらい話せるのかとかを客観的に見たことがあまりないので、多少いろいろな角度からどの言語がどれくらいできるのか分析してみたくなった。

分析するにはある程度の基準がいる。とりあえず大きく5つのレベルに分けることにした。ただし、中間を表すために総数を20段階とし、5段階で区切るというようにする。例えば、日常会話はレベル5だが、ビジネスレベル(レベル10)に達していないが日常会話以上というのはレベル6から9の間といった具合である。以下はレベル:
  • レベル0:その言語を全く話せない
  • レベル5:日常会話レベル、かなり大変だがその言語で生活できる
  • レベル10:ビジネスレベル、職場でコミュニケーションができる
  • レベル15:高等教育レベル、日本なら高校卒業時の国語
  • レベル20:専門家レベル、この言語に関する知識で飯が食える
レベルの付け方に異論はあるかもしれないが、日本語だとTPOに関する場合分けが多いのである程度専門的な教育を受けていないとTPOにあった言葉遣い、例えば敬語等、ができないとしている。また、ビジネスレベルが低めなのは、少なくともオランダでは、仕事で使うツールとしての言語ではコミュニケーションができることが、当然だが、重要視され文法等の細かいことは必要以上に重要視されないというところからきている(もちろん文書などを書く際は別だが)。

さて、上記のレベルに自分の言語を当てはめてみる。何かしらのテストを受けて計測したというわけではないので、感覚的にという単なる目安である。
  • 日本語:レベル15(多分もう少し低いが、敬語とか忘れたし、日本の高校卒業してるということで)
  • 英語:レベル13 - 14(多少色眼鏡付きな気もするけど)
  • オランダ語:レベル4 - 6(一応生きていけるが辛い。仕事では使えない)
こんな感じだろう。言語の能力を語る上で語彙数も重要になる。ここによると、日本語の語彙は大学生レベルで4万5千~5万語らしい。一応大学出ているのでそれくらいとしておこう(正確に測るの怖い)。英語の語彙は数年前に測った時に1万2千~1万5千だったのでそうしておく(新聞を辞書なしで読めるレベル)。ちなみに、オランダに来た時点では5000程度だったので、そこから見ると随分増えたともいえる。オランダ語の語彙は知らない。2000ないかも。

全体の習熟度とすればこんな感じなんだろうけど、個別にみると意外と面白いことが分かる。例えばくしゃみをした人にかける言葉として英語では「Bless you」、オランダ語では「Gezondheid」がある。オランダに長く住んでいるので誰かがくしゃみをすると、たとえくしゃみをした人がオランダ語を話せなくても、「Gezondheid」というようになった。同様なものに「Alstublieft」もしくは「Alsjeblieft」がある。もう少し込み入った例だと、「Kan ik pinnen?」ある。これは「Can I use debit?」のオランダ語バージョンと思ってもらえばいいのだが、アメリカに行ったときとか、店員さんがオランダ語喋れない場合でもこれが勝手に出てくる(アメリカで出た際は流石に、「Kan ik,,, can I use credit card?」になったが)。特定のシチュエーションに於いてはアウトプットが最も多いものが勝手に口をついてくるみたいである。

言語の習熟度があがると、言語間の壁のようなものが薄くなる気がする。最近は日本語のやたらいっぱい母音を喋らないといけないというのが面倒に感じるのだが、これが時として悪い方向に働く。例えば日本人と話しているのに、ふっと英語になるとか。これが起きるときは大体分かってて、
  1. グループ内に日本人以外がいる
  2. カタカナ語を英語の発音で喋ってしまう
この二つが主な原因である。1は非常に簡単で、通訳してると出力方面がごちゃごちゃするという単なる混乱。2は、個人的には面白いと思っているのだが、英語の発音で自分の中のコンテキストが切り替わるというもの(これを回避するために日本語喋ってるときは頑なにカタカナ語で喋るのだが)。どうも、英語方面へのスイッチは緩いらしい。英語→日本語にシフトしたことはないのでそういうことだと思っている。母語が日英両方だった場合には起きないんだろうか?不思議である。

とりとめなく終わり。

2016-04-19

Inter-operable hidden binding

The subject might sound weird, but I couldn't find any better name. So please bare with it.

Problem

Suppose you have a situation that 2 macros need to refer the same implicitly bound variable. For example, consider define-generic and define-method; the declaration of generic function is done by define-generic, and adding definition to it is done by define-method. Now, you want to write it as simple as possible, so you've decided to use implicitly bound hashtable.
;; naive definition of define-generic (doesn't work)
(define-syntax define-generic-naive
  (syntax-rules ()
    ((_ name)
     (begin 
       (define implicit (make-eq-hashtable))
       (define (name . args)
          ;; lookup and execute
          )))))

(define-syntax define-method-naive
  (syntax-rules ()
    ((_ name formals body ...)
     (begin
       (define (real-proc . formals) body ...)
       (define dummy
         ;; oops, implicit can't be referred here!
         (hashtable-set! implicit 'name real-proc))))))
Now, your task is make this happen somewhat.

Passing explicitly

Taking this path isn't really what I want, but it's the only way to do it on R7RS.
(define-syntax define-generic-explicit
  (syntax-rules ()
    ((_ name table)
     (begin 
       (define table (make-eq-hashtable))
       (define (name . args)
          ;; lookup and execute
          )))))

(define-syntax define-method-naive
  (syntax-rules ()
    ((_ name table formals body ...)
     (begin
       (define (real-proc . formals) body ...)
       (define dummy
         (hashtable-set! table 'name real-proc))))))
The problem with this implementation is that you need to know the name of the shared bindings. It might be good for debugging or breaking, but you probably don't want to care about something only used internally.

Macro generating macro

If 2 macros cannot refer the variable defined in one of the macro, then make it in the one macro like this:
(define-syntax define-generic/defmethod
  (syntax-rules ()
    ((_ name method-name)
     (begin
       (define shared (make-eq-hashtable))
       (define (name . args) 
         ;; lookup and execute
         )
       (define-syntax method-name
         (syntax-rules ()
           ((_ name shared formals body (... ...))
            (begin
              (define (real-proc . formals) body (... ...))
              (define dummy 
                (hashtable-set! shared 'name real-proc))))))))))
It's probably better than explicitly passing, but it's rather ugly. The method definition should be more generic. In this implementation, the method definition belongs to specific generic function definition.

Identifier macro

If you are using R6RS, then syntax-case can handle non list macro (not sure how it should be called, but I say identifier macro). So if the name of generic function itself can be evaluated to implicit definition name, then we can share the binding by referring the name.
(define-syntax define-generic
  (syntax-rules ()
    ((_ name shared)
     (begin
       (define shared (make-eq-hashtable))
       (define (real-proc . args)
         ;; lookup
         )
       (define-syntax name
         (lambda (x)
           (syntax-case x ()
             ((_ args (... ...)) #'(real-proc args (... ...)))
             (k (identifier? #'k) #'shared))))))))

(define-syntax define-method
  (syntax-rules ()
    ((_ name formals body ...)
     (begin
       (define (real . formals) body ...)
       ;; method name should generic function name;
       ;; thus, it's an identifier macro to return
       ;; implicit table name.
       (define dummy (hashtable-set! name 'name real))))))
Better, at least for me. If I see it with half eye closed, then it looks like fake LISP-2.

Pitfalls I've got

I first thought that maybe I can use datum->syntax to create the same name; however, this wasn't a good idea. It is okay to if both generic function declaration and method definitions are in the same library; otherwise, you'd get a problem. Suppose you have a library (a) contains only define-generic and other library (b) contains define-method. Now, which template identifier you should use to generate the same name of the implicit binding? You need to use the identifier define-generic in the library (a), and it's impossible to use it in library (b). (This is the reason why I needed to write the version 2, macro generating macro.)

Conclusion

I don't have any intention to say, macro is the best, or something like that, but if something beyond procedure (in this case, emulating LISP-2, kind of), then it is rather necessary feature.

2016-04-15

Generic record copy

I've found a tweet that says R7RS define-record-type doesn't create copier (or copy constructor) by default. Well, I can imagine why it doesn't if I think of C++'s copy constructor (which I think very confusing and causing unexpected behaviour). And it's also context dependent what exactly copy means.

Now, if I just say like this, then it's not so fun. So let's write kind of generic copy procedure. Before that, here our definition of copy is deep copy. So it creates a new object without having the same object inside. So more like cloning.

Preparation

If it's generic, then it should work also builtin types. Generally, Scheme chose to have distinct procedures per types and what we want is one generic procedure. The very simple strategy would be dispatching. It might be convenient if users can specify how copy works per types. So the interface of copy procedure would look like this:
(define *copier-table* '())

(define (generic-copy obj)
  (cond ((assoc obj *copier-table* (lambda (x p) (p x))) =>
         (lambda (s) ((cdr s) obj)))
        ;; shallow copy, sort of
        (else obj)))

(define (register-copier! pred copier)
  (set! *copier-table* (cons (cons pred copier) *copier-table*)))
To register built-in types, we can do like this:
(register-copier! pair? list-copy)
(register-copier! vector? vector-copy)
(register-copier! string? string-copy)
(register-copier! bytevector? bytevector-copy)
Now, we have generic copy procedure for built-in types.

NB: list-copy and vector-copy doesn't consider the elements of copying object. If you want to follow the definition of copy here, you need to create own copy procedure.

Syntax

You know how define-record-type works, right? It needs to be fed name of constructors, predicate procedures. So doing the same for copy procedure. Let's call our brand new record definition syntax define-record-type/copy. It would look like this:
(define-record-type/copy pare (kons a d) pare? pare-copy
   (a kar)
   (d kdr))
The extra argument pare-copy is the procedure automatically generated by the macro.

Implementation strategy

Now, how can we implement it? The strategy I chose (and probably this is the only way to do it portably) is that:
  • Collect field value and order it by constructor tag
  • Create object by passing above value with specified constructor
  • Set field values of fields which are not listed on constructor
So my implementation is like this:
(define-syntax define-record-copier
  (syntax-rules ()
    ((define-record-copier "emit" name (ctr f ...) (acc ...) ((a m) ...))
     ;; now we have all information
     (define (name obj)
       (let ((c (ctr (acc obj) ...)))
         ;; mutate if mutators are defined, then we use it.
         ;; to make it simple, we do for all mutator. so some
         ;; of them are just useless.
         ;; FIXME this is not efficient.
         (m c (a obj)) ...
         c)))
    ((_ "mutator" name ctr accessor mutator ())
     (define-record-copier "emit" name ctr accessor mutator))
    ((_ "mutator" name ctr accessor (mutator* ...) ((f a) rest ...))
     (define-record-copier "mutator" name ctr accessor
       (mutator* ...) (rest ...)))
    ((_ "mutator" name ctr accessor (mutator* ...) ((f a m) rest ...))
     (define-record-copier "mutator" name ctr accessor
       (mutator* ... (a m)) (rest ...)))
    ((_ "collect" name ctr (acc ...) () (def* ...))
     (define-record-copier "mutator" name ctr (acc ...) ()(def* ...)))
    ((_ "collect" name ctr (acc ...) (field field* ...) (def* ...))
     (begin
       ;; this part is not R7RS portable since 'foo' doesn't have to be
       ;; renamed (right?). so some of implementation may raise an error
       ;; of redefinition (e.g. Foment)
       ;; however we can't use letrec-syntax because it creates a scope.
       ;; sucks...
       (define-syntax foo
         (syntax-rules (field)
           ((_ ?n ?c
               ((field ac . ignore) rest (... ...))
               (next (... ...))
               (src  (... ...)))
            (define-record-copier "collect" ?n ?c (acc ... ac)
              (next (... ...)) (src (... ...))))
           ((_ ?n ?c (_ rest (... ...)) (next (... ...)) (src (... ...)))
            (foo ?n ?c (rest (... ...)) (next (... ...)) (src (... ...))))))
       (foo name ctr (def* ...) (field* ...) (def* ...))))
    ((_ name ctr (ctr-field* ...) (field-def* ...))
     (define-record-copier "collect" name ctr
       () ;; accessor
       (ctr-field* ...)
       (field-def* ...)))))

(define-syntax define-record-type/copy
  (syntax-rules ()
    ((_ name (ctr field* ...) pred copier field-def* ...)
     (begin
       (define-record-type name (ctr field* ...) pred
         field-def* ...)
       (define-record-copier copier (ctr field* ...)
         (field* ...) (field-def* ...))))))
I usually use letrec-syntax to detect free identifier (well, it should be bound identifier but I don't think there's no way to do it in range of R7RS). But needed to use define-syntax (see comment).

Then you can use it like this:
(define-record-type/copy pare (kons a d) pare? pare-copy
   (a kar)
   (d kdr)
   (s pare-src pare-src-set!))

(register-copier! pare? pare-copy)
(let ((p (kons 'a 'b)))
  (pare-src-set! p '(src))
  (let ((c (generic-copy p)))
    (print (kar c))
    (print (kdr c))
    (print (pare-src c))))
(Write your own print procedure :P). The implementation is not efficient since we call mutator procedure no matter what. To make it efficient, you need to get mutators of which are not listed on constructor tags.

The whole scripts are here:

Conclusion

Use R6RS or SRFI-99.

2016-04-11

肉体改造部 第十四週

先週は何故か忘れた。

計量結果:

  • 体重: 70.9kg (-0.3kg)
  • 体脂肪率: 22.4% (+0.3%)
  • 筋肉率:43.2% (±0.0%)
最近胃袋が小くなったのか、一回に食べる量が減ったのだが、食べる回数(間食)が増えている気がする。Courseraのコースを取ったので(言い訳)筋トレをさぼりがちになっているというのもよろしくない。

2016-04-08

mod-exptの高速化

タイトルは大分嘘です。

Linux上での暗号ライブラリテストが以上に遅かった。他のOSでは問題ないのだが、Linuxだけ10倍以上遅い。何が遅いのかなぁと調べてみると、鍵対の生成が1024ビット程度でも3秒くらいかかっているというものだった。これはおかしいなぁと思っておもむろに鍵対生成をプロファイラにかけてみるとmod-exptが遅い。120回程度呼ばれて1500ms消費という感じであった。

この手続き自体は確かに重いものなのだが、どうもおかしい。以前(多分0.5.x辺り)ではそんなに時間がかかった記憶がない。つまりその辺から今までで入れた変更でLinuxのみが遅くなった可能性がある。記憶を辿ってみると確かにBignumの演算に手を入れた記憶があったので、とりあえずソースを覗いてみる。っが、特に不審な部分も見当たらない。Linux固有の何かを使ったものはないという意味でではあるが。

疑問を疑問のままにしておくのは今一気持ちが悪いので、Valgrindについてるcallgrindを使ってCレベルのプロファイルを取る。すると、mod-exptの処理自体は高速に終わっているという結果が取れた。っで、コールグラフのその下を見ると、スタック領域の割り出しの処理が異常に重たい。そういえば、Bignumの計算でスタックが溢れる不具合を直した際にそんな処理入れたなぁと思い、ダミーの値を返すようにしてSchemeのプロファイルを取る。3秒が30msになった。お前か・・・

具体的にはスタックベースを取得するのが異常に遅いっぽかった。そもそもスタックベースなど一回取得してしまえば変更されることはないはずなので毎回値を律儀に取得しにいくこともないよなぁと思いスレッドローカルな静的領域に格納するように変更。これだけで100倍の高速化に成功した。(実際は100倍の低速化が行われているので、元に戻っただけだが・・・)

ここからは(も?)与太話。
スタックベースの取得にはBoehmGCのGC_get_stack_baseを使っているのだが、LinuxとCygwinで100倍以上の差が付くのはなぜだろうと思いちょっと実装を覗いてみた。Linux(x86_64)では以下の処理を行う:
  1. pthread_getattr_npの呼び出し
  2. pthread_attr_getstackの呼び出し
  3. pthread_attr_destroyの呼び出し
それぞれの関数の呼び出しがどれくらい重いかはよく知らないが、1万回以上の呼び出しがあったのでそれなりにはかかるだろう。(Bignumの処理は大抵再帰なので再帰的にスタック領域の確認をするのだ。)

っで、Cygwinの実装を見てみた。
    GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *sb)
    {
      void * _tlsbase;

      __asm__ ("movl %%fs:4, %0"
               : "=r" (_tlsbase));
      sb -> mem_base = _tlsbase;
      return GC_SUCCESS;
    }
以上!そら速いわ。。。1万回呼び出されても誤差の範囲に収まるだろうなぁというのは想像に難くない。

特に何もなく、callgrindが便利だったというだけの話だったりはする。他のプロファイラと違いランタイムにリンクさせる必要ないというのはとてもありがたい。その分処理は劇的に遅くなるけど、的が絞れているならこれほど便利なものはないなぁと思ったのでした。

2016-04-01

プロセスとI/O

サーバーが正常に立ち上がったかどうかを確認するのに起動ログを見るか、実際にアクセスして動かないことを確認するしかないというのがだるくなった。なので、ファイルを監視しつつ失敗のキーワードがあれば通知するものを作ったのだが、どうもプロセスをデタッチすると何も出力されないことに気付いた。Sagittarius 0.7.2までは子プロセスの標準入出力は常にパイプが割り当てられるのだが、親プロセスが終了するとパイプから出力を読み取るプロセスがなくなるので何も出力されないという話だった。これでは不便だなぁと思い、えいや!っと出力先を制御できるようにしてみた。

こんな感じで使う。
(import (rnrs) (sagittarius process))
(let ((proc (make-process "foo" '("process")))
      (outfile "pout"))
  (process-call proc :output outfile))
これで、プロセスの標準出力はpoutというファイルになる。出力先を標準出力にしたいときは:stdoutを使う。もちろん、入力(:inputキーワード引数)とエラー出力(:errorキーワード引数)もサポートしている。便利手続きのcreate-processもこれを考慮するように変更したいが、まだしてない(こっちはパイプでも問題ないようにしか使ってないとも言う)。今のところ出力先ファイルは上書きででしか開けないが、必要があれば追記できるようにするかもしれない。

これ変更したのはいいけど、実際に通知を行うのにコンソールに垂れ流すと見落とすということで、notify-sendコマンド使ってデスクトップに通知するようにしたら、パイプ使ってても問題ないなくなった。変更自体は有用だと思うけど、最優先で変更したのはいいが必要なくなった子になってしまった。

2016-03-29

Ellipses expansion of syntax-rules

An interesting post was posted on c.l.s. (c.f. Nested ellipses) It's about how ellipses of syntax-rules should be expanded. The code is as follows:
(define-syntax test
  (syntax-rules ()
    ((test (x ...) ((y ...) ...) )
     '((x (x y) ...) ...) ) ) )
(test (a b c)
      ((1 2 3) (4 5 6) (7 8 9)) )
I'm not sure if this is an error since (x (x y) ...) contains 2 times x followed by an ellipsis. (I think it is, and SRFI-72 expander, a.k.a Van Tonder expander, signals an error.) So I've removed the first x and tested on couple of R6RS and R7RS implementations.
;; Removed the first x
(define-syntax test
  (syntax-rules ()
    ((test (x ...) ((y ...) ...) )
     '(((x y) ...) ...) ) ) )
(test (a b c)
      ((1 2 3) (4 5 6) (7 8 9)) )
#|
Either:
#1
(((a 1) (b 2) (c 3))
 ((a 4) (b 5) (c 6))
 ((a 7) (b 8) (c 9)))
Or
#2
(((a 1) (a 2) (a 3))
 ((b 4) (b 5) (b 6))
 ((c 7) (c 8) (c 9))) 
|#
Implementations emit the #1 are the following:
  • All R6RS implementations
  • Foment
Implementations emit the #2 are the following:
  • Chibi
  • Sagittarius using (scheme base) library
  • Gauche
  • Picrin
To me, if I modify the template like above, it should emit the #1 result. Both R6RS and R7RS have some kind of specification of this pattern:
Pattern variables that occur in subpatterns followed by one or more ellipses may occur only in subtemplates that are followed by (at least) as many ellipses. These pattern variables are replaced in the output by the input subforms to which they are bound, distributed as specified.
R6RS: 11.19 - Macro transformers
Pattern variables that occur in subpatterns followed by one or more instances of the identifier ellipsis are allowed only in subtemplates that are followed by as many instances of ellipsis . They are replaced in the output by all of the elements they match in the input, distributed as indicated.
R7RS: 4.3.2 - Pattern language
I think the difference between R6RS and R7RS is matched ellipses consuming part. R6RS also requires the following:
The subtemplate must contain at least one pattern variable from a subpattern followed by an ellipsis, and for at least one such pattern variable, the subtemplate must be followed by exactly as many ellipses as the subpattern in which the pattern variable appears. (Otherwise, the expander would not be able to determine how many times the subform should be repeated in the output.)

This, I believe, restricts that input expressions of the multiple ellipses have the same length of input. In above example, x and y should have the same length. R7RS, on the other hand, requires to consume all input. Thus, x and y may have different length of inputs (e.g. (test (a b c) ((1 2 3 10) (4 5 6) (7 8 9))) should be valid on above example). In such cases, the expander can't determine how it should be expanded if it needs to expand like R6RS does (as R6RS mentioned).

Maybe there's more direct statement which specifies the behaviour of this case.

2016-03-27

肉体改造部 第十二週

風邪ひいて一週間マルッと寝込んでいたりして二週飛ばし。こんなんばっかだな。。。

計量結果:

  • 体重: 71,2kg (+1.7kg)
  • 体脂肪率: 22.1% (+1.6%)
  • 筋肉率:43.2% (+0.6%)
体重が落ちたのはまず間違いなく風邪のせいだと思われる。まぁ、落とすことも目標ではあるので、問題はないのだが。

なんだかんだで食べ過ぎるなぁという感じがあるので、頑張って食欲に負けないようにしないといけないのだが、「ダイエットは明日から」という言葉を使う人の気持ちが分かるレベルで誘惑に負けそうになる(というか負けてる)。

2016-03-26

ファイルシステムの監視 実装編

とりあえず、inotify、kqueueとReadDirectoryChangesWの3つで大体同じように動くものができた。例えばtailコマンドっぽい何かは以下のように書くことができる。
;; tail.scm
(import (rnrs) (getopt) (sagittarius filewatch) (binary io))

(define (tail file offset)
  (define watcher (make-filesystem-watcher))
  (define in (open-file-input-port file))
  ;; dump contents to stdout
  (define (dump)
    (let loop ()
      (let ((line (get-line in)))
        (unless (eof-object? line) 
          (put-bytevector (standard-output-port) line)
          (put-bytevector (standard-output-port) #vu8(10))
          (loop)))))
  (define size (file-size-in-bytes file))
  ;; move port position if the size if more than offset
  (when (> size offset) (set-port-position! in (- size offset)))
  ;; dump first
  (dump)
  ;; add path to file watcher
  (filesystem-watcher-add-path! watcher file '(modify) 
                                (lambda (path event) (dump)))
  ;; monitor on foreground.
  (filesystem-watcher-start-monitoring! watcher :background #f))

;; this tail is not line oriented
;; it shows tail of the file from the given offset.
(define (main args)
  (with-args (cdr args)
      ((offset (#\o "offset") #t "1024")
       . rest)
    (tail (car rest) (string->number offset))))
#|
sash tail.scm foo
|#
これで延々とファイルに追加されたものを標準出力に吐き出していく。まだドキュメント化していないが、捻りを加える必要もないだろうし、多分これが最終形になると思われる。

実装に関して
前回も書いたが三者三様なのでそれぞれ苦労した。inotifyは素直にできているのでLinuxのinotify(7)にあるサンプルを参考にしながらで十分だった。思ったとおりこれが一番楽だった。次いでkqueueなのだが、こいつは例があまりなかったのと、kqueue自体が非常に総称的にできているので理解するまでに苦労した。理解してしまえばまぁそれほどという感じではある。ReadDirectoryChangesWはこれ自体はそんなに複雑じゃないんだけど、どちらかというとそれ以外の部分(OVERLAPPEDとかFILE_NOTIFY_INFORMATIONとか)が多少面倒だった感じ。動いてるけど正しく実装したのか自信ない。

実装間に於ける制限
意外だったのはkqueueが制限が一番大きくなったこと。kqueueはファイルの監視はできるけど、ディレクトリの監視をした際にどのファイルが変更されたとか追加されたとかを知る術がない。ものすごく頑張ればやれなくないんだけど、監視対象毎にファイルディスクリプタが必要になるので、万を超えるファイルとかがあるディレクトリの監視とかすると普通に死にそう。(ものすごく頑張る必要性を今のところ感じていないので頑張っていないが・・・)
次いでinotify。ディレクトリの再帰的監視は頑張らないと無理(kqueueも無理だけど)。まぁ、再帰的に監視したいかと言われるとよく分からないが。とりあえず他の実装もこれにあわせるようにしてお茶を濁した。
Windowsはファイルの監視ができないんだけど、ディレクトリの監視をすればどのファイルが変更されたかの情報が取れるので特に問題なかった。ありそうなのは、Windows Vista以降ではデフォルトでアクセス時間の変更がされないので、それの監視をしたい場合はシステムを弄らないといけないことか(実装とは関係ない) 。

落とし穴
Cygwinが実は落とし穴だった。CygwinはPOSIX環境を提供してくれるんだけど、inotifyもkqueueもPOSIXじゃないので存在しない。そしてCygwin自体にファイル等を監視するようなAPIもない。どうしたかといえば、Windowsの実装の上にパス変換(cygwin_conv_path)を噛ませるようにした。また、監視を止めるのに他の環境だとスレッドに割り込みをかけるようにしているが、Windowsのコードを流用しなければならないのでそれができない(Cygwinはスレッドの割り込みにシグナルを使うがWindowsはSetEventを使っている)。しょうがないので泥臭い方法で回避している。

所感
疲れた。後は使いつついじっていく感じかな。

2016-03-22

ファイルシステムの監視

最近ファイルの変更を検知したいという要望が僕の中であがってきている。追記型のログファイルを調べるとかそんなちょっとしたことからなんだけど、あると便利かなぁと思い始めてきた。っで、いろいろ調べてみた結果OS毎に作法が全然違うという悲しい事実が判明。Linuxはinotify、WindowsはReadDirectoryChangesW、*BSDはkqueue、OSXはFSEvents(だけど、kqueueも使えるっぽいのでそっち使う予定。手元にOSないし)みたいである。

ちらっと調べてみた感じでは、それぞれに一長一短あるんだけど、個人的にはinotifyが一番楽っぽいイメージ。次いでkqueue。Windowsはやりたいことはやれなくないけど結構大変っぽい。Windowsの問題はファイルシステムの監視がフォルダのみというところで、ファイル自体の変更を検知する直接的な方法はないっぽい。パスを分解、フォルダを監視、その上でターゲットのファイルが変更されたかどうかをチェックするという方法になりそう。

一番楽っぽいかなと思われるLinuxの実装は既にリポジトリに入れた。どのイベントを取るかとか、ディレクトリが指定された際はどうするかとかまだ考えないといけないけど、単純なファイルの監視というところは動いている。次はWindows+Cygwinを何とかしつつ、kqueueは最後にやる感じ。一番問題になるのは、動作をそろえるところだろうなぁ。こればっかりは地道にやるしかないので、ある程度作ったら使いながら調節するという感じになるような気がする。

2016-03-19

ズンドコキヨシ

風邪(と思われる)で1週間寝込んでいたのだが、体調がほぼ戻ってきた。Twitter等でズンドコキヨシなるものを見かけたので、1週間ぶりにリハビリを兼ねて書いてみた。(1週間もコード書かないと鈍るよね?)

#!r6rs
(import (rnrs) (srfi :27))

(define zun "ズン")
(define doko "ドコ")
(define kiyoshi "キ・ヨ・シ!")

(define (zun? o) (string=? zun o))
(define (doko? o) (string=? doko o))
(define (kiyoshi! o) (display o) (display kiyoshi) (exit 0))

(define zun&doko (vector zun doko))
(define (zundoko-generator) (vector-ref zun&doko (random-integer 2)))

(define init-state 0)
(define (gen-next n) (lambda (o) (display o) n))
(define ->init (gen-next init-state))
(define states
  `#((,zun?  ,(gen-next 1) ,->init)
     (,zun?  ,(gen-next 2) ,->init)
     (,zun?  ,(gen-next 3) ,->init)
     (,zun?  ,(gen-next 4) ,->init)
     (,doko? ,kiyoshi! ,(gen-next 4)) ;; more than 4 zun, loop it
     ))

(random-source-randomize! default-random-source)

(let loop ((ns init-state))
  (let ((token (zundoko-generator))
        (state (vector-ref states ns)))
    (if ((car state) token)
        (loop ((cadr state) token))
        (loop ((caddr state) token)))))
普通に4回と1回を数えた方がすっきりするような気もしないでもない。

2016-03-07

オランダの転職エージェント

Amazonのオファーを蹴ってしまった+収入を増やしたい(いろいろ要りようなのですよ)という思いから転職活動をしている。なんだかんだ(いろいろリスクはあるが)で手っ取り早く収入を増やすならよりよい条件の職場に行くのが早い。そうはいっても、あまりがつがつ転職活動をするというつもりもなく、いい条件の職があったらという感じでゆる~くやっているのではあるが。

オランダに限らず転職は3種類くらいパターンがあると思う。
  • 自分で探す
  • 向こうから声をかけられる
  • エージェント経由
二つ目と三つ目は同じかもしれないが、あえて分けている。正直エージェントを使うのは好きではないのだが(理由は後述する)、今回は会社がLeidenにあるということでなんとなく面接することにした。オランダで転職エージェント経由で転職活動するのは大体以下のような流れになる。
  1. エージェントから連絡がくる
  2. 先方との面接日時を決める
    1. 面接
  3. エージェント経由でフィードバックを受け取る
  4. 採用、不採用が決まるまで2-3までを繰り返す
2-3のループは大体2回、多くても3回という感じ。その合間辺りで希望する年収(もしくは月収)を聞かれる。

さて、これだけならわざわざブログの記事にすることもないのだが、ちょっと頭にきたことがあったりして吐き出しを兼ねて適当にエージェントを使うのが嫌いな理由を書いていく。

【転職理由】
大抵のエージェントは、なんでこの会社にいきたいか、みたいな事を質問してくる。ついでになんで転職するのかとか。一度素直に「金」と答えたら、「それじゃだめだ」みたいなことを言われたことがある。仕事内容なんてお前らが言ってることと一致したことねえんだよ!転職理由なんて金払いがよければそれで十分だ!僕にとって「やりがい」とかは副次的であって、主目的は「金」だよ!「やりがい」で腹は膨れないっつーの!

【希望収入】
少なくともオランダでこれを聞かれるときには、セットで現在の収入も答える必要がある。ここで、現在の収入を真面目に答えると損をするので必ず月収なら500ユーロは多く答えておくとよい(経験談)。
転職するのであれば、よほど今の会社から逃げたいとかを除いて、収入が上がることを期待したいものである。よくも悪くも転職にはリスクが伴うし(ペンションとか、職歴とか)。っで、現在の収入より月500ユーロ多くもらえるのを希望すると、「500ユーロも増やせると思うの?」とか「現在の収入からみて妥当なところを探す」とか言われることがある(3分の2のの確立)。ぶっちゃけ、これを言われたら萎える(萎えた、今日)。Nettで300ユーロ増やすのがそんなに悪ですか?あの手この手使って最低ラインを下げようとしてますよね?ぶっちゃっけ月100ユーロ増える程度では職変えないぜ、普通。お前らの交渉能力の低さを棚に上げてこっちにばかり妥協点を押し付けんな!あんまり声を荒げるとか、大人気なく喧嘩する気もないので、希望額に届かなかったら容赦なく辞退するだけですよ。

【自己矛盾】
今の会社もエージェント経由だったんだけど、どうも同じ会社っぽいんだよね。当時(一年前だが)の担当(今の担当の上司らしい)は、この会社に採用された人は長く続けてるからこの会社はいい会社だ、みたいなこと言ってたんだけどねぇ。まぁ、人売り人買いの会社なんて早々に転職させて金儲けしてるんだから当然なのかもしれないけど、この節操のなさにはこっちもびっくりですよ。

【恩着せがましい】
「この会社に他の人を送るのストップしてる」というのは彼らの殺し文句である。そっちの事情は知ったことではないのだよ。それで恩を売って、決まった際に「これだけやったんだから給料が低くても転職しろ」みたいな態度に出られてはたまらない。お前らは仕事、こっちはリスクを負う、恩も義理もない。ビジネスでやってるのに、人情を人質に取ろうとするのに反吐がでる。多少の害には目をつぶれってか?ふざけんな!

適当に書きなぐってしまった。使えそうなら使うくらいの立場でいた方がいいということ。変に義理立てしたり、向こうの意味不明な言論に左右されないというのが大事である。あぁ、腹立った。

2016-03-06

肉体改造部 第九週

なんかいろいろあって2週ほど飛ばしてしまった。

計量結果:

  • 体重: 72.9kg (+0.1kg)
  • 体脂肪率: 23.7% (+0.1%)
  • 筋肉率:42.6% (±0.0%)
先々週のことはあんまり覚えていないんだけど、先週は油断してちと食べ過ぎたのとあんまり筋トレに時間が割けなかった(筋トレの消費カロリーがどれくらいかは知らないけど…)ことを考えると順当に増えたといえるか?

最近懸垂が普通に10回x4セットくらいできるようになってきたのでちょいちょい筋肉が付いてきたのではと思っているのだが、数字には表れていない様子(当てになるかもよく分からんけど)。懸垂しても上腕二頭筋にあまり負荷がかかっていない感じがするということは、自重トレーニングする分には十分ということなのだろうか?ジムに行きたいところではあるが、時間が取れないんだよなぁ。

2016-03-04

Cache

I'm currently working on ORM library (this) and have figured out that creating prepared statement is more expensive than I expected. You might be curious  how much more expensive? Here is the simple benchmark script I've used.
(import (rnrs)
        (time)
        (sagittarius control)
        (postgresql))

(define conn (make-postgresql-connection
              "localhost" "5432" #f "postgres" "postgres"))
;; prepare the environment
(postgresql-open-connection! conn)
(postgresql-login! conn)
(guard (e (else #t)) (postgresql-execute-sql! conn "drop table test"))
(guard (e (else #t))
  (postgresql-execute-sql! conn "create table test (data bytea)"))

(postgresql-terminate! conn)

;; let's do some benchmark
(postgresql-open-connection! conn)
(postgresql-login! conn)

(define data
  (call-with-input-file "bench.scm" get-bytevector-all :transcoder #f))

(define (insert-it p)
  (postgresql-bind-parameters! p data)
  (postgresql-execute! p))

;; Re-using prepared statement
(let ((p (postgresql-prepared-statement
          conn "insert into test (data) values ($1)")))
  (time (dotimes (i 10) (insert-it p)))
  (postgresql-close-prepared-statement! p))

(define (create-it)
  (let ((p (postgresql-prepared-statement
            conn "insert into test (data) values ($1)")))
    (insert-it p)
    (postgresql-close-prepared-statement! p)))
;; Creating prepared statement each time
(time (dotimes (i 10) (create-it)))

;; bye bye
(postgresql-terminate! conn)
I'm using (postgresql) library.  <ad>BTW, I think this is the only portable library that can access database. So you gotta check it out. </ad> It's simply inserting the same binary data (in this case the script file itself) 10 times. One is re-using prepared statement, the other one is creating it each time. The difference is the following:
$ sash bench.scm

;;  (dotimes (i 10) (insert-it p))
;;  0.760319 real    0.008487 user    3.34e-40 sys

;;  (dotimes (i 10) (create-it))
;;  1.597769 real    0.014841 user    5.76e-40 sys
More than double. It's just doing 10 iterations but this much difference. (Please ignore the fact that the library itself is already slow.) There are probably couple of reasons including PostgreSQL itself but from the library perspective, sending messages to the server would be slow. Wherever a DB server is, even localhost, communication between script and the server is done via socket. And calling postgresql-prepared-statement does at least 7 times of I/O (and 5 times for postgresql-close-prepared-statement!). So if I re-use it, then in total 120 times (12 x 10, of course) of I/O can be saved.

Now, my ORM library hides low level operations such as creating prepared statement, connection management, etc. (that's what ORM should do, isn't it?). So keeping prepared statement in users' script isn't an option. Especially, there's no guarantee that users woudl get the same connection each time they do some operation. So it's better to manage it on the framework.

Sagittarius has (cache lru) library, undocumented though, so first I thought I could use this. After modifying couple of lines and found out, no this isn't enough. The library only provides very simple cache mechanism. It even doesn't provide a way to get all objects inside the cache. It's okay if the object doesn't need any resource management, however prepared statements must be closed when it's no longer used. Plus, LRU may not be good enough for all situations so it might be better if users can specify which cache algorithm should be used.

There are variety of cache algorithms. Implementing all of them would take a bit time. So it's better to make a framework or interface of cache. The framework/interface should have the following properties:
  • Auto eviction and evict event handler
  • Limitation of storage size (unlimited as well)
  • A way to get all cached objects
  • Implementation independent interface
Now, make myself busy.

2016-02-15

Server performance 2

So (net server) itself wasn't too bad performance, then there must be other culprit. To find out it, I usually use profiler however it can only work on single thread environment. That means it's impossible to use it on the server program written on top of (net server) library.

Just giving up would be very easy way out but my consciousness doesn't allow me to do it (please let me go...). Thinking current HTTP server implementation uses 2 layers, Paella and Plato. The first one is the basic, then web framework. At least I can see which one would be slow. So I've just tried with bare Paella server. Copy&pasting the example and modify a bit like this:
(import (rnrs)
        (net server)
        (paella))

(define config (make-http-server-config :max-thread 10))

(define http-dispatcher
  (make-http-server-dispatcher
    (GET "/benchmark" (http-file-handler "index.html" "text/html"))))

(define server 
  (make-simple-server "8500" (http-server-handler http-dispatcher)
                      :config config))

(server-start! server)
Then uses the same script as before.The result is this:
$ time ./benchmark.sh
./benchmark.sh  4.66s user 3.76s system 335% cpu 2.507 total
Hmmm, bare server is already slow. So I can assume most of the time are consumed by the server, not the framework.

Listing up what's actually done by server would help:
  1. Converting socket to buffered port
  2. Parsing HTTP header
  3. Parsing request path.
  4. Parsing query string (if there is)
  5. Parsing mime (if there is)
  6. Parsing cookie (if there is)
  7. Calling handler
  8. Writing response
  9. Cleaning up
So I've started with the second item (port conversion actually improves performance so can't be removed, unless I write everything from scratch using socket but that sound too much pain in the ass). Conclusion first, I've improved header parsing almost 100% (mostly reducing memory allocation) but it didn't affect the performance of the server at all. Parsing header occurs once per request, so I've dumped headers what cURL sends and carefully diagnosed which procedure takes time. As the result, SRFI-13 related procedures consuming a lot of times because it has rich interface but requires packing rest arguments. So I've replaced them with no rest argument version. Then in the same library, the procedure rfc5322-header-ref which is for referring header value called string-ci=? which calls string-foldcase internally. So changed it to call case folding once. And couple of more improvements. All of them, ideed, improved performance however calling header parser only 1000 times took 30ms from the beginning. So make it 15ms doesn't make that much change.

Then I've started doubting that the benchmark script itself is actually slow. I'm not sure how fast cURL itself is but forking it 1000 times and wait for them didn't sound fast. So I've written the following script:
#!read-macro=sagittarius/bv-string
(import (rnrs)
        (sagittarius socket)
        (sagittarius control)
        (time)
        (util concurrent)
        (getopt))

(define header
  #*"GET /benchmark HTTP/1.1\r\n\
     User-Agent: curl/7.35.0\r\n\
     Host: localhost:8500\r\n\
     Accept: */*\r\n\r\n")

(define (poke)
  (define sock (make-client-socket "localhost" "8500"))
  (socket-send sock header)
  ;; just poking
  (socket-recv sock 256)
  (socket-close sock))

(define (main args)
  (with-args (cdr args)
      ((threads (#\t "threads") #t "10")
       (unit    (#\u "unit")    #t "1000"))
    (let* ((c (string->number unit))
           (t (string->number threads))
          (thread-pool (make-thread-pool t raise)))
      (time (thread-pool-wait-all!
             (dotimes (i (* c t) thread-pool)
               (thread-pool-push-task! thread-pool poke))))
      (thread-pool-release! thread-pool))))
Send fixed HTTP request and recieve the response (could be partially). -t option specifies how many threads should used and -u option specifies how many request should be done per thread. So if this ideed takes time, then my assumption is not correct. Lemme do it with bare HTTP server:
$ sash bench.scm -t 100 -u 100

;;  (thread-pool-wait-all! (dotimes (i (* c t) thread-pool) (thread-pool-push-task! thread-pool poke)))
;;  4.052414 real    0.670089 user    1.255910 sys
100 threads and 100 request per thread so in total 10000 request were send. Then it took 4 seconds, so 2500 req/s. It's faster than cURL version.

2500 req/s isn't fast but for my purpose it's good enough for now. So I'll put this aside for now.

2016-02-14

肉体改造部 第六週

今週(先週?)は風邪ひいてダウンしていた日が2日あったりした。月曜の夜にベッドの中で寒くて震えていたのはいい思い出である。普段は冬でも布団を蹴っ飛ばしてるのに・・・

計量結果:

  • 体重: 72.8kg (-0.3kg)
  • 体脂肪率: 23.6% (±0.0%)
  • 筋肉率:42.6% (±0.0%)
体重は減ったのにそれ以外が変わっていないというのはいったいどういうことなのだろう?骨か、骨が減ったのか?単純に誤差の範囲で見かけ上動いていないだけだとは思うけど、不安な結果ではある。

2016-02-12

Server performance

Sagittarius has server framework library (net server) and on top of this library I've written simple HTTP server and web framework, Paella. I don't use it in tight situation so performance isn't really matter for now. However if you write something you want to check how good or bad it is, don't you? And yes I've done simple benchmark and figured out it's horrible.

I've created a very simple static page with Plato which is a web application framework bundled to Paella. It just return a HTML file. (although it does have some overhead...) It looks like this:
(library (plato webapp benchmark)
    (export entry-point support-methods)
    (import (rnrs) (paella) (plato) (util file))

  (define (support-methods) '(GET))
  (define (entry-point req)
    (values 200 'file (build-path (plato-current-path (*plato-current-context*))
                                  "index.html")
            '("content-type" "text/html")))
)
The index.html has 200B data.
I don't have modern nice HTTP benchmark software like ApatchBench (because I'm lazy) so just used cURL and shell. The script looks like this:
#!/bin/sh

invoke () {
    curl http://localhost:8500/benchmark > /dev/null 2>&1
}

call () {
    for i in `seq 1 1000`;
    do
        invoke &
    done
}

call
wait
It's just create 1000 processes background and wait them.

The benchmark is done on default starting script which Plato generates. So number of threads are 10. Then this is the result:
$ time ./benchmark.sh
./benchmark.sh  4.89s user 3.77s system 313% cpu 2.764 total
So, I've done couple of times and average is approx 3 seconds per 1000 requests. So 300 Req/S. It's slow.

If I run the above benchmark with 10 requests, then the result was like this:
$ time ./benchmark.sh
./benchmark.sh  0.05s user 0.05s system 249% cpu 0.040 total
And 1 request is like this:
$ time ./benchmark.sh
./benchmark.sh  0.01s user 0.01s system 77% cpu 0.025 total
So up to thread number, I can assume it does better, at least it's not increased 10 times. But if it's 100, then it's about 7 times more.
$ time ./benchmark.sh
./benchmark.sh  0.49s user 0.35s system 285% cpu 0.293 total
1 to 10 is twice, but 10 to 100 is 7 times. Then 100 to 1000 is 10 times. Something isn't right to me.

Why it's so slow and gets slow when number of requests is increased? I think there are couple of reasons. The (net server) uses combination of select (2) and multithreading. When the server accepts the connection, then it tries to find least used thread. After that it pushes the socket to the found thread. The thread calls select if there's something to read. Then invokes user defined procedure. After the invocation, it checks if there's closed socket or not and waits input by select again. So flow is like this (n = number of thread, m = number of socket per thread):
  1. Find least used thread. O(nm) (best case O(1) if none of the threads are used)
  2. Push socket to the thread. O(1)
  3. Handling request. O(m)
  4. Cleaning up sockets. O(m)
I think dispatching socket smells slow. So I've made some changes like the followings:
  • Adding load balancing thread which simply manage priority queue
  • Just asking the queue which thread is least loaded
  • Code cleaning up
    • Using (util concurrent shared-queue) instead of manually managing sockets and locks
    • Don't assume write side shutdowned socket is not used.
    • more...
With these changes, the first step only takes O(1).  Now benchmark! This is the result:
$ time ./benchmark.sh
./benchmark.sh  4.61s user 3.76s system 317% cpu 2.633 total
YAHOOOOO!!!! 100ms faster!!! ... WHAAAATTTT!???

Well in average it's 2.6sec per 1000 request so it is a bit faster like 300ms - 400ms. And using (util concurrent) made the server itself more robust (it sometimes hanged before). I think the server framework itself is not too bad but HTTP server. So that'd be the next step.