4要素の分岐しないソート (GCC用)
(こちらの記事の続き、おまけです)
「分岐しないソート」の、VC++向けのコードをGCC+Linux向けに修正してみました。
実はC言語によるソートである sort4l() のほうも相当速いので、私の手元の環境だと同じくらいの速度しか出なかった*1んですが、面白いのでオッケー。例えば {1,2,3,4} から生成される順列(24通り)を何百万回か食わすような処理を書いてみると Celeron 2.4GHz & Linux だとアセンブラ版が10%遅く、Athron64 3200+ & Linux/x86_64, gcc -m32 だとアセンブラ版が10%速く、ARM9 200MHz & Linux だと同じ速度でした。
まずx86向けから。
// for GCC + Linux/x86
void sort4(unsigned int *d) {
__asm__ (
"mov (%0), %%eax;"
"mov 4(%0), %%ebx;"
"mov 8(%0), %%ecx;"
"mov 12(%0), %%edx;"
"cmp %%ebx, %%eax;"
"cmova %%ebx, %%eax;"
"cmova (%0), %%ebx;"
"cmp %%edx, %%ecx;"
"cmova %%edx, %%ecx;"
"cmova 8(%0), %%edx;"
"mov %%eax, %%esi;"
"cmp %%ecx, %%eax;"
"cmova %%ecx, %%eax;"
"cmova %%esi, %%ecx;"
"mov %%ebx, %%esi;"
"cmp %%edx, %%ebx;"
"cmova %%edx, %%ebx;"
"cmova %%esi, %%edx;"
"mov %%ebx, %%esi;"
"cmp %%ecx, %%ebx;"
"cmova %%ecx, %%ebx;"
"cmova %%esi, %%ecx;"
"mov %%eax, (%0);"
"mov %%ebx, 4(%0);"
"mov %%ecx, 8(%0);"
"mov %%edx, 12(%0);"
:: "D"(d)
: "%eax", "%ebx", "%ecx", "%edx", "%esi", "memory" );
}せっかくコードが短く、オブジェクトサイズ比で sort4l() の1/5以下なので、マクロにしてコード中にインラインで埋め込んでしまうのも良いかもしれません。当然ですが関数呼び出しのオーバーヘッドが減る分、ずいぶん速くなります。Cで書かれた sort4l() を、無理矢理 __attribute__((__always_inline__)) 等でインライン化するより、(気のせいかもしれませんが)スマートかと。
こんな感じ。
// for GCC + Linux/x86
#define SORT4(d) \
({ \
unsigned int *d_ = (d); \
__asm__ \
( \
"mov (%0), %%eax;" \
"mov 4(%0), %%ebx;" \
"mov 8(%0), %%ecx;" \
"mov 12(%0), %%edx;" \
"cmp %%ebx, %%eax;" \
"cmova %%ebx, %%eax;" \
"cmova (%0), %%ebx;" \
"cmp %%edx, %%ecx;" \
"cmova %%edx, %%ecx;" \
"cmova 8(%0), %%edx;" \
"mov %%eax, %%esi;" \
"cmp %%ecx, %%eax;" \
"cmova %%ecx, %%eax;" \
"cmova %%esi, %%ecx;" \
"mov %%ebx, %%esi;" \
"cmp %%edx, %%ebx;" \
"cmova %%edx, %%ebx;" \
"cmova %%esi, %%edx;" \
"mov %%ebx, %%esi;" \
"cmp %%ecx, %%ebx;" \
"cmova %%ecx, %%ebx;" \
"cmova %%esi, %%ecx;" \
"mov %%eax, (%0);" \
"mov %%ebx, 4(%0);" \
"mov %%ecx, 8(%0);" \
"mov %%edx, 12(%0);" \
:: "D"(d_) \
: "%eax", "%ebx", "%ecx", "%edx", "%esi", "memory" \
); \
})次はx86_64向け。
// for GCC + Linux/x86_64
void sort4(unsigned int* d) {
__asm__
(
"mov (%0), %%r8d;"
"mov 4(%0), %%r9d;"
"mov 8(%0), %%r10d;"
"mov 12(%0), %%r11d;"
"cmp %%r9d, %%r8d;"
"cmova %%r9d, %%r8d;"
"cmova (%0), %%r9d;"
"cmp %%r11d, %%r10d;"
"cmova %%r11d, %%r10d;"
"cmova 8(%0), %%r11d;"
"mov %%r8d, %%eax;"
"cmp %%r10d, %%r8d;"
"cmova %%r10d, %%r8d;"
"cmova %%eax, %%r10d;"
"mov %%r9d, %%eax;"
"cmp %%r11d, %%r9d;"
"cmova %%r11d, %%r9d;"
"cmova %%eax, %%r11d;"
"mov %%r9d, %%eax;"
"cmp %%r10d, %%r9d;"
"cmova %%r10d, %%r9d;"
"cmova %%eax, %%r10d;"
"mov %%r8d, (%0);"
"mov %%r9d, 4(%0);"
"mov %%r10d, 8(%0);"
"mov %%r11d, 12(%0);"
:: "D"(d)
: "%r8", "%r9", "%r10", "%r11", "%eax", "memory"
);
}おまけでARM向け。ARMというCPUは、殆ど全ての命令をconditionalに実行できます(条件実行機能)。ですので、分岐しないソートを簡単に実装できます。Armadillo-9で動作確認しました。
// for GCC + Linux/arm
void sort4(unsigned int* d) {
__asm__ (
"LDMIA %0, {r1-r4};"
"MOV r5, r1;"
"CMP r2, r1;"
"MOVCC r1, r2;"
"MOVCC r2, r5;"
"MOV r5, r3;"
"CMP r4, r3;"
"MOVCC r3, r4;"
"MOVCC r4, r5;"
"MOV r5, r1;"
"CMP r3, r1;"
"MOVCC r1, r3;"
"MOVCC r3, r5;"
"MOV r5, r2;"
"CMP r4, r2;"
"MOVCC r2, r4;"
"MOVCC r4, r5;"
"MOV r5, r2;"
"CMP r3, r2;"
"MOVCC r2, r3;"
"MOVCC r3, r5;"
"STMIA %0, {r1-r4};"
:: "g"(d)
: "r1", "r2", "r3", "r4", "r5", "memory" );
}以上です。http://www.nist.gov/dads/HTML/bitonicSort.html も参考になります。同じアルゴリズムですね。
*1:わたしが何か間違えていなければ