昨日,command_not_found_handlerというシェルスクリプトの関数を書いた*1のだけど,どうも実行が遅いような気がした.
試しに時間を計ってみると,確かに遅かった.再帰的に13回呼ぶのに,1秒くらい待たされている.
% time XXXXXXXXXXXXX => xXXXXXXXXXXXX => x xXXXXXXXXXXX => x x xXXXXXXXXXX => x x x xXXXXXXXXX => x x x x xXXXXXXXX => x x x x x xXXXXXXX => x x x x x x xXXXXXX => x x x x x x x xXXXXX => x x x x x x x x xXXXX => x x x x x x x x x xXXX => x x x x x x x x x x xXX => x x x x x x x x x x x xX => x x x x x x x x x x x x x (eval):1: command not found: x XXXXXXXXXXXXX 0.62s user 0.29s system 94% cpu 0.957 total
もとの関数はこれ.大文字をスペース+小文字に展開する処理を,Rubyのワンライナーで書いている.
function command_not_found_handler() { new=`echo $* | ruby -pe 'sub(/[A-Z]/){" "+$&.downcase}'` if [ "$*" != "$new" ]; then echo "=> $new" eval $new return 0 fi return 127 }
Rubyのワンライナーが遅いと思ったので,Cで書き直してみた.
#include <stdio.h> #include <ctype.h> int main(int argc, char *argv[]) { int i, j, found = 0; char* s; for (i = 1; i < argc; i++) { s = argv[i]; if (found) { printf("%s", s); } else { for(j = 0; s[j] != '\0'; j++) { if (isupper(s[j]) == 0) { putc(s[j], stdout); } else { printf(" %c%s", tolower(s[j]), (s[j+1] != '\0') ? &s[j+1] : ""); found = 1; break; } } } if (i < argc-1) printf(" "); } return found; }
これを使って,さっきの関数を書き直した.
function command_not_found_handler() { new=`~/bin/expand $*` if [ $? -ne 0 ]; then echo "=> $new" eval $new return 0 fi return 127 }
実行時間を計測するために,それぞれの関数で,
time XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
してみる.
Rubyのワンライナーで書いたとき,
4.54s user 2.25s system 93% cpu 7.278 total
7秒くらい待たされる.
Cで書いたとき,
0.14s user 0.52s system 76% cpu 0.861 total
0.8秒で終わる.
RubyのワンライナーをCで書き直すと10倍くらい速くなった.Cすごい.
*1:[http://d.hatena.ne.jp/hitode909/20091215/1260856588:title]