憂鬱な午後のひととき


4月28日_

PHP

しばらくPHPのお勉強. 入門のページを見てhello worldなどをしつつ. シェルとCが混ざったような感じの言語なんだな.

4月27日_

肩がえらく凝ってしんどい.

最近ずっと気を付けていたのだけどな. 在宅勤務にしようと思ったら, 週末会社のビルの電気設備点検で停電のため自分のマシンの電源を落としていたのだった.

有給にしてTeXのページ設定をいろいろ調べる. 夕方, 会社の人にマシンの電源を入れてもらえばよかったと思いつく.

4月26日_

『正規表現技術入門』(新屋良磨, 鈴木勇介, 高田謙)

まるまる正規表現とオートマトンの本. 新屋さんにいただいた. ありがとうございます. 8章までざっと読んだ(Appendixはこれから). 最初は簡単なルールの紹介から, 拡張機能の説明と各種処理系での違いの列挙. NFAベースのものをVM型と呼んでいるのが一瞬戸惑ったけど理由が書かれている. 正規表現の歴史はよく調べたなあと感心する.

4章ではオートマトンやNFAからDFAを構成する方法の解説. それだけで独立して動作確認できる簡潔なPythonのコードなど参考になると思う. DFAの状態数爆発問題に対処するためのOn-the-Fly構成法などは, サイボウズ・ラボユースで新屋さんがJITコンパイル中に対応するためがんばってたなあと思い出す.

鬼雲の実装紹介は実際に作ると細かいところでいろいろ大変だというのが伝わってくる.

第7章の「正規表現の落とし穴」は正規表現をよく使う人は目を通したらよいと思う. ちょうど会社でもいくつか正規表現で想定していないパフォーマンス劣化問題があったところ.

4月25日_

『私たちは今でも進化しているのか?』(マーリーン・ズック 渡会圭子訳)

進化というのはとても長い期間をかけてゆっくり進化するものというイメージだったけど, それは違うという. 著者は, 鳴いていると寄生ハエにやられてしまうので5年で鳴かない体に変化した例を発見した. その辺りが一番面白い. 他にもいろいろ急激に進化している例を紹介している.

アメリカで流行ってるらしいパレオ主義(原始人の生活に戻ろう)を間違っていると批判する. それがこの本の主張の一つなのでしかたないけど, パレオ主義自体を知らないのでくどく感じる.

4月24日_

関数呼び出しのコスト

最近は関数呼び出しのコストは低いよね?という問いがあったので実測してみた[rel-jmp.cpp]. 擬似的なコードはこんな感じ.

calc:
  mul eax
  add eax, 12345
  ret

funcAddr:
  dq calc

.lp:
  switch (mode) {
  case 0:
    mul eax
    add eax, 12345
    break;
  case 1:
    call(calc); break;
  case 2:
    call([rip + funcAddr]); break;
  case 3:
    call(r8); break; // r8 = calc
  case 4:
    call([r8]); break; // r8 = funcAddr
  }
  sub(ecx, 1);
  jnz(.lp);

case 0はループの中で直接計算する. case 1はcalc関数を呼び出す. case 2はrip相対で関数アドレスを読んでcall. case 3はレジスタにcalcのアドレスを入れてcall. case 4はレジスタのアドレスにあるcalcアドレスを読んでcall.

inline vs. call on Haswell
inline call rel32 call [rip] call reg call [reg]
clk 5.35 5.15 5.52 5.03 5.02

Haswellや第4世代ではどれも殆ど差は無いのだけど0.3clk程度inlineよりcall regの方が速かった. ループ先頭のalignmentはしていない. してもあまり結果に差は無かった. callしたほうが速いと同じ現象が起きてるのかな.

もともと本来の趣旨はジャンプ先が常に同じならそれにかかる処理コストは隠蔽されるだろうという予想で, それは概ね当たっていたのだが, なかなか予想外の結果だった.

4月23日_

『不思議宇宙のトムキンス』(ジョージ・ガモフ ラッセル・スタナード 青木薫訳)

自転車に乗ってる人が横につぶれるシーン. 昔からあるのは知ってはいたがオリジナルのは初めて読んだ.

この本は記述が古くなった部分をスタナードさんが書き直したもの. 教授の娘モードの記述が気に入らなくてかなり変わったらしいが, 筋にそれほど絡んでるわけでもないので微妙. 中盤以降は話が行ったり来たり, 飛んだりでいまいちだった.

ニュートリノの説明する部分はまあまあよかったが, いきなりSU(3)を説明しだしたりちぐはぐ感もある.

4月22日_

Effective Modern C++勉強会#4

unique_ptr, shared_ptr, weak_ptr周りの話. 次回は5/20.

4月20日_

TeXの索引のスタイルファイル

makeidx.sty: LaTeXパッケージにあるように, そこで取得できるdot.istを使って

mendex -c -g -S -s dot.ist ango.idx

のようにすると項目とページの間に点線を入れてくれる.

4月18日_

筋肉痛

久しぶりのサイクリングと山登りで筋肉痛になるかと思ったが, 意外と大丈夫だった. まああちこちミシミシいってる気はするけどそれほどでもない.

4月16日_

山登り

会社で合同お食事会というのがあって, 普段本社の方とそれほど関わり合いがあるわけではないのでよい機会と参加してみる. 行き先は名前は知ってるけど行ったことのない軽井沢. そこで一泊.

初めての方ともいろいろおしゃべりできてよかった. 自由時間に「今から小浅間山に登りますが, どうですか」と言われてせっかくなのでご一緒させてもらう. 東京にきてからは殆どハイキングとかしてないし.

まず二人で旅館の自転車を借りて標高1000mぐらいから出発. 蜂の茶屋(1400m)までひたすら上り坂. これが結構きつい. 15分おきぐらいに水分をとりながら黙々と1時間ちょい.

そこから歩いて登る. ところどころ残雪. 40cmぐらいありそうなところも. 誘ってくれた方はステッキにアイゼンと準備がいい. 私はランニングシューズでなんとかついていく. 山頂付近(1655m)はかなり急勾配で四つんばいになって登る. 昨日は雨も降っていたが今日は快晴ですばらしい眺めだった.

4月9日_

TeXのurlコマンド

長いURLを\urlコマンドに与えると折り返してくれなくて困って検索したら,

\usepackage[hyphens]{url}

とすればよいらしい. ハイフンのところで折り返してくれるようになるとのこと. ところが手元の環境でやるとエラーになった. なんだかよく分からないので最新環境にしてみるかとアップデートしたら何もコンパイルできなくなってしまった. TeXインストーラ 3を使ってるのだけど, どうもtexフォルダはできるのだがその下が空っぽ. 今までそんなことになったことはないのだが. 一度全部消してやり直したりしたのだけどどうもうまくいかない.

無駄に時間を使い, 結局諦めて前のバージョンに戻した.

で, urlはhyperrefよりも先にusepackageすればよいことが判明した.

// ok
\usepackage[dvipdfmx]{hyperref}
\usepackage{url}

// error
\usepackage[dvipdfmx]{hyperref}
\usepackage[hyphens]{url}

// ok
\usepackage[hyphens]{url}
\usepackage[dvipdfmx]{hyperref}

素人には近づきがたい.

4月7日_

mallocされた領域のサイズを取得する

標準関数にはないがgccならmalloc_usable_size, VCなら_msizeを使う. ただNULLを渡したときmalloc_usable_sizeは0を返すが_msizeはエラーになる. 先にポインタがNULLか否かを確認する必要がある.

ところでmalloc_usable_sizeは要求された値よりも大きい値を返すことがある. これはサイズを適当に丸めて処理しているから. 行儀はよくないがmanにはそのサイズまではアクセスしてもよいと書かれている.

#include <stdio.h>
#include <malloc.h>

int main()
{
    for (int i = 23; i < 26; i++) {
        char *p = (char*)malloc(i);
        size_t s = malloc_usable_size(p);
        printf("malloc size=%d usable_size=%zd\n", i, s);
        p[s - 1] = '0';
        free(p);
    }
}
malloc size=23 usable_size=24
malloc size=24 usable_size=24
malloc size=25 usable_size=40

この環境ではsize=23, 25のときは24, 40byteまでアクセスしてよいようだ.

ただaddress sanitizerでmalloc_usable_size()の値までアクセスするとエラーになることがあったようだ. valgrindは3.5から大丈夫になっている[malloc_usable_size doesn't return a usable size]. address sanitizerも少し前から大丈夫になったようだ. 上記コードをvalgrind上で実行すると結果が変わる.

malloc size=23 usable_size=23
malloc size=24 usable_size=24
malloc size=25 usable_size=25

よく考えられている.

4月6日_

VCでメモリリークを探す

古典的なネタだけど久しぶりに必要になったのでまとめておく.

//#define _CRTDBG_MAP_ALLOC
//#include <crtdbg.h>

#include <stdio.h>

void fff();

int main()
{
    // この3行を追加する
    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
    _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
    _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
    malloc(123);
    fff();
    new int[4];
}

_CrtSetReportFileでファイルハンドルを指定することもできる. そして全てのファイルの先頭で#define _CRTDBG_MAP_ALLOCしてからcrtdbg.hをインクルードする. /Dマクロと/FIファイル名オプションを指定すればよい.

cl /MDd /D_CRTDBG_MAP_ALLOC /FIcrtdbg.h

デバッグライブラリを使うように/MDdか/MTdを指定する. こうするとリークしてるmallocの行番号がでるようになる.

Detected memory leaks!
Dumping objects ->
{89} normal block at 0x0000000000493E00, 16 bytes long.
 Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
{88} normal block at 0x0000000000493DB0, 5 bytes long.
 Data: <     > CD CD CD CD CD
s.cpp(5) : {87} normal block at 0x000000000049A770, 12345 bytes long.
 Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
t.cpp(13) : {86} normal block at 0x00000000004970F0, 123 bytes long.
 Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
Object dump complete.

ただよく見ると分かるようにnewのリークは検知しているが行番号はでない. これに対応するにはnewに__FILE__と__LINE__を渡す必要がある.

/D"new=::new(_NORMAL_BLOCK, __FILE__, __LINE__)"

オプションを追加する. そうすると

Detected memory leaks!
Dumping objects ->
t.cpp(23) : {89} normal block at 0x00000000004C3E00, 16 bytes long.
 Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD

ファイル名と行番号が出るようになった. ただしnewをマクロで置き換えてしまうので独自newや配置newはエラーになってしまう. そのファイルではnewを定義しないか, 必要なnewをNewなどに置き換えてNewを定義する(こちらのほうが安全か). このあたりはvalgrindやaddress sanitizerに比べてかなり不便.

一応VC独自のpush_macro, pop_macroを使って必要な箇所を囲む方法もある.

#pragma push_macro("new")
#undef new

namespace s {
void *operator new(size_t) { return 0; } // ここでだけnewを元に戻す
}

#pragma pop_macro("new")

ちなみにcrtdbg.hを眺めてると一瞬_CRTDBG_MAP_ALLOC_NEWを定義すれば__FILE__, __LINE__つきのnewを使うように見える. しかし, それはcrtdbg.hで定義されたnewのファイル名と行番号が出るだけで何も意味がない. うっかり勘違いして作ってしまった名残なんだろうか.

These inlines do not add any information, due that __FILE__ is expanded to "crtdbg.h", which is not very helpful to the user.

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