昨日の続きです。
blog.psl.ne.jp
この処理にどのくらいの時間がかかるかを可視化したいが、time()だと正数秒単位でしか計測できず、今回の1~100万の数字のランダマイズでは1秒未満のため0秒となってしまう。clock()では実時間が測れない。で、探したところ、timespec_get()を見つけた。time.hをインクルードすれば使える。厳密にいうと、プログラムが起動してから終了するまでを測りたいが、コード内に書くので、メインの処理前と処理後にそれぞれ計測して、差分を出すことにした。 timespec_getが整数部分とマイクロ秒(100万分の1秒)部分とに分かれて受け取るため、まどろっこしい計算になっている。 この時間計測の処理と、配列のランダマイズの処理をそれぞれ関数にしたコードは以下の通り。 shuffle.cpp Cの文法に全く疎いままだが、shuffle()の第1引数は配列の参照渡しのようになっていらしい。戻り値を受け取らなくても、seeknoが更新されている。 これを実行すると、 となる。CPUの能力によるが、私のPCでは、0.3~0.4秒くらいとなった。 これ、Perlで書いたら楽だなあと思い、なるべくCのコードに似せるようにして書いてみた。ただし、List::Util::shuffleとTime::HiResを使った。 うーむ短い。中間変数をかなり省略できるのがよい。このコードを実行すると、0.4~0.6秒くらいとなった。実行のたびに数値に揺れがあるので、それぞれ100回実行するように書き換えて比べたところ、 C…27.7651秒 Perl…41.8353秒 となった。やはりCの方が速いが、開発効率まで考えるとPerlのメリットも十分あると感じた。ただ今回は速度最優先+クライアントの指定のため、Cを使うしかない。実行時間の計測をしたい
struct timespec st_start, st_end;
float diff;
long sec, nsec;
timespec_get(&st_start, TIME_UTC);
// メイン処理
timespec_get(&st_end, TIME_UTC);
// 経過時間を算出
sec = end.tv_sec - start.tv_sec - (start.tv_nsec > end.tv_nsec ? 1 : 0);
nsec = end.tv_nsec - start.tv_nsec + (start.tv_nsec > end.tv_nsec ? 1000000000 : 0);
diff = sec + nsec / 1000000000.0;
printf("経過時間は %.4f 秒でした。\n", diff);#include <time.h>
#include <stdlib.h>
#include <stdio.h>
#define NUMBER 1000000
int main(void) {
int i;
int *seekno;
FILE *fp;
struct timespec st_start, st_end;
float diff;
float get_diff (timespec end, timespec start);
void shuffle(int array[], int size);
timespec_get(&st_start, TIME_UTC);
printf("要素数 %d 個の配列を生成してランダマイズします。\n", NUMBER);
seekno = (int *)malloc(NUMBER * sizeof(int));
for (i=0; i<NUMBER; i++) {
seekno[i] = i;
}
srand((int)st_start.tv_sec);
shuffle(seekno, NUMBER);
// シャッフル後の配列の冒頭10件を表示してみる
for (i = 0; i < 10; i++) {
printf("%d,", seekno[i]);
}
printf("\n\n");
// シャッフル後の配列を書き出す
fp = fopen("shuffle.txt", "w");
for (i=0; i<NUMBER; i++){
fprintf(fp, "%d\n", seekno[i]);
}
fclose(fp);
timespec_get(&st_end, TIME_UTC);
printf("要素数 %d 個の配列を生成しました。\n", NUMBER);
printf("経過時間は %.4f 秒でした。\n", get_diff(st_end, st_start));
return 0;
}
float get_diff (timespec end, timespec start) {
long sec = end.tv_sec - start.tv_sec - (start.tv_nsec > end.tv_nsec ? 1 : 0);
long nsec = end.tv_nsec - start.tv_nsec + (start.tv_nsec > end.tv_nsec ? 1000000000 : 0);
// printf("get_diff: %9ld, %9ld\n", sec, nsec);
return sec + nsec / 1000000000.0;
}
void shuffle(int array[], int size) {
for(int i = 0; i < size; i++) {
int j = (int)(rand() * 1000000.0 / RAND_MAX);
int tmp = array[i];
array[i] = array[j];
array[j] = tmp;
}
}
C:\***\***\ctest>shuffle
要素数 1000000 個の配列を生成してランダマイズします。
995244,347819,497573,612292,464339,154942,336466,550950,573412,179662,
要素数 1000000 個の配列を生成しました。
経過時間は 0.3006 秒でした。おまけ:Perlで書いた場合
use strict;
use List::Util;
use Time::HiRes qw(gettimeofday tv_interval);
my $NUMBER = 1000000;
my $start = [gettimeofday()];
printf("要素数 %d 個の配列を生成してランダマイズします。\n", $NUMBER);
my @seekno = List::Util::shuffle (0..$NUMBER-1);
### シャッフル後の配列の冒頭10件を表示してみる
print join(",", @seekno[0..9]), "\n\n";
open(my $fh, ">", "shuffle_pl.txt");
print $fh "$_\n" for @seekno;
close($fh);
printf("要素数 %d 個の配列を生成しました。\n", $NUMBER);
printf("経過時間は %.4f 秒でした。\n", tv_interval($start));