ファイルに int な文字列がたくさん書いてあって、 uniq するだけのために sort するときって、 "-n" つけますか??
きっと sort するためには numeric で扱ってくれたほうが楽なんだろうけど、 numeric に変換するのもまたコストだよなー、とおもうところです。
ということで、比較してみました。
環境
- OpenSolaris (snv_121), メモリ16GB
ichii386% uname -a SunOS aho 5.11 snv_121 i86pc i386 i86pc Solaris ichii386% cat /etc/release OpenSolaris Development snv_121 X86 Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. Use is subject to license terms. Assembled 13 August 2009 ichii386% locale LANG=C LC_CTYPE="C" LC_NUMERIC="C" LC_TIME="C" LC_COLLATE="C" LC_MONETARY="C" LC_MESSAGES="C" LC_ALL=C
- sortのバイナリ
gnu 以外の sort がどう違うのかはよくわかんないです。誰かおしえてください。
結果
"yyyymmdd [tab] id" が 1 億レコード並んで 1.7GB のファイルを以下のようなかんじで sort しました。 /hoge/tmp は十分な容量とそれなりのI/O性能がある場所です。
cat aho.txt > /dev/null time /usr/bin/sort -T/hoge/tmp aho.txt > /dev/null time /usr/bin/sort -T/hoge/tmp -n aho.txt > /dev/null time /usr/xpg4/bin/sort -T/hoge/tmp aho.txt > /dev/null time /usr/xpg4/bin/sort -T/hoge/tmp -n aho.txt > /dev/null time /usr/gnu/bin/sort -T/hoge/tmp aho.txt > /dev/null time /usr/gnu/bin/sort -T/hoge/tmp -n aho.txt > /dev/null time /usr/bin/i86/sort -T/hoge/tmp aho.txt > /dev/null time /usr/bin/i86/sort -T/hoge/tmp -n aho.txt > /dev/null time /usr/bin/amd64/sort -T/hoge/tmp aho.txt > /dev/null time /usr/bin/amd64/sort -T/hoge/tmp -n aho.txt > /dev/null
結果はつぎのグラフのようになりました。
なんと、gnu版だけ -n のほうが速く、しかもトータルで一番速かった!!
なにが違う?
数値で比較してそうなとこはたぶん以下。
gnu
/* Compare strings A and B as numbers without explicitly converting them to machine numbers. Comparatively slow for short strings, but asymptotically hideously fast. */ static int numcompare (const char *a, const char *b) { while (blanks[to_uchar (*a)]) a++; while (blanks[to_uchar (*b)]) b++; return strnumcmp (a, b, decimal_point, thousands_sep); }
strnumcmp は coreutils 付属ですが、中身は至ってふつう。
onnv
/* * Scan fractional part. */ for (++i; i < length; i++) { if (IS_SEPARATOR(number[i])) continue; if (!isdigit((uchar_t)number[i])) break; if (number[i] != '0') state |= IN_NUMBER; if (sign == '0') digits[j++] = '0' + '9' - number[i]; else digits[j++] = number[i]; }
ここに貼ったのだけじゃなんも分かんなくて、ポインタのつもり。
で、なにがちがうかというと、 onnv 版では IS_SEPARATOR のところで LC_NUMERIC の thousands_sep だけでなく、 LC_MONETARY の mon_thousands_sep も見てるみたいです。
結果が違う例
ichii386% cat test.txt 1 1,200 1.100 500 1300
1,200 が 500 と 1300 の間に入ってほしいところです。
- C, gnu
- "," 以降は無視されているようです。
ichii386% LC_MONETARY=C /usr/gnu/bin/sort -n test.txt 1 1,200 1.100 500 1300
- C, onnv
- gnu 同様 "," 以降は無視っぽい
ichii386% LC_MONETARY=C /usr/bin/sort -n test.txt 1 1,200 1.100 500 1300
ichii386% LC_MONETARY=ja_JP.UTF-8 /usr/gnu/bin/sort -n test.txt 1 1,200 1.100 500 1300
- ja_JP.UTF-8, onnv
- ちゃんと 1,200 が 500 の次に来ている!
ichii386% LC_MONETARY=ja_JP.UTF-8 /usr/bin/sort -n test.txt 1 1.100 500 1,200 1300