挿入ソートの話のまとめ
gcc4.3 -O3 on Atom, N=10000の条件で挿入ソートを測った結果(ソースコードは下に)。所要時間(time)を測っただけでは振る舞いが掴めないので、cachegrindでD1キャッシュ読み回数(D1read)を数えてみた。
関数 | メモリ | time | D1read |
---|---|---|---|
yane0 | malloc | 9.79 | 62M |
wkpd0 | malloc | 9.82 | 27M |
yane1 | static | 8.09 | 27M |
wkpd1 | static | 11.35 | 28M |
大規模なソートということでmallocで確保したメモリをソートする場合、最適化がよく効いている限り、どちらもほぼ同等の速度で動くようだ。
ただし、やね版はstaticに確保したメモリをソートする場合、Wikipedia版よりも約3割速く動いた。ほぼ本人の言っている通りの成績だ。
〆。
とはいえ、jのオフセットや番兵のありなしで20%くらい揺らぐのも事実。要素数によっても要素サイズによってもCPUによってもコンパイラによっても簡単に変わる話なので、両方実装して速度を比較しないと判断できない、といったところだろうか。
あと、iccではWikipedia版の方が速い、というより、iccではやね版が妙に遅い、という傾向がある。オプション調整で速くなるのかもしれないけれど試さない。
ソース。
#include <stdlib.h> #include <stdio.h> #include <string.h> void yaneurao_ins_sort_000(int data[], size_t const size) { int i, j, tmp; for (i = 1; i < size; ++i) { tmp = data[i]; if (data[i-1] > tmp) { j = i; do { data[j] = data[j-1]; --j; } while (j > 0 && data[j-1] > tmp); data[j] = tmp; } } } void wikipedia_ins_sort_000(int data[], size_t const size) { int i, j, tmp; for (i = 1; i < size; ++i) { tmp = data[i]; for (j = i; j > 0 && data[j-1] > tmp; j--) { data[j] = data[j-1]; } data[j] = tmp; } }
#include <stdlib.h> #include <stdio.h> #include <string.h> #define MAX 100000 static int local[MAX]; void yaneurao_ins_sort_001(int const orig[], size_t const size) { int i, j, tmp; if (MAX < size) { return; } memcpy(local, orig, size*sizeof(int)); for (i = 1; i < size; ++i) { tmp = local[i]; if (local[i-1] > tmp) { j = i; do { local[j] = local[j-1]; --j; } while (j > 0 && local[j-1] > tmp); local[j] = tmp; } } } void wikipedia_ins_sort_001(int const orig[], size_t const size) { int i, j, tmp; if (MAX < size) { return; } memcpy(local, orig, size*sizeof(int)); for (i = 1; i < size; ++i) { tmp = local[i]; for (j = i; j > 0 && local[j-1] > tmp; j--) { local[j] = local[j-1]; } local[j] = tmp; } }
#include <stdlib.h> #include <stdio.h> #include <string.h> #include <time.h> extern void yaneurao_ins_sort_000(int data[], size_t const size); extern void wikipedia_ins_sort_000(int data[], size_t const size); extern void yaneurao_ins_sort_001(int const data[], size_t const size); extern void wikipedia_ins_sort_001(int const data[], size_t const size); int main (int argc, char *argv[]) { int i, loop, size; clock_t start, stop; if (argc < 3) { return -1; } loop = atoi(argv[1]); size = atoi(argv[2]); int *orig = malloc(size*sizeof(int)); int *data = malloc(size*sizeof(int)); srand(0xdeadbeef); for (i = 0; i < size; ++i) { orig[i] = rand(); } start = clock(); for (i = 0; i < loop; ++i) { memcpy(data, orig, size*sizeof(int)); yaneurao_ins_sort_000(data, size); } stop = clock(); for (i = 1; i < size; ++i) { if (data[i] < data[i-1]) { puts("ERROR!"); } } printf("%f\n", (stop-start)*1E-6); start = clock(); for (i = 0; i < loop; ++i) { memcpy(data, orig, size*sizeof(int)); wikipedia_ins_sort_000(data, size); } stop = clock(); for (i = 1; i < size; ++i) { if (data[i] < data[i-1]) { puts("ERROR!"); } } printf("%f\n", (stop-start)*1E-6); start = clock(); for (i = 0; i < loop; ++i) { yaneurao_ins_sort_001(orig, size); } stop = clock(); printf("%f\n", (stop-start)*1E-6); start = clock(); for (i = 0; i < loop; ++i) { wikipedia_ins_sort_001(orig, size); } stop = clock(); printf("%f\n", (stop-start)*1E-6); free(data); free(orig); return 0; }
CC = gcc CFLAGS = -O3 #CC = icc #CFLAGS = -O3 all:a.out ./a.out 100000000 10 ./a.out 1000000 100 ./a.out 10000 1000 ./a.out 100 10000 ./a.out 1 100000 cg:a.out valgrind --tool=cachegrind ./a.out 1000000 10 valgrind --tool=cachegrind ./a.out 10000 100 valgrind --tool=cachegrind ./a.out 100 1000 valgrind --tool=cachegrind ./a.out 1 10000 find . -name 'cachegrind.out.*' -exec cg_annotate {} \; clean: ${RM} *.out *.o ${RM} cachegrind.out.* .c.o: ${CC} -c ${CFLAGS} $? a.out:extern.o sort_000.o sort_001.o ${CC} ${CFLAGS} extern.o sort_000.o sort_001.o -o $@