0

配列インデックスを指す変数が必要で、配列の最後に到達したときに circle が 0 を返すようにします。if ステートメントを使用して判断できることはわかっていますが、同じ機能を実現するために mod を使用しない方がより迅速であるかどうかはわかりません。誰かアドバイスをもらえますか?

int p=0;
int arr[10];
void add_index(){   
   if(p==9) p=0;
   else     p++;
}

または

int p=0;
int arr[10];
void add_index(){
   p=(p+1)%10;
}
4

3 に答える 3

2

ちょっとしたテストを書き、gcc -O4最適化してコンパイルしました。

add_index_modこのテストのadd_index_if実装は次のとおりです。

void add_index_mod(int *p) {
    *p = (*p + 1) % 10;
}

void add_index_if(int *p) {
    if (*p == 9)
        *p = 0;
    else
        (*p)++;
}

そして、それは私が得たものですadd_index_mod:

mov eax, dword [rdi]
mov edx, 0x66666667
lea ecx, dword [rax + 1]
mov eax, ecx
imul edx
mov eax, ecx
sar eax, 0x1f
sar edx, 2
sub edx, eax
lea eax, dword [rdx + rdx*4]
add eax, eax
sub ecx, eax
mov dword [rdi], ecx
ret

ここで、コンパイラが div を一連の mul、shift、および sub に置き換えたことがわかります。このトリックについては、こちらで詳しく説明しています。

そして、それは私が得たものですadd_index_if:

mov edx, dword [rdi]            
lea eax, dword [rdx + 1]        
cmp edx, 9                      
mov edx, 0                      
cmove eax, edx                  
mov dword [rdi], eax            
ret

ここでは、cmp と条件付き mov だけで特別なことは何もありません。

これで、このを使用して、この両方の関数のアセンブリ コードの効率を計算することができます。しかし、これは順不同の実行、分岐予測などのため、最善の方法ではありません。

上で述べたように、ちょっとしたテストを書きました。

#include <stdio.h>
#include <stdint.h>

#define REPEATS (1 << 30)

static inline uint64_t rdtsc() {
  unsigned int hi, lo;
  __asm__ volatile("rdtsc" : "=a" (lo), "=d" (hi));
  return ((uint64_t)hi << 32) | lo;
}

void add_index_mod(int *p) {
    *p = (*p + 1) % 10;
}

void add_index_if(int *p) {
    if (*p == 9)
        *p = 0;
    else
        (*p)++;
}

int main() {
    int p = 0;
    uint32_t i;
    uint64_t start, stop;
    double delta, ticks_per_call;

    // mod ================================

    start = rdtsc();

    for (i = 0; i < REPEATS; ++i) {
        add_index_mod(&p);
    }

    stop = rdtsc();

    // gcc with -O4 can remove above loop
    // if we don't use its result so print it
    printf("%d\n", p);

    delta = (double)(stop - start);
    ticks_per_call = delta / REPEATS;
    printf("add_index_mod: %f\n", ticks_per_call);


    // if ================================

    start = rdtsc();

    for (i = 0; i < REPEATS; ++i) {
        add_index_if(&p);
    }

    stop = rdtsc();

    printf("%d\n", p);

    delta = (double)(stop - start);
    ticks_per_call = delta / REPEATS;
    printf("add_index_if: %f\n", ticks_per_call);

    return 0;
}

Intel Core i5-6500 の出力は次のとおりです。

add_index_mod: 9.643092
add_index_if: 2.063125

したがって、膨大な数の呼び出しに対して、私の CPUadd_index_ifよりも 5 倍高速です。add_index_mod

于 2016-06-07T15:18:20.233 に答える
1

状況の組み立てを掘り下げずに、ここで考慮すべきことがいくつかあります。

1) 分岐するとき (if ステートメント/関数呼び出しなど)、プロセッサはパイプラインをフラッシュする必要がある場合があります。これが意味することは、実行する必要があるかどうかを知る前に実行された一連の命令があり、その「処理能力」が失われているということです。これが常に起こると言っているわけではありませんが、可能性があります

2) 現在のエントリの 5 エントリ前に発生したエントリを見つけて、それに対していくつかの計算を行いたいとしましょう。2つの間の平均が必要だとしましょう。計算を行って結果を保存する代わりに、余分な変数を用意し、その不器用さをすべて使用する代わりに、より洗練されたソリューションを作成できます。

(array[index%10] + array[(index-5)%10])/2;

これで、循環バッファーをラップできます。

if/else ステートメントを使用してインデックスを決定するよりも、そのようにすると、そのようにコードを書くことに慣れると思います。

ただし、これには注意すべきことが 1 つあります。負の数のモジュラスを取ると、c は数学的に間違っています。否定的な答えになってしまいます。したがって、このようなことを行う場合 (たとえば、現在のエントリの前にエントリを検索する場合) は、一番上のインデックスからインデックス作成を開始します。

お役に立てれば。

于 2016-06-07T13:00:39.833 に答える