■_
このマンガがすごい2014
一位だったからというのがあるんだろうけど
「さよならソルシエ」作者インタビューが載ってたのね。
このマンガがすごい! 2014
お、なんか面白そうなものが 東京都・恵比寿で「AKIRA」などを手がけた大友克洋のポスター展 - 入場無料 | マイナビニュース 東京・六本木に日本初のアンディ・ウォーホル公式カフェ限定オープン | マイナビニュース
一つ前へ
2014年2月(中旬)
一つ後へ
2014年3月(上旬)
このマンガがすごい2014
一位だったからというのがあるんだろうけど
「さよならソルシエ」作者インタビューが載ってたのね。
このマンガがすごい! 2014
お、なんか面白そうなものが 東京都・恵比寿で「AKIRA」などを手がけた大友克洋のポスター展 - 入場無料 | マイナビニュース 東京・六本木に日本初のアンディ・ウォーホル公式カフェ限定オープン | マイナビニュース
総集編(中間的なまとめ)。 ちょっとどう進めたものか悩んでたもので。
GNU grep はマルチバイト文字を使う locale では(条件にもよるけれども)「遅い」 2.17 辺りでその辺を(部分的にでも)どうにかしようという変更が入った。
-i オプション指定時は、入力を「変換」してから検索を実行している (「一行ごとに処理」云々はたぶんこれが理由。 変換結果の格納のために新たにメモリ割り当てをしているので でかい「バッファ」を対象とすると面倒がある)。
文字を内部的に Unicode で持つということはしていないし、 Unicode が使われるという仮定もしていない (wchar_t は Unicode とは限らない)。 ただし、UTF-8 に対しては特別扱いしているところがある。 ほぼ毎回、必要になったところで wide character (≠Unicode) への変換やその逆の変換をいちいち行う。
たとえばある文字を小文字に変換する場合、 wide character に変換 → iswupper 関数で「大文字」か判定 → 「大文字」であれば towlower で「小文字」変換 → 変換された「小文字」を narrow character に変換 という手順を踏む。
正規表現エンジンを二種類抱えている (glibcと共通のものとそうでないもの)。 コンパイル時のオプションによっては pcre も抱えるが、それはとりあえず除外して話を進める。
2.17 → 2.18 での対応は「場当たり」 参考までに、2.17 と 2.18 のメインのプログラムの差分はこんなもん
diff -u grep-2.17/src/dfa.c grep-2.18/src/dfa.c --- grep-2.17/src/dfa.c 2014-02-18 10:42:39.000000000 +0900 +++ grep-2.18/src/dfa.c 2014-02-21 15:23:40.000000000 +0900 @@ -753,7 +753,7 @@ /* UTF-8 encoding allows some optimizations that we can't otherwise assume in a multibyte encoding. */ -static inline int +int using_utf8 (void) { static int utf8 = -1; @@ -1108,28 +1108,31 @@ { /* Defer to the system regex library about the meaning of range expressions. */ - regex_t re; - char pattern[6] = { '[', 0, '-', 0, ']', 0 }; - char subject[2] = { 0, 0 }; - c1 = c; - if (case_fold) - { - c1 = tolower (c1); - c2 = tolower (c2); - } - - pattern[1] = c1; - pattern[3] = c2; - regcomp (&re, pattern, REG_NOSUB); - for (c = 0; c < NOTCHAR; ++c) + struct re_pattern_buffer re = { 0 }; + char const *compile_msg; +#if 199901 <= __STDC_VERSION__ + char pattern[] = { '[', '\\', c, '-', '\\', c2, ']' }; +#else + char pattern[] = { '[', '\\', 0, '-', '\\', 0, ']' }; + pattern[2] = c; + pattern[5] = c2; +#endif + re_set_syntax (syntax_bits | RE_BACKSLASH_ESCAPE_IN_LISTS); + compile_msg = re_compile_pattern (pattern, sizeof pattern, &re); + if (compile_msg) + dfaerror (compile_msg); + for (c = 0; c < NOTCHAR; c++) { - if ((case_fold && isupper (c))) - continue; - subject[0] = c; - if (regexec (&re, subject, 0, NULL, 0) != REG_NOMATCH) - setbit_case_fold_c (c, ccl); + char subject = c; + switch (re_match (&re, &subject, 1, 0, NULL)) + { + case 1: setbit (c, ccl); break; + case -1: break; + default: xalloc_die (); + } } regfree (&re); + re_set_syntax (syntax_bits); } colon_warning_state |= 8; diff -u grep-2.17/src/dfa.h grep-2.18/src/dfa.h --- grep-2.17/src/dfa.h 2014-01-02 10:31:51.000000000 +0900 +++ grep-2.18/src/dfa.h 2014-02-21 15:23:40.000000000 +0900 @@ -99,3 +99,5 @@ takes a single argument, a NUL-terminated string describing the error. The user must supply a dfaerror. */ extern _Noreturn void dfaerror (const char *); + +extern int using_utf8 (void); diff -u grep-2.17/src/main.c grep-2.18/src/main.c --- grep-2.17/src/main.c 2014-02-18 10:42:39.000000000 +0900 +++ grep-2.18/src/main.c 2014-02-21 15:23:40.000000000 +0900 @@ -34,6 +34,7 @@ #include "c-ctype.h" #include "closeout.h" #include "colorize.h" +#include "dfa.h" #include "error.h" #include "exclude.h" #include "exitfail.h" @@ -1883,6 +1884,11 @@ trivial_case_ignore (size_t len, char const *keys, size_t *new_len, char **new_keys) { + /* Perform this translation only for UTF-8. Otherwise, this would induce + a 100-200x performance penalty for non-UTF8 multibyte locales. */ + if ( ! using_utf8 ()) + return false; + /* FIXME: consider removing the following restriction: Reject if KEYS contain ASCII '\\' or '['. */ if (memchr (keys, '\\', len) || memchr (keys, '[', len)) diff -u grep-2.17/src/searchutils.c grep-2.18/src/searchutils.c --- grep-2.17/src/searchutils.c 2014-02-10 14:18:37.000000000 +0900 +++ grep-2.18/src/searchutils.c 2014-02-21 15:23:40.000000000 +0900 @@ -19,6 +19,7 @@ #include <config.h> #include <assert.h> #include "search.h" +#include "dfa.h" #if HAVE_LANGINFO_CODESET # include <langinfo.h> #endif @@ -234,13 +235,8 @@ const char *p = *good; const char *prev = p; mbstate_t cur_state; -#if HAVE_LANGINFO_CODESET - static int is_utf8 = -1; - - if (is_utf8 == -1) - is_utf8 = STREQ (nl_langinfo (CODESET), "UTF-8"); - if (is_utf8 && buf - p > MB_CUR_MAX) + if (using_utf8 () && buf - p > MB_CUR_MAX) { for (p = buf; buf - p > MB_CUR_MAX; p--) if (mbclen_cache[to_uchar (*p)] != (size_t) -1) @@ -249,7 +245,6 @@ if (buf - p == MB_CUR_MAX) p = buf; } -#endif memset (&cur_state, 0, sizeof cur_state);
中間的なまとめといいつつこれまで書いてないことも書いてるけどまあいいやw
二月が逃げた
健康診断。
Developer Reading List | Dr Dobb's
このふたつ面白かった。 Programming Exercises So Long, and Thanks for All the Tests
今日も続けるよ! GNU grep 2.18リリース: 10倍速くなったと思ったら今度は200倍遅くなっていた | はむかず! git のコミット記録見てたら 2.18 てのがあったので、 てっきりタグかと思ったら違ってたw さて。
テストはja_JP.eucJPでやっている(日本語のこと気にしてくれてありがとう!) つまり流れとしては以下のとおり。 UTF8の場合、検索文字列を正規表現になおしてから検索したほうが10倍速くなったので、そのような変更をした(バージョン2.17としてリリース) ↓ 上記の変更でUTF8以外のとき、200倍遅くなっていた ↓ UTF8以外のマルチバイトのときは、正規表現変換はせずに、従来の方法で検索するように変更(バージョン2.18としてリリース) つまり、現在の実装としては、UTF8のときは検索文字列を正規表現に変換し、それ以外マルチバイトロケールの場合は テキストの方を変換してから検索することになっている。先日説明したトルコ語のI,i問題(あるいはトルコ語以外でも 言語によっては大文字小文字でバイト数が違う問題)は、UTF8以外では発生しないらしい。 文字コードの世界、深すぎる…はてなブックマーク - GNU grep 2.18リリース: 10倍速くなったと思ったら今度は200倍遅くなっていた | はむかず! 今回もここについたコメント見ながらニヤニヤしてたんですがそれはさておき。 grep.git - grep を見るに
grep.git - grep grep -i: avoid a performance regression in multibyte non-UTF8 locales * src/main.c: Include dfa.h. (trivial_case_ignore): Perform this optimization only for UTF8 locales. This rectifies a 100-200x performance regression in non-UTF8 multi-byte locales like ja_JP.eucJP. The regression was introduced by the 10x UTF8/grep-i speedup, commit v2.16-4-g97318f5. * NEWS (Bug fixes): Mention it. Reported by Norihiro Tanaka in http://debbugs.gnu.org/16232#50
repoted by タナカノリヒロ さんなので eucjp でのレポートなのもまあそういうことなのでは。 この方自身もいくつかコミットされてますね。 #16232 - [PATCH] grep: make --ignore-case (-i) faster (sometimes 10x) in multibyte locales - GNU bug report logs
#16232 - [PATCH] grep: make --ignore-case (-i) faster (sometimes 10x) in multibyte locales - GNU bug report logs From: Norihiro Tanaka To: Padraig Brady Cc: 16232 <16232 <at> debbugs.gnu.org>, Jim Meyering Subject: Re: bug#16232: [PATCH] grep: make --ignore-case (-i) faster (sometimes 10x) in multibyte locales Date: Wed, 19 Feb 2014 23:22:08 +0900 Hi, Slow down may be caused by the patch, because MBCSET is processed by not DFA engine but regexp engine. I tested performance on grep-2.17 and the version which the patch is reverted. Latter is 100x faster. yes $(printf '%078dm' 0)|head -10000 > in grep-2.17 original: $ for i in $(seq 10); do env LC_ALL=ja_JP.eucJP time src/grep -i n in; done Command exited with non-zero status 1 5.92user 1.69system 0:07.73elapsed 98%CPU (0avgtext+0avgdata 3856maxresident)k 0inputs+0outputs (0major+422minor)pagefaults 0swaps Command exited with non-zero status 1 5.59user 1.87system 0:07.58elapsed 98%CPU (0avgtext+0avgdata 3872maxresident)k 0inputs+0outputs (0major+423minor)pagefaults 0swaps Command exited with non-zero status 1 6.06user 1.58system 0:07.81elapsed 97%CPU (0avgtext+0avgdata 3872maxresident)k 0inputs+0outputs (0major+423minor)pagefaults 0swaps Command exited with non-zero status 1 5.73user 1.66system 0:07.52elapsed 98%CPU (0avgtext+0avgdata 3856maxresident)k 0inputs+0outputs (0major+422minor)pagefaults 0swaps Command exited with non-zero status 1 6.42user 1.19system 0:07.86elapsed 96%CPU (0avgtext+0avgdata 3872maxresident)k 0inputs+0outputs (0major+423minor)pagefaults 0swaps Command exited with non-zero status 1 6.15user 1.56system 0:08.34elapsed 92%CPU (0avgtext+0avgdata 3888maxresident)k 0inputs+0outputs (0major+424minor)pagefaults 0swaps Command exited with non-zero status 1 6.97user 0.61system 0:07.77elapsed 97%CPU (0avgtext+0avgdata 3856maxresident)k 0inputs+0outputs (0major+422minor)pagefaults 0swaps Command exited with non-zero status 1 7.00user 0.57system 0:07.71elapsed 98%CPU (0avgtext+0avgdata 3872maxresident)k 0inputs+0outputs (0major+423minor)pagefaults 0swaps Command exited with non-zero status 1 7.16user 0.25system 0:07.56elapsed 97%CPU (0avgtext+0avgdata 3872maxresident)k 0inputs+0outputs (0major+423minor)pagefaults 0swaps Command exited with non-zero status 1 7.04user 0.39system 0:07.60elapsed 97%CPU (0avgtext+0avgdata 3856maxresident)k 0inputs+0outputs (0major+422minor)pagefaults 0swaps After revert the patch: $ for i in $(seq 10); do env LC_ALL=ja_JP.eucJP time src/grep -i n in; done Command exited with non-zero status 1 0.07user 0.02system 0:00.10elapsed 92%CPU (0avgtext+0avgdata 3072maxresident)k 0inputs+0outputs (0major+232minor)pagefaults 0swaps Command exited with non-zero status 1 0.03user 0.01system 0:00.05elapsed 101%CPU (0avgtext+0avgdata 3072maxresident)k 0inputs+0outputs (0major+218minor)pagefaults 0swaps Command exited with non-zero status 1 0.04user 0.01system 0:00.06elapsed 90%CPU (0avgtext+0avgdata 3072maxresident)k 0inputs+0outputs (0major+218minor)pagefaults 0swaps Command exited with non-zero status 1 0.03user 0.02system 0:00.05elapsed 103%CPU (0avgtext+0avgdata 3056maxresident)k 0inputs+0outputs (0major+217minor)pagefaults 0swaps Command exited with non-zero status 1 0.04user 0.01system 0:00.06elapsed 86%CPU (0avgtext+0avgdata 3088maxresident)k 0inputs+0outputs (0major+219minor)pagefaults 0swaps Command exited with non-zero status 1 0.04user 0.01system 0:00.06elapsed 91%CPU (0avgtext+0avgdata 3056maxresident)k 0inputs+0outputs (0major+217minor)pagefaults 0swaps Command exited with non-zero status 1 0.04user 0.01system 0:00.06elapsed 87%CPU (0avgtext+0avgdata 3056maxresident)k 0inputs+0outputs (0major+217minor)pagefaults 0swaps Command exited with non-zero status 1 0.03user 0.02system 0:00.05elapsed 105%CPU (0avgtext+0avgdata 3072maxresident)k 0inputs+0outputs (0major+218minor)pagefaults 0swaps Command exited with non-zero status 1 0.04user 0.00system 0:00.06elapsed 90%CPU (0avgtext+0avgdata 3072maxresident)k 0inputs+0outputs (0major+218minor)pagefaults 0swaps Command exited with non-zero status 1 0.04user 0.01system 0:00.06elapsed 90%CPU (0avgtext+0avgdata 3056maxresident)k 0inputs+0outputs (0major+217minor)pagefaults 0swaps Norihiro
ここでの変更点を確認するとこう。
diff --git a/src/main.c b/src/main.c index bd20297..56ec6b3 100644 --- a/src/main.c +++ b/src/main.c @@ -34,6 +34,7 @@ #include "c-ctype.h" #include "closeout.h" #include "colorize.h" +#include "dfa.h" #include "error.h" #include "exclude.h" #include "exitfail.h" @@ -1883,6 +1884,11 @@ static bool trivial_case_ignore (size_t len, char const *keys, size_t *new_len, char **new_keys) { + /* Perform this translation only for UTF-8. Otherwise, this would induce + a 100-200x performance penalty for non-UTF8 multibyte locales. */ + if ( ! using_utf8 ()) + return false; + /* FIXME: consider removing the following restriction: Reject if KEYS contain ASCII '\\' or '['. */ if (memchr (keys, '\\', len) || memchr (keys, '[', len)) -- cgit v0.9.0.2
なんですが、ここだけ見ても本質を見落としてしまいます。 なぜこの trivial_case_ignore が utf8 のときだけしか効果がなく、 場合によってはむしろ逆効果になってしまうのか? それを理解するには、 例の十倍高速化パッチがどういったものなのかをきちんと把握しておく必要があるのです。 というわけでまだまだ続きます。
NHK のニュースでビットコイン関連のものが流れていましたが 「担保する」というのをアナウンサーが言ってたのを聴いて、 ああそこまで定着してんだなあと観念しました。
続き。 今日は compile/execute 辺りを掘り進めようと思ったのですが、ちと方向転換。
いまさらgrepが10倍高速化したのはなぜか | はむかず! ・マルチバイトロケールによっては、「大文字→小文字」または「小文字→大文字」の変換をすると文字のバイト数が変わってしまうことがある(!) ・そのため、バッファを使った効率の良い検索が使えず、1行読み込んで変換して検索するという効率の悪い実装が行われていた ・そこで発想を変えて、検索される文字列を同値な正規表現にあらかじめ変換すると速くなった。つまり「foo」という文字列を検索するときは「[fF][oO][oO]」という正規表現に変換してから検索する。
これですが、「1行読み込んで変換」という部分は正しくありません。 一行分ずつ変換してはいるのですが、読み込みは一行ずつではありません。
昨日も見た部分ですが main.c の do_execute という関数の
do_execute (char const *buf, size_t size, size_t *match_size, char const *start_ptr) { (略) if (MB_CUR_MAX == 1 || !match_icase) return execute (buf, size, match_size, start_ptr);
というのが「効率のよい」方のやり方で、 一方「一行ずつ」というのは
for (line_next = buf; line_next < buf + size; ) { const char *line_buf = line_next; const char *line_end = memchr (line_buf, eolbyte, (buf + size) - line_buf); if (line_end == NULL) line_next = line_end = buf + size; else line_next = line_end + 1; if (start_ptr && start_ptr >= line_end) continue; result = execute (line_buf, line_next - line_buf, match_size, start_ptr); if (result != (size_t) -1) return (line_buf - buf) + result; }
のように、バッファ中の切れ目(改行コード)を探してから 一行の先頭と末尾を検索対象(の範囲)として渡しているのであって、 読み込み自体はどちらの方法でも変わりありません (fillbuf という関数が読み込みのメイン部分です)。
さて。
grep.c static void Gcompile (char const *pattern, size_t size) { GEAcompile (pattern, size, RE_SYNTAX_GREP | RE_NO_EMPTY_RANGES); } static void Ecompile (char const *pattern, size_t size) { GEAcompile (pattern, size, RE_SYNTAX_POSIX_EGREP | RE_NO_EMPTY_RANGES); } static void Acompile (char const *pattern, size_t size) { GEAcompile (pattern, size, RE_SYNTAX_AWK); } static void GAcompile (char const *pattern, size_t size) { GEAcompile (pattern, size, RE_SYNTAX_GNU_AWK); } static void PAcompile (char const *pattern, size_t size) { GEAcompile (pattern, size, RE_SYNTAX_POSIX_AWK); } struct matcher const matchers[] = { { "grep", Gcompile, EGexecute }, { "egrep", Ecompile, EGexecute }, { "awk", Acompile, EGexecute }, { "gawk", GAcompile, EGexecute }, { "posixawk", PAcompile, EGexecute }, { "fgrep", Fcompile, Fexecute }, { "perl", Pcompile, Pexecute }, { NULL, NULL, NULL }, };
面倒なので細かい部分は無慈悲に切り捨てて行きますが、 通常の grep として起動した場合「コンパイル」を行う関数は GEAcompile、 「実行」する関数は EGexecute です。 とはいえ今回関係するのは最後の部分の
dfasearch.c void GEAcompile (char const *pattern, size_t size, reg_syntax_t syntax_bits) { (略) dfa = dfaalloc (); dfacomp (pattern, size, dfa, 1); kwsmusts (); free(motif); }
ここだけです。さらに言えば free() の手前にある kwsmusts() なのですが… ここまできて重大な事実が発覚!
dfasearch.c /* If the DFA turns out to have some set of fixed strings one of which must occur in the match, then we build a kwset matcher to find those strings, and thus quickly filter out impossible matches. */ static void kwsmusts (void) { /* With case-insensitive matching in a multi-byte locale, do not use kwsearch, because in that case, it would be too expensive, requiring that we case-convert all searched input. */ if (MB_CUR_MAX > 1 && match_icase) return; struct dfamust const *dm = dfamusts (dfa); if (dm) { char const *err; kwsinit (&kwset); /* First, we compile in the substrings known to be exact matches. The kwset matcher will return the index of the matching string that it chooses. */ for (; dm; dm = dm->next) { if (!dm->exact) continue; ++kwset_exact_matches; if ((err = kwsincr (kwset, dm->must, strlen (dm->must))) != NULL) error (EXIT_TROUBLE, 0, "%s", err); } /* Now, we compile the substrings that will require the use of the regexp matcher. */ for (dm = dfamusts (dfa); dm; dm = dm->next) { if (dm->exact) continue; if ((err = kwsincr (kwset, dm->must, strlen (dm->must))) != NULL) error (EXIT_TROUBLE, 0, "%s", err); } if ((err = kwsprep (kwset)) != NULL) error (EXIT_TROUBLE, 0, "%s", err); } }
今回注目している変更は主に grep.git - grep で行われた部分なのですがマルチバイト文字関連の高速化はこの他にも grep.git - grep があって、これで手の入った部分が最初の変更に影響してるっぽいのですね。 遅くしてしまったということはないと思いますが、 ある種無駄なことをしているんじゃないかという (たぶん、後の変更だけ入れておけば良いような気が。あーでも被ってない部分もあるな)。
そもそも、この kwsmusts() の先頭で
MB_CUR_MAX > 1 && match_icase
とかやってますけど
trivial_case_ignore() で正規表現への置き換えが行われたときには
match_icase って0になってんですよね。
で、commit 履歴を見ると
grep.git - grep 2014-01-26 dfasearch: skip kwset optimization when multi-byte+case-insensitive Norihiro Tanaka 2 -38/+19 2014-01-26 dfa: remove GREP-ifdef'd code in favor of code used by gawk Norihiro Tanaka 1 -8/+0 2014-01-25 gnulib: update to latest Jim Meyering 1 -0/+0 2014-01-17 grep: DFA now uses rational ranges in unibyte locales Paul Eggert 3 -28/+19 2014-01-17 grep: add undocumented '-X gawk' and '-X posixawk' options Aharon Robbins 1 -0/+14 2014-01-11 tests: remove superfluous uses of printf Pádraig Brady 1 -2/+2 2014-01-10 grep: make --ignore-case (-i) faster (sometimes 10x) in multibyte locales Jim Meyering 4 -1/+160
正規表現への置き換えをやってる方は 1月10日に行われたもので、 それとは別の方が行った高速化が1月26日にあります。 前の変更が反映されてないところに変更をかけたとも思えませんが、どうにも引っかかるものが。 というのは、1月10日で避けるようになっていた「重い」部分が 1月26日の変更で書きかえられてるようなんですね。 ということは、正規表現への置き換えという手法による高速化の影響を確認するには 2.17 のソースコード(と1月10日の差分)を見てはいけなかったのでしたーーーーーっ。
(たぶん)続く。
History of Java Language « The Crazy Programmer
History of Java Language « The Crazy Programmer There were five primary goals for creating the Java language 1. It should be "simple, object-oriented and familiar" 2. It should be "robust and secure" 3. It should be "architecture-neutral and portable" 4. It should execute with "high performance" 5. It should be "interpreted, threaded, and dynamic"
無気力ー
いまさらgrepが10倍高速化したのはなぜか | はむかず! の
1. マルチバイトロケールによっては、「大文字→小文字」または「小文字→大文字」の変換をすると文字のバイト数が変わってしまうことがある(!) 2. そのため、バッファを使った効率の良い検索が使えず、1行読み込んで変換して検索するという効率の悪い実装が行われていた 3. そこで発想を変えて、検索される文字列を同値な正規表現にあらかじめ変換すると速くなった。つまり「foo」という文字列を検索するときは「[fF][oO][oO]」という正規表現に変換してから検索する。
という結論に違和感。 1. については良いのだけど、 なぜ、バイト数が変わるケースがあると「効率の良い検索」ができないのか? (「そのため、」というのはそういうことですよね) そして、行ごとに処理することでなぜそんなに効率が落ちるのか? (三つのなかで遅くなる要因に言及しているのはこれだけ) 最後に、なぜ正規表現に変換すると速くなるのか。 これはまあ2. で使えなくなった「効率の良い検索」が使えるようになったということなのだろうけど それはなぜ? (「正規表現に変換したから」というのは理由になっていない)
まず、一行ごと云々に関して
main.c /* Invoke the matcher, EXECUTE, on buffer BUF of SIZE bytes. If there is no match, return (size_t) -1. Otherwise, set *MATCH_SIZE to the length of the match and return the offset of the start of the match. */ static size_t do_execute (char const *buf, size_t size, size_t *match_size, char const *start_ptr) { size_t result; const char *line_next; /* With the current implementation, using --ignore-case with a multi-byte character set is very inefficient when applied to a large buffer containing many matches. We can avoid much of the wasted effort by matching line-by-line. FIXME: this is just an ugly workaround, and it doesn't really belong here. Also, PCRE is always using this same per-line matching algorithm. Either we fix -i, or we should refactor this code---for example, we could add another function pointer to struct matcher to split the buffer passed to execute. It would perform the memchr if line-by-line matching is necessary, or just return buf + size otherwise. */ if (MB_CUR_MAX == 1 || !match_icase) return execute (buf, size, match_size, start_ptr); for (line_next = buf; line_next < buf + size; ) { const char *line_buf = line_next; const char *line_end = memchr (line_buf, eolbyte, (buf + size) - line_buf); if (line_end == NULL) line_next = line_end = buf + size; else line_next = line_end + 1; if (start_ptr && start_ptr >= line_end) continue; result = execute (line_buf, line_next - line_buf, match_size, start_ptr); if (result != (size_t) -1) return (line_buf - buf) + result; } return (size_t) -1; }
MB_CUR_MAX が1でなく、また、大小文字無視の検索(match_icase が0以外)場合に execute() の呼び出しが一行ごとになっているのが見て取れます。 中身はともかくとして、execute() の呼び出し回数の増加分だけ オーバーヘッドも増えるだろうことが予想できます。 が、なぜこのような使い分けが必要なのかはわかりません。
do_execute() がどこで呼ばれているかというとここ。 名前からしてバッファに対してgrepするというところですか
/* Scan the specified portion of the buffer, matching lines (or between matching lines if OUT_INVERT is true). Return a count of lines printed. */ static intmax_t grepbuf (char const *beg, char const *lim) { intmax_t nlines, n; char const *p; size_t match_offset; size_t match_size; nlines = 0; p = beg; while ((match_offset = do_execute (p, lim - p, &match_size, NULL)) != (size_t) -1) { char const *b = p + match_offset; char const *endp = b + match_size; /* Avoid matching the empty line at the end of the buffer. */ if (b == lim) break; if (!out_invert) { prtext (b, endp, NULL); nlines++; outleft--; if (!outleft || done_on_match) { if (exit_on_match) exit (EXIT_SUCCESS); after_last_match = bufoffset - (buflim - endp); return nlines; } } else if (p < b) { prtext (p, b, &n); nlines += n; outleft -= n; if (!outleft) return nlines; } p = endp; } if (out_invert && p < lim) { prtext (p, lim, &n); nlines += n; outleft -= n; } return nlines; }
さらに呼び出し階層の上に行くとファイルに対する grep な関数に なっていくので省略
次に目先を変えて main() のオプション解析のあたり
int main (int argc, char **argv) { case 'i': case 'y': /* For old-timers . . . */ match_icase = 1; break;
-i オプション指定時には match_icase という変数に1をセットと。
そこからちょっと進むと
/* As currently implemented, case-insensitive matching is expensive in multi-byte locales because of a few outlier locales in which some characters change size when converted to upper or lower case. To accommodate those, we revert to searching the input one line at a time, rather than using the much more efficient buffer search. However, if we have a regular expression, /foo/i, we can convert it to an equivalent case-insensitive /[fF][oO][oO]/, and thus avoid the expensive read-and-process-a-line-at-a-time requirement. Optimize-away the "-i" option, when possible, converting each candidate alpha, C, in the regexp to [Cc]. */ if (match_icase) { size_t new_keycc; char *new_keys; /* It is not possible with -F, not useful with -P (pcre) and there is no point when there is no regexp. It also depends on which constructs appear in the regexp. See trivial_case_ignore for those details. */ if (keycc && ! (matcher && (STREQ (matcher, "fgrep") || STREQ (matcher, "pcre"))) && trivial_case_ignore (keycc, keys, &new_keycc, &new_keys)) { match_icase = 0; free (keys); keys = new_keys; keycc = new_keycc; } } #if MBS_SUPPORT if (MB_CUR_MAX > 1) build_mbclen_cache (); #endif compile (keys, keycc); free (keys);
match_icase が0以外のときに、 さらに keycc があって、matcher が fgrep でも pcre でもないときに trivial_case_ignore を呼び出してその戻り値が0以外のときに match_icase を 0 にしています。 つまり、大小文字の違いを無視するという指定を無効にしているわけですね。 ということは、名前からしても trivial_case_ignore がアヤシイ。
#define MBRTOWC(pwc, s, n, ps) \ (MB_CUR_MAX == 1 ? \ (*(pwc) = btowc (*(unsigned char *) (s)), 1) : \ mbrtowc ((pwc), (s), (n), (ps))) #define WCRTOMB(s, wc, ps) \ (MB_CUR_MAX == 1 ? \ (*(s) = wctob ((wint_t) (wc)), 1) : \ wcrtomb ((s), (wc), (ps))) /* If the newline-separated regular expressions, KEYS (with length, LEN and no trailing NUL byte), are amenable to transformation into otherwise equivalent case-ignoring ones, perform the transformation, put the result into malloc'd memory, *NEW_KEYS with length *NEW_LEN, and return true. Otherwise, return false. */ static bool trivial_case_ignore (size_t len, char const *keys, size_t *new_len, char **new_keys) { /* FIXME: consider removing the following restriction: Reject if KEYS contain ASCII '\\' or '['. */ if (memchr (keys, '\\', len) || memchr (keys, '[', len)) return false; /* Worst case is that each byte B of KEYS is ASCII alphabetic and each other_case(B) character, C, occupies MB_CUR_MAX bytes, so each B maps to [BC], which requires MB_CUR_MAX + 3 bytes. */ *new_keys = xnmalloc (MB_CUR_MAX + 3, len + 1); char *p = *new_keys; mbstate_t mb_state; memset (&mb_state, 0, sizeof mb_state); while (len) { wchar_t wc; int n = MBRTOWC (&wc, keys, len, &mb_state); /* For an invalid, incomplete or L'\0', skip this optimization. */ if (n <= 0) { skip_case_ignore_optimization: free (*new_keys); return false; } char const *orig = keys; keys += n; len -= n; if (!iswalpha (wc)) { memcpy (p, orig, n); p += n; } else { *p++ = '['; memcpy (p, orig, n); p += n; wchar_t wc2 = iswupper (wc) ? towlower (wc) : towupper (wc); char buf[MB_CUR_MAX]; int n2 = WCRTOMB (buf, wc2, &mb_state); if (n2 <= 0) goto skip_case_ignore_optimization; assert (n2 <= MB_CUR_MAX); memcpy (p, buf, n2); p += n2; *p++ = ']'; } } *new_len = p - *new_keys; return true; }
ここで foobar → [Ff][Oo][Oo][Bb][Aa][Rr] への変換をやってました。
さて、では続いて正規表現マッチングのところを… と思いましたが時間切れのようです。
あ、あとあれだ。今回見ているこれはそもそも
Savannah Git Hosting - grep.git/commitdiff These days, nearly everyone uses a multibyte locale, and grep is often used with the --ignore-case (-i) option, but that option imposes a very high cost in order to handle some unusual cases in just a few multibyte locales. This change gets most of the performance of using LC_ALL=C without eliminating the ability to search for multibyte strings. With the following example, I see an 11x speed-up with a 2.3GHz i7: Generate a 10M-line file, with each line consisting of 40 'j's: yes jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj | head -10000000 > k Time searching it for the simple/noexistent string "foobar", first with this patch (best-of-5 trials): LC_ALL=en_US.UTF-8 env time src/grep -i foobar k 1.10 real 1.03 user 0.07 sys Back out that commit (temporarily), recompile, and rerun the experiment: git log -1 -p|patch -R -p1; make LC_ALL=en_US.UTF-8 env time src/grep -i foobar k 12.50 real 12.41 user 0.08 sys
元がとんでもなく遅いものだったというのをお忘れなく。 この先続けるかどうかは気分次第。
例のアレでようやくiOS7に上げることにした…… なんじゃこりゃーーっ
あれ Bletchley ParkとTMNOCとの大きな問題 の元記事読んだ覚えがない… The Colossal Problem with Bletchley Park and TMNOC
三ツ木氏には、さらに伝説のエピソードがある。
Excelブックを渡す者よ、浄化されよ。
Total refactoring is not a solution.を「完全なリファクタリング~」とするのはなんか違う気がする
iOS7.0.6で修正された「最悪のセキュリティバグ」はありがちなコーディングミスで発生していた | スラッシュドット・ジャパン アップル で今日も結構盛り上がっていたようで。 あ、こりゃ元記事読まないで思い込みで言ってるなという 発言(ブクマコメントとかツイートとか)を見る度にやにやしてしまう 性悪なわたしでありました。
もちろんそうでないものもあるわけで
[CI Testでケース検証してこなかった] -> [コードレビューで見過ごしてしまう体制だった] -> [OpenSourceなのにコミュニティも気づかずにいた] の3条件の合わせ技なので、「カバレッジが〜」「コードレビューが〜」のどちらかで完結する話でないと思う
— ぼうくん (@VoQn) 2014, 2月 24
ifの中括弧の省略とか、gotoの可否とかどうでもいくて、fail に無条件に飛んでてなんでデータが漏れる側に処理が倒れたのか。データ送信しなかったり、回線ぶった切ったりして兎に角止まる側に fail がなってないならどんな言語で書いたってダメだろうよ
— Kazuhiko Kikuchi (@kazuk) 2014, 2月 24
なんかハッシュタグも決まってた Twitter / 検索 - #gotofail
またうたた寝して時間がなくなったのであとで書くモード Learning from Apple’s #gotofail Security Bug | Arie van Deursen
例のトヨタの件で何か進展がと思ったらちょっと違ったぽい。 slashdot で取りあげられたサイト見に行ったけど… Stack Overflow Could Explain Toyota Vehicles' Unintended Acceleration - Slashdot ところで slashdotのこれをうけた reddit のこれにはw Stack Overflow Could Explain Toyota Unintended Acceleration : programming
eneloop が1本寿命尽きたっぽい。 充電器にかけてもすぐに充電完了になってしまうけど 使おうとしてもダメ。 まー結構長く使ったからなあ。こんなもんか。
情報処理技術者試験
春の試験の申し込みは締め切られたんでしたっけか。
前にちょっと書いたと思うんですが、某弊社、昨今のソフトウェアの重要性の増大に鑑みて
情報処理技術者試験の受験(と合格)を奨励するんだそうですよ。
それで、そのために何をやるか(やったか)というと、
「合格者に対する受験料を会社が負担」
合格して会社に対して申請したら受験料分を出してくれるんですと。
あー、応用情報以上だったかな。まあいいやその辺は。
もうね(以下検閲削除)
秋葉原
久しぶりに、
中央通りからJRと反対側に一本向こうの通り(名前ついてたっけ?)を
末広町の方まで幾つか店に(パーツショップな)入りながら歩いてみたりしたんですが
あそこもどうしてだいぶ雰囲気変わってますねー。
んで、帰りは歩行者天国の中央通りで秋葉原駅の方へ。
そういやこれを見かけました。あんなところにこういうのがあったのですねー
秋葉原の「花房神社」が、中央通りからも見えるように: Seaside Tears 2
二巻が四月予定と。めもめも。 スティーブ・ジョブズ(2)/ヤマザキマリ【コミック】 まんが王倶楽部 MangaohClub
なんかとある機械のファームにバグがあり WRT120N fprintf Stack Overflow - /dev/ttyS0
WRT120N fprintf Stack Overflow - /dev/ttyS0 With a good firmware disassembly and JTAG debug access to the WRT120N, it’s time to start examining the code for more interesting bugs. As we’ve seen previously, the WRT120N runs a Real Time Operating System. For security, the RTOS’s administrative web interface employs HTTP Basic authentication:
で
WRT120N fprintf Stack Overflow - /dev/ttyS0 The interesting bit of code to focus on is this: fprintf(request->socket, "Location %s\n\n", GetWebParam(cgi_handle, "TM_Block_URL")); Although it at first appears benign, cgi_tmUnBlock‘s processing of the TM_Block_URL POST parameter is exploitable, thanks to a flaw in the fprintf implementation:
長い文字列渡すとどかーんというありがちなパターンでした。と。 元記事は画像をたくさん使って詳しく説明してて面白いですよ
バグの話その2 午前中に Twitter / anohana: うわぁ。S式を使ってたらこんな間違いは無かったろうに… ht ... このツイートで知ったのですが ImperialViolet - Apple's SSL/TLS bug
いやまあそこかしこで盛り上がってること Apple's SSL/TLS bug | Hacker News アップル iOS 7.0.6提供開始、SSLの脆弱性を修正。OS X にも同じバグ発覚 - Engadget Japanese Apple's SSL/TLS bug : programming Use of GOTOs and implicit If statement blocks that led to server SSL certs being ignored in IOS 10.9+ : programming Learning from Apple’s #gotofail Security Bug | Arie van Deursen Apple史上最悪のセキュリティバグか、iOSとOS XのSSL接続に危険すぎる脆弱性が発覚──原因はタイプミス? | アプリオ
こんなご意見が
このコミットをしたのはきっとpythonプログラマだったのだ / “Apple史上最悪のセキュリティバグか、iOSとOS XのSSL接続に危険すぎる脆弱性が発覚──原因はタイプミス? | アプリオ”
http://t.co/YZK5OYT9cc
— tagomoris (@tagomoris)
2014, 2月 23
梅が咲いてまして。 梅と言えば昨年引っ越された近所の家に梅の木があって、 行き帰りに目にしていたのですが引っ越しの後建物を取り壊すときにその木もなくなってしまいました。 その土地はしばらく更地だったのですが最近になって建築のお知らせの看板が。 年年歳歳花相似 歳歳年年人不同。 オチはありません。
InfoQ のたぶん日本語翻訳はされないよなーってのにこういうのがありまして
Timothy Baldridge on Clojure's Core.Async
その sammary のところに
Timothy Baldridge (@timbaldridge) is a developer with Cognitect Inc. He hails from the mountain regions of Denver Colorado (USA).
He is a polyglot programmer with experience in Clojure, C#, Python, and Erlang. Most recently he was deeply
involved in the development of Clojure's Core.Async library, where he designed, implemented, and maintains the
state-machine code rewriting macro known as "go".
polyglot programmer (複数のプログラミング言語を使うプログラマー) って最近
それなりに見かけるような気がするなあ。そういや polyなんとかって色々あるよね、で思いつくままに挙げてみると
ポリバレント
(polyvalent - Wiktionary)
ポリマー
(polymerの意味 - 英和辞典 Weblio辞書)
polyline
(Polyline クラス (System.Windows.Shapes))
polynomial (expression)
(多項式の英語・英訳 - 英和辞典・和英辞典 Weblio辞書)
ポリフェノール - Wikipedia
ポリエチレン - Wikipedia
ポリエステル - Wikipedia
ポリカーボネート - Wikipedia
ポリプロピレン - Wikipedia
ポリウレタン - Wikipedia
あ、ポリゴンもか
ポリゴン - Wikipedia
まあ poly- の意味がそういうものなので
polyの意味 - 英和辞典 Weblio辞書
おまけ。
OVA 新 破裏拳ポリマー [DVD]
QCon New York 2014のトラック発表 を眺めてると Architectures You've Always Wondered About Applied Data Science and Machine Learning Java Innovations Software Architecture Improvements 辺りを見たいかなあ。 Architectures you've always wondered about | QCon New York 2014 Java Innovations - The Latest Trends in Java Technology | QCon New York 2014 Software Architecture Improvements | QCon New York 2014
なお倉田英之さんの「R.O.D」第12巻はさらに延期となって現在は予約停止中だ。
technical debt ってちょっと書こうかと思ったんだけど 今ひとつまとまりが。
Flash Player更新版がまた緊急公開、ゼロデイ攻撃発生のため - ITmedia エンタープライズ なんでこう後から後から(ry なのか、ソースコードを見てみたい。
日本語記事が出るとぱっと広まるのに今回ももやもや GoogleのJavaコーディング規約 といいつつこっちの元記事は見落としてたな。 QCon New York 2014のトラック発表 ま、行けないんですけどね。
アキバ系!電脳空間カウボーイズZ: 第三百七十二回 橋本和幸x清水亮「関数言語ナイト!幻のLISPマシンSymbolics!」前編
意見はイロイロだよね。 と思いつつ読んでみたらあれあれ? という気が。 事実誤認というか思い込み、単に知らないで書いちゃってるところもないだろうか
Why I prefer Java over C++ | softwarecave Why I prefer Java over C++ Posted on February 18, 2014 by robertp1984 C++ is a language I used for almost 2 years at work, for all my university projects except when there was an explicit requirement to use a different programming language and at home for very different tasks. The language alone is really powerful and well designed although the designers had to keep it compatible with C. Like every programming language, C++ has its own difficulties and problems: (略) Don’t get me wrong – I really like C++. I got used to the issues mentioned above and they are not big impediments for me. They just slow me down. I like pointers (the references in Java are very similar concept), I have no bad experience with multiple inheritance and I like templates. I fully understand why the handling of programming errors is not much better, why the compilation is slow and why memory management is done this way. But there is still one and the biggest reason why I don’t want to use C++ at work: very poor libraries. Of course, you can say that we have famous STL and Boost in C++ but to me they are just a bunch of weird macros, even weirder templates and unnecessary inheritance. Just ask yourself whether you can easily understand how to use given Boost class by just looking at its header and how hard is to understand the compilation errors when using them wrong. (略)
挙げられた項目は ポインター、多重継承、テンプレート、エラー処理、コンパイル速度、メモリー管理 なんだけどんー。
ライセンス云々に至っては、はあそうですか。としか。
PLMニュース:「IoTの経済効果、3Dプリンティングの約10倍」――PTCにおけるIoT戦略とは - MONOist(モノイスト) とかありますが、こういう話もあると モノのインターネットはすさまじく危険だ - そして多くはパッチ不可能である by Bruce Schneier (The Internet of Things Is Wildly Insecure - And Often Unpatchable) 元記事読んで気になってたんだけど 翻訳されてたのね(しかも新山さんだー)
Amazon.co.jp: Rethinking the Internet of Things: A Scalable Approach to Connecting Everything eBook: Francis daCosta: Kindleストア
あら kindle版のお値段が
紙の本の価格: ¥ 4,167
Kindle 価格: ¥ 0
一つ前へ
2014年2月(中旬)
一つ後へ
2014年3月(上旬)
リンクはご自由にどうぞ
メールの宛先はこちら