sqlite は想像していたよりパフォーマンスが良かった件

なんだかよくわからない先入観で遅いだろうと決め付けていたが、 http://www.sqlite.org/cvstrac/wiki?p=SpeedComparison によると、 transaction でまとめて実行するとずいぶん早い。

Test 1: 1000 INSERTs

    CREATE TABLE t1(a INTEGER, b INTEGER, c VARCHAR(100));
    INSERT INTO t1 VALUES(1,13153,'thirteen thousand one hundred fifty three');
    INSERT INTO t1 VALUES(2,75560,'seventy five thousand five hundred sixty');
    ... 995 lines omitted
    INSERT INTO t1 VALUES(998,66289,'sixty six thousand two hundred eighty nine');
    INSERT INTO t1 VALUES(999,24322,'twenty four thousand three hundred twenty two');
    INSERT INTO t1 VALUES(1000,94142,'ninety four thousand one hundred forty two');

SQLite 3.3.3 (sync):	   3.823
SQLite 3.3.3 (nosync):	   1.668
SQLite 2.8.17 (sync):	   4.245
SQLite 2.8.17 (nosync):	   1.743
PostgreSQL 8.1.2:	   4.922
MySQL 5.0.18 (sync):	   2.647
MySQL 5.0.18 (nosync):	   0.329
FirebirdSQL 1.5.2:	   0.320

Test 2: 25000 INSERTs in a transaction

    BEGIN;
    CREATE TABLE t2(a INTEGER, b INTEGER, c VARCHAR(100));
    INSERT INTO t2 VALUES(1,298361,'two hundred ninety eight thousand three hundred sixty one');
    ... 24997 lines omitted
    INSERT INTO t2 VALUES(24999,447847,'four hundred forty seven thousand eight hundred forty seven');
    INSERT INTO t2 VALUES(25000,473330,'four hundred seventy three thousand three hundred thirty');
    COMMIT;

SQLite 3.3.3 (sync):	   0.764
SQLite 3.3.3 (nosync):	   0.748
SQLite 2.8.17 (sync):	   0.698
SQLite 2.8.17 (nosync):	   0.663
PostgreSQL 8.1.2:	   16.454
MySQL 5.0.18 (sync):	   7.833
MySQL 5.0.18 (nosync):	   7.038
FirebirdSQL 1.5.2:	   4.280

http://www.sqlite.org/cvstrac/wiki?p=SpeedComparison

transaction ごとに fsync() を実行するようなので、何度も fsync() を呼ばすに一度で済ませられるからだろうか。

Test 1 では IO 待ちが完全にネックになっている模様。

実際に試してみた結果

$ time ./a.out 

real	0m10.812s
user	0m0.068s
sys	0m2.164s
$ time ./a.out transaction
COMMIT
real	0m0.169s
user	0m0.044s
sys	0m0.032s

確かに早くなってる

ソース

コンパイルは gcc -lsqlite3 sample.c

#include <stdio.h>
#include <sqlite3.h>

#define COUNT 1000

int main(int argc, char *argv[]) {
    sqlite3 *db;
    char *errmsg;
    int rc, i;

    rc = sqlite3_open("dat.sqlite3", &db);
    if (rc) {
        fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
        sqlite3_close(db);
        exit(1);
    }

    rc = sqlite3_exec(db, "create table if not exists sample(id)", NULL, 0, &errmsg);

    if (argv[1] != NULL)
        sqlite3_exec(db, "BEGIN", NULL, NULL, &errmsg);

    for (i = 0; i < COUNT; i++) {
        char statement[100];

        sprintf(statement, "insert into sample values(%d)", i);

        rc = sqlite3_exec(db, statement, NULL, 0, &errmsg);
    }

    if (argv[1] != NULL) {
        sqlite3_exec(db, "COMMIT", NULL, NULL, &errmsg);
        printf("COMMIT");
    }

    rc = sqlite3_exec(db, "drop table sample", NULL, 0, &errmsg);

    sqlite3_close(db);
}

エラー処理はほとんどしてない。

突っ込み待ってます><


AnyEvent + OAuth で Twitter API を叩く

最近ようやく CPAN Author になりました。ふと http://d.hatena.ne.jp/sun-basix/20100620/1277054186 が目にとまったので、モジュールの紹介もかねて書いておきます。

AnyEvent::Twitter の _make_oauth_request() という undocumented なメソッドを使えば ChirpUserStream も OAuth 対応させることができ、 config.json は eg/gen_token.plで生成できます。 want_body_handle やら on_header やらオプションが必要なので、根本的にはリンク先とは変わらない感じ。

こんなかんじ

#!/usr/bin/perl

use strict;
use warnings;
use utf8;
use Encode;
use Data::Dumper;
use AnyEvent;
use AnyEvent::HTTP;
use AnyEvent::Twitter;

use JSON;
use Perl6::Slurp;

my $json_text = slurp 'config.json';
my $config = decode_json($json_text);
my $ua = AnyEvent::Twitter->new(%$config);

my $req = $ua->_make_oauth_request(
    request_url    => 'http://chirpstream.twitter.com/2b/user.json',
    request_method => 'GET',
    extra_params   => {}
);

my $cv = AE::cv;

http_request('GET' => $req->to_url,
    want_body_handle => 1,
    on_header => sub {
        my $hdr = shift;
        warn "$hdr->{Status}: $hdr->{Reason}";
    },
    sub {
        my $hdl = shift;

        my $r = sub {
            my (undef, $json) = @_;
            if (my $text = $json->{text}) {
                print encode_utf8 "$json->{user}{screen_name}: $text\n";
            } else {
                print encode_utf8 Dumper($json);
            }
        };
        $hdl->on_read(sub { $hdl->push_read( json => $r ); });
    }
);

$cv->recv;

exit;

__END__

JavaScript bookmarklets

開いているページをはてブする

javascript:window.open("http://b.hatena.ne.jp/bookmarklet?url="+encodeURIComponent(window.location),document.title);window.opener.history.back();

開いているページを twitter に流す

javascript:window.open("http://twitter.com/home/?status=%20/%20"+encodeURIComponent(document.title+("%20-%20"+window.location),document.title+Math.random()));window.opener.history.back();

Readability というブックマークレットが便利すぎる件

たぶんご存知の方もいらっしゃるとは思いますが、 http://lab.arc90.com/experiments/readability/ というブックマークレットが思ったより便利なので紹介します。

英語のページを見ていると

文字が小さすぎて大変見にくいことが多々ある。これはBBCのページ。

Readabilityを使うと

こんな感じに見やすくなる

Readabilityの設定

Style, Size, Marginの設定を確認しながらブックマークレットを作れるので大変よい


AnyEvent::Twitter について(続)

結論から書くと

elmex 氏に AnyEvent::Twitter の maintainer に設定していただいたので、http://github.com/punytan/AnyEvent-Twitter に変更になります。

モジュールの API が大幅に変更になります。6月12-13日を目処に変更する予定です。

蛇足

elmex 氏にメールを送ったところ、AnyEvent::Twitter のメンテナンスの予定は無いとの返答でした。

API の大幅な変更に関しては、現状の AnyEvent::Twitter は Basic 認証の廃止でとにかく使えなくなってしまうので、まあそれほど悪い選択肢じゃないよ、と助言いただき、また、廃止は今回限りのことで、長期で見た場合に AnyEvent::Twitter の namespace を空けておくよりも良いだろうということで、このような結論になりました。


AnyEvent::Twitter について

いくつか Twitter のボットを作っているので Twitter の Basic 認証が6月に廃止される( http://apiwiki.twitter.com/ )ことが気になりつつも、一応いまのところ id:sugyan の EnableOAuth があるのでその場しのぎにそれを使う分には問題ないのだけれども、patch-like なモジュールは完全な解決ではないので、 AnyEvent::Twitter 作者の elmex 氏に OAuth 対応の予定あるか聞いてみようと思って IRC で聞いてみたけど応答が無いのでメールも送ってみてそれの返答待ち。対応する予定が無い場合 http://github.com/punytan/AnyEvent-Twitter-OAuth を上げる旨も書き添えておきました。

mlehmann 氏に http://github.com/punytan/AnyEvent-Twitter-OAuth を発見されて、一応動く旨を伝えたところ、彼の経験上、elmex 氏は unpolished patch よりも well-written hack code を評価するだろうという意見を(罵倒されること無く無事に)得られました。

Twitter API を使うという同じ目的のために CPAN の namespace は汚したくないし、もし OAuth 対応してくれるなら AnyEvent::Twitter を使い続けたいと思っていますがここは返信待ち状態です。

こういうときはどうしたらいいのか、perl hacker な方で詳しい方はご意見ください。ちなみに PAUSE ID は持っていません。時間のある方はついでに AnyEvent::Twitter::OAuth も試してみてください。


App::AutoInstaller を書いた

とりあえず動いているみたいなので報告。人柱 & bug fix 歓迎しております。

レポジトリ

http://github.com/punytan/App-AutoInstaller

これは何?

perlbrew を使っているとモジュールを各バージョンにインストールする必要があって面倒なのでそれを楽にするもの。perlbrew + cpanm + cpan-outdated な感じです。

動作としては、

  • スクリプトで使われているモジュールを抽出してcpanmに渡す
  • cpan-outdatedをかける

を自動でやってくれる

使い方

$ wget http://tinyurl.com/cpan-auto http://tinyurl.com/auto-script-pl
$ chmod +x ./cpan-auto
$ cat script.pl        ### auto-script-pl is script.pl
$ ./cpan-auto script.pl

各自で必要なモジュール類を use したスクリプトを準備して perlbrew switch して cpan-auto に渡すと便利。

参考

http://gugod.org/2010/05/install-all-used-modules-at-once.html


Git Cheat Sheet - 1

入門Git 4章までの要約。

installation

apt-get install git-core

configuration

git config --global user.name "example"
git config --global user.email "example@example.com"
git config --global color.ui auto

revision

プロジェクトの状態
以前の状態に戻すために必要な情報
revision 間の変更に関するそのほかの情報
変更履歴を調べるために必要な情報

glossary

object
SHA-1 hash をファイル内容の名前とする
blob object
1つのファイル内容は、1つの blob object として記録される
tree object
ディレクトリの状態を記録したもの
object database
project の状態を記録する object を格納した database
HEAD
現在最新の commit。現在最新の commit の object 名の代わりになる。ちなみに、新しく作るコミットは現在のHEADの子供。
index
commit が記録する tree 状態と、 working tree 上にあるファイルの内容との間に位置する。
to stage
index に記録すること
.gitignore(ファイル)
版管理しないファイルを指定

best practice

  • 論理的に関連の無い複数の変更は、別々の commit として扱う
    • revert のときに便利になるよ!

3-way merge の条件

  • merge を行うことで、branch での変更がプロジェクトの目標に近づく
  • branch で変更した人たちの判断を信頼していること

git init

git var GIT_COMMITTER_IDENT
git var GIT_AUTHOR_IDENT

で確認を忘れずに。

git add

git add .
working tree の「現在の」ファイルの内容を index に記録する
git add -u
working tree 内で管理しているすべてのファイルの現在の状態を index に記録する
git add -p
patch 形式の出力を見ながら、どの変更をインデックスに含め、あるいは含めないのかを選択できる。1つのファイルに対して行った変更のうち一部だけを行った状態を index に記録する。dit diff 出力の最初の hunk が出力された後、Stage this hunk? と問われる。
git add -A
.gitignore で無視していないファイルすべての情報を index に登録する。version 1.6.0以前では、
git add . && git add -u

git diff

git diff
working tree と index の差分を表示する
git diff HEAD
まだ commit していないすべての変更点を確認する
git diff < object >
working tree と引数として指定された commit object が記録する差分を出力
git diff --cached
最新の commit と index に記録した状態間の変更

git commit

git commit
標準的な log message の形式
  • commit で変更する内容を1行で要約
  • 1行の空行
  • 変更した理由の説明
git commit -m "message"
editor の起動を省略することができる
git commit -a
= git add -u; git commit
git commit -v
git diff --cached の出力を見ながら log message を書ける。 git add -p で stage した後に使う。"Changed but not updated" を確認。
git commit < paths >
すでにいくつか stage した後でも、それらを超えて指定ファイルの状態だけを記録できる
git commit --amend
commit log message を修正するときに使う

git status

git status
変更 summary をgit commit を実行せずに preview できる

git log

git log (引数なし = HEAD)
commit object に記録された parent をたどって、すべての commit を順に出力
git log -p
各 commit の変更を patch 形式で出力
git log -p< 数字 >
数を指定することで、出力される commit 数を制限できる
git log --pretty=short
log message の最初の段落のみを出力
git log -2 -p --pretty=short
git log < paths >
gti log --pretty=short index.html
git log --grep=< pattern >
log message に書いた文字列で検索する。default は OR 条件。
git log --pretty=short --all-match --grep='string1' --grep='string2'
log message を AND 条件として扱う
git log --author='author'
default は OR 条件
git log --committer='committer'
default は OR 条件

git show

git show HEAD(or commit object)
指定した commit の内容を確認する

git reset

git reset
index の内容を最新の commit と同じ状態にする。今までgit add してきたすべてのファイルを引数に与えたことと同じ
git reset < paths >
指定したファイルを次回の commit から除外する

git blame

git blame < file >
ファイルの各行がどの commit で記録したのかを出力する

git revert

git revert < object >
取り消したい変更をした commit を取り消す。log message には取り消した理由を記述する

git checkout

git checkout < paths >
working tree の変更を取り消す(変更以前の状態に戻す)
git checkout HEAD < paths >
git add を使った後からでも HEAD に記録された内容に戻せる

git reset

git reset HEAD^
working tree のファイルは変化しないが、HEAD^ の状態まで index と commit を戻せる
git reset --hard HEAD^
working tree のファイルを含め、HEAD^ の状態まで戻せる

git rebase

git rebase -i
interactive (対話的) rebaseのこと。

e.g.)最新から2つめの commit で記録した変更を訂正する場合

git rebase -i HEAD~3

rebase への指示を編集する(変更したい commit 行の pick を edit に変更する)

git show
# なんらかの修正
git commit --amend
git rebase --continue

Perl で MeCab を使う on Debian Lenny

インストール

# vim /etc/apt/sources.list
 -> non-freeを追記
# apt-get update
# apt-get install mecab libmecab-dev mecab-ipadic-utf8
# vim /etc/apt/sources.list
 -> non-freeを削除
# apt-get update
# cpanm Text::MeCab

正しくインストールされたか確認

ソース
use strict;
use utf8;
use Text::MeCab;

my $text = 'すもももももももものうち';
my @fields = qw(id surface feature length);

sub text_mecab {
    my $mecab = Text::MeCab->new();
    for(my $node = $mecab->parse($text);
        $node;
        $node = $node->next
    ) {
        for my $field (@fields) {
            print $node->$field(), "\n";
        }
    }
}

text_mecab();
出力
9
すもも
名詞,一般,*,*,*,*,すもも,スモモ,スモモ
9
18
も
助詞,係助詞,*,*,*,*,も,モ,モ
3
24
もも
名詞,一般,*,*,*,*,もも,モモ,モモ
6
30
も
助詞,係助詞,*,*,*,*,も,モ,モ
3
36
もも
名詞,一般,*,*,*,*,もも,モモ,モモ
6
45
の
助詞,連体化,*,*,*,*,の,ノ,ノ
3
56
うち
名詞,非自立,副詞可能,*,*,*,うち,ウチ,ウチ
6
62

BOS/EOS,*,*,*,*,*,*,*,*
0

あとはコードを書くだけ。


AnyEvent::Twitter::Chirp を書いた

ChirpUserStreamsを簡単に使えるようにしてみました

repository

http://github.com/punytan/AnyEvent-Twitter-Chirp

ChirpUserStreams?

http://d.hatena.ne.jp/sugyan/20100423/1271981903 これを簡単に使えるようにしたモジュールです

使い方

イベントごとにcoderefを登録していく感じ。

#!/usr/bin/perl

use strict;
use warnings;
use utf8;
use Encode;
use Data::Dumper;

use AnyEvent;
use AnyEvent::Twitter::Chirp;

my $user = '';
my $password = '';


my $cv = AE::cv;

my $chirp = AnyEvent::Twitter::Chirp->new(
    username => $user,
    password => $password,
    on_tweet => sub {
        my $tweet = shift;
        print encode_utf8($tweet->{text} . "\n");
    },
    on_friends => sub {
        my @friends = @_;
        print 'friends: ', join(',', @friends), "\n";
    },
    on_follow => sub {
        my $follow = shift;
        print Dumper ['follow', $follow];
    },
    on_retweet => sub {
        my $retweet = shift;
        print Dumper ['retweet', $retweet];
    },
    on_favorite => sub {
        my $favorite = shift;
        print Dumper ['fav', $favorite];
    },
    on_unfavorite => sub {
        my $unfavorite = shift;
        print Dumper ['unfav', $unfavorite];
    },
    on_delete => sub {
        my $delete = shift;
        print Dumper ['delete', $delete];
    },
    on_unknown => sub {
        my $unknown = shift;
        print Dumper ['on_unknonw', $unknown];
    },
#    timeout => 45,
);

$cv->recv;

__END__

ファイルは eg/sample.pl にあります。

http://github.com/punytan/AnyEvent-Twitter-Chirp/blob/master/eg/sample.pl

このモジュールの今後

miyagawaさんが AE::Twitter::Stream のほうに user stream を追加される予定らしいです。それまで待てない!という方向けです。CPANには登録せず、githubどまりにしたいと思います。


« 9 »