憂鬱な午後のひととき


11月30日_
2007 代数曲線暗号を巡ってに参加.
暗号からは暫く遠ざかっていたけどぼちぼち身の回りも落ち着いてきたし, 折角自由な環境にいることができるのだからどんどん勉強せねば. 楕円曲線を頂点, 同種写像を辺としてグラフを作り, その上でランダムウォークをして新しい暗号を考える話と匿名認証の話が面白かった. 後者は例えば私がある会社に所属しているという事実のみを, 私の名前を伏せたまま第三者に証明することができる. 効率よいプロトコルのために1/(x+c)Pの形のものがうまく使われていた.
11月29日_
・niftyのサーバの障害で11/15~11/28までのメールがSPAM扱いされている可能性が高いです. ざっと確認して7件ほど発掘しましたが, 見落としているかもしれません. もし返事無いという方がいらっしゃれば申し訳ありませんが再送お願いします.
・不動産屋さんに1カ月後引っ越すことになったからと電話したら, 書面でないと受け付けないとのこと. 2日ずれると8000円ほど料金が増えるわけで, せこいなあと思いながら送付. しかし全く返事が無い. 例の不動産なのでやっぱりなと思いつつ1週間後に電話したら, 「鍵を○日までに持って来い」と. それは当然だからいいのだけど「そういうのってそちらから連絡すべきことじゃないのか? 書類が届きましたの連絡ぐらいせーよ」と言ってものらりくらり. 物件はいいのに本当に駄目なところだ. この分だとちゃんと敷金返ってくるか心配だなあ.
11月28日_
bep.20071128.zip
Bep: 最小完全ハッシュ関数を用いた連想配列をいじったもの. オリジナルよりちょびっと速く(35%up@64bit Linux, 90%up@32bit Linux), ちょびっと小さい(1エントリーあたりに必要なメモリが27/8bitから19/8bitと1bitお得). あと, VC8にも対応. メモリを減らしたのは多少演算が増えても速度向上が見込めたから. というかいじれるところはそこぐらいしかない. 小さい時用のhashを考えるのはまたいずれ.
分かりやすい解説もあり, とても勉強になりました. どうもありがとうございます. levelAやlevelBの大きさは絶妙ですね. 感心します. SSE4のpopcntが使えればメモリ構造も含めてまた戦略を考えられるのだが, 現状では「これしかない」. 一つだけ言うなら, hppでusing namespace std;するのは行儀よくないかと.
勉強しながらだったので変数名とか全然違うものになってます. すいません. あ, ライセンスはBSDで
11月27日_
・USENのIP電話を申し込もうと思ったが, 相談窓口(フリーダイアル)の電話番号の横に小さく0120はつながりませんとある. 問い合わせたら, 窓口には携帯電話か固定電話でおかけくださいとのこと. なんだかしっくりこない.
追記:shinichiro_hさん
Oblivious Polynomial Evaluationです. 昔IPAの未踏で実装しようと思っていたけど, 当時まだ効率のよいプロトコルじゃなかったので未実装. 今はよいのがあるのかな.
プライバシーを守った検索はコンテンツ配信管理システムとその安全性確認に関する研究の3.1や3.2あたりかなあ. 以前「そういう研究をしている」というお話は伺ったことがあるけど詳細は知りません. 勉強してみたい分野である.
11月26日_
Checked Iterators
VCでは, 標準のReleaseモードでもstd::vectorの[]を使うと範囲チェックが行われる. つまり遅い. コンパイル時に/D_SECURE_SCL=0をつけることでそのチェックを省くことができる.
/* サンプル */
int sum(const std::vector<int>& v)
{
    int sum = 0;
    for (size_t i = 0, n = v.size(); i < n; i++) {
        sum += v[i];
    }
    return sum;
}
を/D_SECURE_SCL=1でコンパイル
.lp:
    test    eax, eax
    je    SHORT .exit
    mov    ecx, DWORD PTR [edi+8]
    sub    ecx, eax
    sar    ecx, 2
    cmp    esi, ecx ; 範囲チェック
    jb    .ok
.ng:
    call    __invalid_parameter_noinfo
.ok:
    mov    eax, DWORD PTR [edi+4]
    add    ebx, DWORD PTR [eax+esi*4]
    add    esi, 1
    cmp    esi, ebp
    jb    .lp
/D_SECURE_SCL=0でコンパイル
.lp:
    add    eax, DWORD PTR [ecx]
    add    ecx, 4
    cmp    ecx, edx
    jne    .lp
ちなみにloopじゃなくてaccumelateを使うとiteratorのチェックは入力前に一度だけになるので/D_SECURE_SCL=1でもループ自体は遅くならない.
int sum(const std::vector<int>& v)
{
    return std::accumulate(v.begin(), v.end(), 0);
}
11月25日_
PEG意見交換会に参加.
動機は凄い人たちが来るという噂を聞いてミーハー気分で. でも最低限の定義や問題点などは読んでおいたのでなかなか楽しめました. 終わってからの, 「デスクトップアプリは無くなってブラウザだけの世界が来るのか?」という議論も面白かった.
私は「無料だからってなんでもかんでもサーバにデータを置くのは不安だ」という気持ち.
11月22日_
↓PGOの効果ってどんなかなーとちょっとテスト.
#include <stdio.h>

int func(int i)
{
    switch (i) {
    case '0':  case '1':  case '2':  case '3':  case '4':
    case '5':  case '6':  case '7':  case '8':  case '9':
    case 'A':  case 'B':  case 'C':  case 'D':  case 'E': case 'F':
        return 1;
    default:
        return 0;
    }
}

int main(int argc, char *argv[])
{
    int sum = 0;
    for (int i = 0; i < 10000; i++) {
        sum += func(i & argc);
    }
    printf("sum=%d\n", sum);
    return 0;
};
これにたいして通常モードでのコンパイル結果は
.lp:
    mov         ecx,eax
    and         ecx,esi
    add         ecx,0FFFFFFD0h
    cmp         ecx,16h
    ja          .default
    movzx       ecx,byte ptr tbl1[ecx]
    jmp         dword ptr tbl2[ecx*4] ; match/default
.match:
    mov         ecx,1
    jmp         .next
.default:
    xor         ecx,ecx
.next:
    add         eax,1
    add         edx,ecx
    cmp         eax,2710h
    jl          .lp:
とテーブル参照でジャンプ. これをPGOモードでコンパイル&実行(オプション無し)すると, argc == 0が多い(これしかない)という情報をコンパイラに与えることになる. で, 結果は
.lp:
    mov         eax,ecx
    and         eax,esi
    cmp         eax,30h
    jge         <上記相当の分岐処理>
    xor         eax,eax
    add         ecx,1
    add         edx,eax
    cmp         ecx,2710h
    jl          .lp
...
<分岐処理>
となった. つまりfunc()に渡される値が0の時のみ特に高速に動作するようになった. 分岐しないのでパイプラインも乱れない. 凄いなあ.
11月21日_
switchの展開
VCで試してみる.
_main    PROC NEAR
    mov    eax, DWORD PTR _i$[esp-4] ; eax = i
    add    eax, -48
    cmp    eax, 22
    ja    SHORT .Lexit0 ; 範囲外なので0を返す
    xor    ecx, ecx
    mov    cl, byte ptr tbl0[eax]
    jmp    dword ptr tbl1[ecx*4]
.Lexit1:
    mov    eax, 1
    ret    0
.Lexit0:
    xor    eax, eax
    ret    0
tbl1:
    DD    .Lexit1
    DD    .Lexit0
tbl0:
DB    0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 1, 1, 1, 1, 1, 1,
    1, 0, 0, 0, 0, 0, 0,
まあ普通だわな. 二個目の方でも
    mov    eax, DWORD PTR _i$[esp-4]
    cmp    eax, 50                    ; 00000032H
    ja    SHORT L5
    xor    ecx, ecx
    mov    cl, BYTE PTR $L280[eax]
    jmp    DWORD PTR $L281[ecx*4]
L0:
    mov    eax, 1
    ret    0
L1:
    mov    eax, 9
    ret    0
L2:
    mov    eax, 6546
    ret    0
L3:
    mov    eax, 4534
    ret    0
L4:
    mov    eax, 4324
    ret    0
L5:
    xor    eax, eax
    ret    0
    npad    3
$L281:
    DD    L0, L1, L2, L3, L4, L5
$L280:
    DB    0, 1, 2, 3, 5, 5, 5, 5, ..., 4
と素直. gcc 4.2.2だとL?の部分が
.L3:
    popl    %ecx
    movl    $1, %eax
    popl    %ebp
    leal    -4(%ecx), %esp
    ret
.L4:
    popl    %ecx
    movl    $9, %eax
    popl    %ebp
    leal    -4(%ecx), %esp
    ret
.L5:
    popl    %ecx
    movl    $6546, %eax
    popl    %ebp
    leal    -4(%ecx), %esp
    ret
    ...
ってなっててややもっさり感が(これはmainの特殊事情?). ところでVCだと成立しないことが分かってるdefaultに__assume(0);を書くと, 範囲外チェックを省く最適化が行われるけどgccだと何だったけ. あと実際にプログラムを実行した結果を解析して最適化するPGOなら頻度の高いcaseを先にチェックしそうだな(未確認).
11月20日_
Bep: 最小完全ハッシュ関数を用いた連想配列のソースを勉強&ちょっと最適化(for 32bit gcc4.2.2).
<original>
n:1025325 bn:1332921
read 1025325 keys
lookup_wo 0.633916 key:0.618259 usec
lookup    1.028642 key:1.003235 usec

<optimized>
n:1025325 bn:1332921
read 1025325 keys
lookup_wo 0.294019 key:0.286757 usec
lookup    0.760556 key:0.741771 usec
lookup_woで2倍ちょい.
11月19日_
そういやメールアドレスも変わったのであった. 前のメールアドレスを知ってる方はezwebの部分をsoftbankに変更してください.
11月18日_
携帯電話を変更した. SANYOでは日付や時刻を入力したいとき, 漢字モードのまま数字(たとえば"ああかな")を入力してから右上のボタンを押すと"11時25分"に変換できたのにSHARPのでは出来ない. 一番よく使ってた機能なのでとても不便だ. あと, メニューに数字が表示されてないので数字キーでぱっとジャンプ出来ない(ジャンプ機能自体はあるようだが). 失敗したかな.
11月15日_
今ログインしているユーザが管理者権限を持っているかを判別するのにNetUserGetLocalGroupsを使ってみたけど, これじゃあ"Administrator"を他の名前に変えている場合は判定できないなあと思ったところでそものもずばりのIsUserAnAdminを発見.
ところで以前はAdministratorアカウントの名称を変更するなどの記事を見て私もそうしていたのだが, 緊急時に回復コンソールでログイン出来なくて焦って以来止めている.
KB258585のせいだったのかなあ.
11月13日_
今余裕が無いのでXbyakのx64対応は出来たとしても年末か年明けかなあ. まず環境を用意しなきゃ. あとコードも整理しないと.
11月12日_
100円/kgという和歌山のみかんを送ってもらった. 送料の方が高くなってしまうがとても濃厚でおいしい. 昔はよく食べてたが, そういや東京来てあまり食べなくなってたのはやっぱりおいしくないからかなあ.
11月11日_
最近, 新聞の折り込み広告にある家の値段が意味不明である. 今日も3LDK+Sで85,100万円を筆頭に50,600万円やら39,800万円やら29,800万円やらぞろぞろ. そんな中にぽつりと7,980万円があったりするとなんだかほっとするが, 冷静に考えれば非現実な数値. ニッキュッパと言えば2,980円だろう. 一体どういう世界だ.
11月7日_
std::string strのCの生文字列を参照したい時に, str.c_str()ではなくついつい&str[0]を使ってしまって痛い目に遭う. この違いって当たり前なんだろうか.
... vectorじゃないから当たり前?
11月3日_
XbyakをSSSE3/SSE4に対応させました. 団子厨(Dango-Chu)さん, 参考にさせて頂きました. ありがとうございます.
同じpextrw r32, xmmに対してMMXとSSE4とで異なるバイト命令を割り当てるという珍現象. intel何してる.
11月2日_
・isspaceのベンチを取ってみた.
isspaceの速度(単位:秒, 小さいほど速い)
std::isspace(C++)isspace(C)isspace1isspace2isspace3
Core2 2.6GHz + VC2005(Xp)5.225.224.661.911.78
Core2 1.6GHz + gcc 4.2.2(32bit Linux)23.694.004.444.93.9
Opteron 240 1.4GHz gcc 3.4.6(64bit Linux)13.0713.077.127.125.79
bool isspace1(char c)
{
    const int begin = 0x09;
    const int end = 0x0d;
    if (c == ' ' || (begin <= c && c <= end)) {
        return true;
    }
    return false;
}

bool isspace2(char c)
{
    const int begin = 0x09;
    const int end = 0x0d;
    if (c == ' ' || (unsigned)(c - begin) <= end - begin) {
        return true;
    }
    return false;
}

char isspace3(char c)
{
    static const char tbl[256] = {
        0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        1
    };
    return tbl[(unsigned char)c];
}
というわけで ・Shibuya.js
えらい言語仕様が増えるのね. 演算子オーバーロードよりは64bit integer欲しい.
Xbyak + SSE4
あー, じゃあ対応いたします. SSSE3まではテスト通った. SSE4はまだ出てないしなあ. FPUにも対応してくれとの意見も.
11月1日_
Haskellで速いwcを書いてみよう
このC/C++に比べてHaskellが速いと言ってもなあという気がする. 単に1行加えてバッファリングするだけでもtanakhさんの4~5倍は速くなるんだけど(以下すべてgcc 4.2).
#include <stdio.h>
#include <ctype.h>

int main()
{
    int nl = 0, nw = 0, nc = 0, c, prev = 1;
    for (;;) {
        char buf[8192];
        int i;
        int readSize = fread(buf, 1, sizeof(buf), stdin);
        for (i = 0; i < readSize; i++) {
            int c = buf[i];
            if (c == '\n') nl++;
            int cur = isspace(c);
            if (prev && !cur) nw++;
            prev = cur;
        }
        if (readSize < sizeof(buf)) break;
        nc += readSize;
    }
    printf("%d %d %d\n", nl, nw, nc);
}
実行結果
time tanakhさんのC <data
>3.090u 0.090s 0:03.18 100.0%    0+0k 0+0io 167pf+0w
time 上記C <data
>0.560u 0.060s 0:00.61 101.6%    0+0k 0+0io 166pf+0w
time wc < data
3209697 10749964 90689245
>1.010u 0.070s 0:01.07 100.9%    0+0k 0+0io 98pf+0w
この時点で既にwcの1.7倍ぐらい速いですね. C++なら
    size_t readSize = fread(buf, 1, sizeof(buf), stdin);
の代わりに
    size_t readSize = std::cin.read(buf, sizeof(buf)).gcount();
を使ってもいい(若干遅いけど). ただ気をつけないといけないのはC++のisspace()はCに比べてめっちゃ遅い. なまじ同名なだけにたち悪い. 試しにC版のコードに#include <iostream>を加えるとそちらが使われるんだがその場合は
>1.680u 0.060s 0:01.74 100.0%    0+0k 0+0io 205pf+0w
と3倍近く遅くなる. これはlocaleを見るあたりが複雑なためかな(詳しくは未調査). 一般にC++でis***関数は気をつけないと危ないっぽい. asciiのみ前提なら
static inline bool myIsSpace1(char c)
{
    if (c == ' ') return true;
    if (0x09 <= c && c <= 0x0d) return true;
    return false;
}

static inline char myIsSpace2(char c)
{
    static const char tbl[256] = {
        0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        1
    };
    return tbl[(unsigned char)c];
}
なんかを作った方がよさそうだ(上記URLのHaskellでもそんなことしてるし不公平ではないだろう).

御意見は光成滋生<herumi@nifty.com>までお願いします