2010年4月29日木曜日

速度比較。文字列の組み立て - AWK

文字列を組み立てる方法
o 単純に文字列を並べた時、2つの方法の実行速度を調べてみました。
   + そのまま並べる方法
   + sprintfを使う方法

結果:
o どちらも、10万回で4秒程度でした。
o 但し、Gawk on Windows (3.1.7)上で sprintfを使うと、10万回で40秒前後まで遅くなりました。
   + mawk MBCS (32bit版) (1.3.3) の場合は、逆に、sprintfの方が数%程度速くなりました。
o どちらも、ウィルスチェックソフトなど、色々起動している中で測定しました。ですから、数字は特定環境下での目安です。


**** 実行方法
o 確認したAWKプログラムは
   + Gawk on Windows (3.1.7)
   + mawk MBCS (32bit版) (1.3.3)
o 確認したOSは、Windows XP

o 実行コマンドは
----------------
gawk.exe -f awkファイル名 > out_a_gawk.txt
mawk32.exe -f awkファイル名 > out_a_mawk.txt
----------------

o gettime.cpp -- systimeよりも高精度で測定する為の補助プログラム gettime.exeのソースコード
----------------
#include <windows.h>
#include <stdio.h>

int main(int argc, char* argv[]) {
  fprintf(stdout, "%u\n", GetTickCount());
  return 0;
}
----------------
   + このプログラムはミリ秒を返しますが、実際の精度は 10ミリ秒(= 0.01秒)程度か、それ以下です。これに、パイプ実行分の誤差が加わります。
   + 0.1秒程度の精度があれば十分ですので、Windows APIの中から動作が軽いものを選びました。
   + 今回は文字列渡しですが、system関数経由の整数渡しにすると、もっと動作が速くなるかもしれません。


**** test1 = sprintf から先に実行した場合
o gettime は時間を取得するプログラムです。
    + 実行時の負荷が少ない、GetTickCount関数を使用して います。実行環境での精度は30ミリ秒程度です。

o 実行したAWKプログラム
----------------
function f_out(v_title_str, v_start, v_end, v_time) {
 v_time = 0 + v_end - v_start;
 print v_title_str " : " v_time " (msec)";
}

function f_test1(v_title) {
 v_timer_cmd | getline v_start; close(v_timer_cmd);
 for (i = 0; i < 1000000; ++i) {
  v = sprintf("テストは%sと%sと%sと%sと%dです。", v1, v2, v3, v4, v5);
  v = sprintf("数字は%dです。", v0);
 }
 v_timer_cmd | getline v_end; close(v_timer_cmd);
 f_out(v_title, v_start, v_end);
}

function f_test2(v_title) {
 v_timer_cmd | getline v_start; close(v_timer_cmd);
 for (i = 0; i < 1000000; ++i) {
  v = "テストは" v1 "と" v2 "と" v3 "と" v4 "と" v5 "です。";
  v = "数字は" v0 "です。";
 }
 v_timer_cmd | getline v_end; close(v_timer_cmd);
 f_out(v_title, v_start, v_end);
}

BEGIN {
 v1 = "abcdefg";
 v2 = "hijklmn";
 v3 = "opqrstu";
 v4 = "vwxyz";
 v5 = 1234567890;
 v0 = 0;
 v_timer_cmd = "gettime";

 f_test1("# 1. sprintfを使った場合。1回目");
 f_test2("# 2. 変数を並べた場合。1回目");
 f_test1("# 3. sprintfを使った場合。2回目");
 f_test2("# 4. 変数を並べた場合。2回目");
 f_test1("# 5. sprintfを使った場合。3回目");
 f_test2("# 6. 変数を並べた場合。3回目");
 exit;
}
----------------
o Gawk on Windows (3.1.7)
# 1. sprintfを使った場合。1回目 : 39266 (msec)
# 2. 変数を並べた場合。1回目   :  4125 (msec)
# 3. sprintfを使った場合。2回目 : 39296 (msec)
# 4. 変数を並べた場合。2回目   :  4265 (msec)
# 5. sprintfを使った場合。3回目 : 41844 (msec)
# 6. 変数を並べた場合。3回目   :  4312 (msec)

o mawk MBCS (32bit版) (1.3.3)
# 1. sprintfを使った場合。1回目 : 4266 (msec)
# 2. 変数を並べた場合。1回目   : 4312 (msec)
# 3. sprintfを使った場合。2回目 : 4235 (msec)
# 4. 変数を並べた場合。2回目   : 4375 (msec)
# 5. sprintfを使った場合。3回目 : 4187 (msec)
# 6. 変数を並べた場合。3回目   : 4375 (msec)


**** test2 = 変数を並べる書き方から先に実行した場合
o 実行したAWKプログラム
----------------
function f_out(v_title_str, v_start, v_end, v_time) {
 v_time = 0 + v_end - v_start;
 print v_title_str " : " v_time " (msec)";
}

function f_test1(v_title) {
 v_timer_cmd | getline v_start; close(v_timer_cmd);
 for (i = 0; i < 1000000; ++i) {
  v = sprintf("テストは%sと%sと%sと%sと%dです。", v1, v2, v3, v4, v5);
  v = sprintf("数字は%dです。", v0);
 }
 v_timer_cmd | getline v_end; close(v_timer_cmd);
 f_out(v_title, v_start, v_end);
}

function f_test2(v_title) {
 v_timer_cmd | getline v_start; close(v_timer_cmd);
 for (i = 0; i < 1000000; ++i) {
  v = "テストは" v1 "と" v2 "と" v3 "と" v4 "と" v5 "です。";
  v = "数字は" v0 "です。";
 }
 v_timer_cmd | getline v_end; close(v_timer_cmd);
 f_out(v_title, v_start, v_end);
}

BEGIN {
 v1 = "abcdefg";
 v2 = "hijklmn";
 v3 = "opqrstu";
 v4 = "vwxyz";
 v5 = 1234567890;
 v0 = 0;
 v_timer_cmd = "gettime";

 f_test2("# 1. 変数を並べた場合。1回目");
 f_test1("# 2. sprintfを使った場合。1回目");
 f_test2("# 3. 変数を並べた場合。2回目");
 f_test1("# 4. sprintfを使った場合。2回目");
 f_test2("# 5. 変数を並べた場合。3回目");
 f_test1("# 6. sprintfを使った場合。3回目");
 exit;
}
----------------
o Gawk on Windows (3.1.7)
# 1. 変数を並べた場合。1回目   :  4375 (msec)
# 2. sprintfを使った場合。1回目 : 39922 (msec)
# 3. 変数を並べた場合。2回目   :  4062 (msec)
# 4. sprintfを使った場合。2回目 : 39719 (msec)
# 5. 変数を並べた場合。3回目   :  4188 (msec)
# 6. sprintfを使った場合。3回目 : 39562 (msec)

o mawk MBCS (32bit版) (1.3.3)
# 1. 変数を並べた場合。1回目   : 4422 (msec)
# 2. sprintfを使った場合。1回目 : 4187 (msec)
# 3. 変数を並べた場合。2回目   : 4422 (msec)
# 4. sprintfを使った場合。2回目 : 4188 (msec)
# 5. 変数を並べた場合。3回目   : 4437 (msec)
# 6. sprintfを使った場合。3回目 : 4187 (msec)


==
関連ページ:
    文字列を組み立てる方法
    ▼AWKの文字列操作関数と関連機能▼ABC順
    ▼AWKプログラムを書く▼ABC順
    ▼AWK
    ▼制作メモ
    > 文字列から文字を1つずつ取り出す
    文字列中で使用出来る特殊文字
    > プログラムの基本パターン
    ローカル変数を使う
(2010年9月11日追加。mawk32に対する詳細評価)
(2010年8月17日追加。補助プログラム gettimeのソースコード)