Friday, May 23, 2014

[メモ] Perlのクロージャ生成速度は遅くない件

いくつかのスクリプト言語の処理系では、オブジェクトを生成して利用する場合と比較して、クロージャを生成する場合のオーバーヘッドが大きいという問題が知られています。最近、Perlでクロージャを使いたい場面に遭遇したので、ベンチマークをとってみることにしました。

結果、以下のように両者を使うアプローチで大きな速度差はないということがわかったのでメモ。

$ perl closure-vs-method.pl
         Rate  method closure
method  535/s      --    -12%
closure 609/s     14%      --
$ cat closure-vs-method.pl
use strict;
use warnings;
use Benchmark qw(cmpthese);

my $COUNT = 1000;

sub doit_closed {
    my $cb = shift;
    $cb->();
}

sub doit_method {
    my $cb = shift;
    $cb->meth();
}

cmpthese(-1, {
    'closure' => sub {
        for my $i (1..$COUNT) {
            doit_closed(sub {
                $i;
            });
        }
    },
    'method' => sub {
        for my $i (1..$COUNT) {
            doit_method(Holder->new($i));
        }
    }
});

package Holder;

sub new {
    my ($class, $value) = @_;
    return bless {
        value => $value
    }, $class;
}

sub meth {
    my $self = shift;
    return $self->{value};
}

Tuesday, May 20, 2014

[メモ] Apache+mod_sslでSIGBUSが発生した件

@hirose31さんと、Apache HTTPDからHTTPSでファイルダウンロード中にサーバプロセスがSIGBUSで死ぬって件にぶちあたり、

「OpenSSLの中でmemcpyがSIGBUSしてます」「な、なんだってー!」

って調べたのですが、理由は以下のとおりだった。

  • HTTPSの場合、デフォルト設定だとファイル読込にmmap(2)が使われる
    • mmapされたファイルのサイズが変更されてもApacheはそれを検知しようがない
    • そして、ファイル末尾以降のデータを読もうとするとセグメンテーションエラー(SIGBUS)が発生し、Apacheのサーバプロセスは異常終了する
  • HTTPの場合は、ローカルファイルシステムの場合sendfile(2)が使われるので、ファイルサイズが変更になってもApacheは異常終了しない
    • ただし、mod_deflateのような出力フィルタを使っている場合は、HTTPS同様に異常終了する可能性がある注1

この動作はApacheの「仕様」のようです注1日本語版だとNFS特有の現象のように読めるけど、英語版は以下のように書かれています。
Deleting or truncating a file while httpd has it memory-mapped can cause httpd to crash with a segmentation fault.
EnableMMAP Directive - core - Apache HTTP Server
言われてみれば当然だけど、mmapやsendfile等を自動的に使い分けているサーバの場合、これが原因だとは気づきにくいですよね。

というわけで、Apacheを使う場合はEnableMMAP Offにしておきましょう注1、さもないとアラートが上がってきたりしてウザいですよという話でした。本当は配信ファイルを全てアトミックに更新できばいいのですが、運用的にできないこともありますからね。

Apache以外のサーバでも同様の問題があるかどうかは知りません。

注1: HTTPSに限らない問題なので加筆修正 (17:06)。参照: Bug 46688 – Child segfault when mmaped file truncated

Friday, May 9, 2014

良いソフトウェアに求められる3点セットとJSXの開発手法の改善とgit-pushdirについて


テスト駆動開発(TDD)の一般化とGitHubの登場によって、機能追加の際にコードとテストを同時に実装する(そして、両者を一括してmasterにmergeする)という開発手法が一般化してきました。

しかし、「良いプログラム」の要素を構成するのは、コードとテストのみではありません。動作するコードと、その品質を担保するためのテストがあったとしても、適切なドキュメントがなければ、ユーザーはそのプログラムをどうやって使ったら良いかわかりません。

つまり、ユーザーに使いやすいプログラムを継続的に開発/提供しようと思うと、

  • コード
  • テスト
  • ドキュメント

の3点セットを提供する必要があるのです注1

今日のJSXが抱えている最大の課題は、ドキュメントが不足しているという点にあります。その原因は、「機能追加」の際にコードとテストのみを実装してmasterにmergeすることを繰り返す一方で、ドキュメントはプロジェクトページレポジトリとして別個に更新作業を行ってきた注2点にあり、その結果、コードとテストは存在するが対応するドキュメントがない機能がいろいろ実装されてきてしまった、と認識しています。

以上の認識に基づき、JSXのプロジェクトページにあった文書を全てJSX本体のレポジトリへ移動しました。今後は、

  • プルリクエストはコード/テスト/ドキュメントの3点セットで構成すること注4
  • そのために、すべてのドキュメントをレポジトリ本体の一部として管理すること

という形で運用していきたいと考えています。

また、この目標を実現するために、git-pushdirというスクリプトを書きました。このスクリプトは、あるディレクトリの中にあるファイルを丸ごと、指定したGitレポジトリにpushすることができます。JSXではこのスクリプトをMakefileから以下のように呼び出すことで、リリースの際に自動的にプロジェクトページのドキュメントを更新するようにしました。

publish: publish-test
        npm publish
        $(MAKE) doc-publish

doc-publish: doc
        (cd doc/jsx.github.com && ../../tool/git-pushdir -m "`git log --format='doc at commit %h' | head -1`" git@github.com:jsx/jsx.github.com.git)


似たようなことはgit subtreeを使っても可能注3ですが、subtreeの定義やpullした場合のconflictに絡む問題がない点がgit-pushdirの優位性になるかと思います。

つーことでよろしくお願いします>関係者諸氏。

注1: テストを書くコストが見合わない、あるいはドキュメントなど不要というケースもあるとは思います
注2: jsxdoc(javadocライクなツール)が出力するAPIリファレンスではなく、言語仕様に関わる文書のようなものについて述べています
注3: 参照: git subtreeで自動生成ドキュメントをGitHub Pagesに連携してみた - Life goes on
注4: コードとテストのレビュー完了 → ドキュメント加筆更新 → merge というフローでいいと思います