Lundinに追加します。はい、ARMのような命令セットは、レジスタベースのインデックスを持っているか、即時のインデックスを持つリーチを使用すると、コンパイラがインデックスを使用するのに役立つ場合があります。また、例えばARMはロード命令でポインタレジスタをインクリメントすることができますが、基本的には1つの命令で* p ++になります。
p [i]またはp [i ++] vs * pまたは* p ++を使用すると常にトスアップしますが、いくつかの命令セットはどのパスを取るべきかがはっきりしています。
同様にインデックス。あなたがアップの代わりにカウントダウンを使用していない場合は、ループごとに命令を保存することができます。あなたはループごとに命令を保存するかもしれませんが、あなたがカウントダウンをしていた場合
inc reg
cmp reg,#7
bne loop_top
:いくつかは、これを行う可能性があります
dec reg
bne loop_top
か一つでもプロセッサ私は通常
decrement_and_jump_if_not_zero loop_top
コンパイラを知っているがそれを知って、あなたはそれらを励ます必要はありません。しかし、メモリの読み込み順序が重要なp [i]形式を使用すると、コンパイラは読み込みの順序を任意に変更することはできません。その場合、コード数を減らしたいと思うでしょう。gccとLLVM(打ち鳴らす)の両方で
unsigned fun1 (const unsigned char *p, unsigned *x)
{
unsigned sum;
unsigned sqsum;
int i;
unsigned f;
sum = 0;
sqsum = 0;
for(i=0; i<8; i++)
{
f = *p++;
sum += f;
sqsum += f*f;
}
//to keep the compiler from optimizing
//stuff out
x[0]=sum;
return(sqsum);
}
unsigned fun2 (const unsigned char *p, unsigned *x )
{
unsigned sum;
unsigned sqsum;
int i;
unsigned f;
sum = 0;
sqsum = 0;
for(i=8;i--;)
{
f = *p++;
sum += f;
sqsum += f*f;
}
//to keep the compiler from optimizing
//stuff out
x[0]=sum;
return(sqsum);
}
unsigned fun3 (const unsigned char *p, unsigned *x)
{
unsigned sum;
unsigned sqsum;
int i;
sum = 0;
sqsum = 0;
for(i=0; i<8; i++)
{
sum += (unsigned)p[i];
sqsum += ((unsigned)p[i])*((unsigned)p[i]);
}
//to keep the compiler from optimizing
//stuff out
x[0]=sum;
return(sqsum);
}
unsigned fun4 (const unsigned char *p, unsigned *x)
{
unsigned sum;
unsigned sqsum;
int i;
sum = 0;
sqsum = 0;
for(i=8; i;i--)
{
sum += (unsigned)p[i-1];
sqsum += ((unsigned)p[i-1])*((unsigned)p[i-1]);
}
//to keep the compiler from optimizing
//stuff out
x[0]=sum;
return(sqsum);
}
:
だから私はこれらの事のすべてを試してみました。もちろん、それは定数だったので、両方ともループをアンロールしました。 gccでは、微妙なレジスタミックスの変更があった場合、それぞれの実験で同じコードが生成されます。そして、私は少なくとも1人の読者がコードによって記述された順序ではないので、バグを主張するだろう。
すべての4つのgccソリューションは、いくつかの読み込み順序を変更して、ソースコードから順不同であることに気付きました。これが、コードに記述されている順序で読み込みに依存しているハードウェア/ロジックに対するものであれば、大きな問題があります。
00000000 <fun1>:
0: e92d05f0 push {r4, r5, r6, r7, r8, sl}
4: e5d06001 ldrb r6, [r0, #1]
8: e00a0696 mul sl, r6, r6
c: e4d07001 ldrb r7, [r0], #1
10: e02aa797 mla sl, r7, r7, sl
14: e5d05001 ldrb r5, [r0, #1]
18: e02aa595 mla sl, r5, r5, sl
1c: e5d04002 ldrb r4, [r0, #2]
20: e02aa494 mla sl, r4, r4, sl
24: e5d0c003 ldrb ip, [r0, #3]
28: e02aac9c mla sl, ip, ip, sl
2c: e5d02004 ldrb r2, [r0, #4]
30: e02aa292 mla sl, r2, r2, sl
34: e5d03005 ldrb r3, [r0, #5]
38: e02aa393 mla sl, r3, r3, sl
3c: e0876006 add r6, r7, r6
40: e0865005 add r5, r6, r5
44: e0854004 add r4, r5, r4
48: e5d00006 ldrb r0, [r0, #6]
4c: e084c00c add ip, r4, ip
50: e08c2002 add r2, ip, r2
54: e082c003 add ip, r2, r3
58: e023a090 mla r3, r0, r0, sl
5c: e080200c add r2, r0, ip
60: e5812000 str r2, [r1]
64: e1a00003 mov r0, r3
68: e8bd05f0 pop {r4, r5, r6, r7, r8, sl}
6c: e12fff1e bx lr
負荷や微妙レジスタ混合のためのインデックスは、すべての操作は、同じ順序で同じであった、GCCからの機能の唯一の違いでした。
LLVM /打ち鳴らす:おそらくキャッシュを考えると、すべてのワンショットで読み込むなって、読み、従うことが
00000000 <fun1>:
0: e92d41f0 push {r4, r5, r6, r7, r8, lr}
4: e5d0e000 ldrb lr, [r0]
8: e5d0c001 ldrb ip, [r0, #1]
c: e5d03002 ldrb r3, [r0, #2]
10: e5d08003 ldrb r8, [r0, #3]
14: e5d04004 ldrb r4, [r0, #4]
18: e5d05005 ldrb r5, [r0, #5]
1c: e5d06006 ldrb r6, [r0, #6]
20: e5d07007 ldrb r7, [r0, #7]
24: e08c200e add r2, ip, lr
28: e0832002 add r2, r3, r2
2c: e0882002 add r2, r8, r2
30: e0842002 add r2, r4, r2
34: e0852002 add r2, r5, r2
38: e0862002 add r2, r6, r2
3c: e0870002 add r0, r7, r2
40: e5810000 str r0, [r1]
44: e0010e9e mul r1, lr, lr
48: e0201c9c mla r0, ip, ip, r1
4c: e0210393 mla r1, r3, r3, r0
50: e0201898 mla r0, r8, r8, r1
54: e0210494 mla r1, r4, r4, r0
58: e0201595 mla r0, r5, r5, r1
5c: e0210696 mla r1, r6, r6, r0
60: e0201797 mla r0, r7, r7, r1
64: e8bd41f0 pop {r4, r5, r6, r7, r8, lr}
68: e1a0f00e mov pc, lr
はるかに簡単。少なくとも1つのケースでは、llvmが順不同で読み込みを取得しました。
00000144 <fun4>:
144: e92d40f0 push {r4, r5, r6, r7, lr}
148: e5d0c007 ldrb ip, [r0, #7]
14c: e5d03006 ldrb r3, [r0, #6]
150: e5d02005 ldrb r2, [r0, #5]
154: e5d05004 ldrb r5, [r0, #4]
158: e5d0e000 ldrb lr, [r0]
15c: e5d04001 ldrb r4, [r0, #1]
160: e5d06002 ldrb r6, [r0, #2]
164: e5d00003 ldrb r0, [r0, #3]
はい、ラムからの値の平均値は、問題ありません。
コンパイラはアンロールされたパスを選択し、マイクロオプティマイズについて気にしませんでした。ループのサイズのために、ループごとにロードされた値の1つを保持している一連のレジスタを書き込んだ後、それらの一時的な読み込みまたは乗算から加算を実行することを選択します。私たちがループのサイズを少し増やした場合、アンロールされたループ内でsumとsqsumの累算がレジスタを使い果たしたときに累積することが予想されます。あるいは、ループをアンロールすることを選択したところでしきい値に達するでしょう。
長さを渡して、上のコードの8を長さが渡されたものに置き換えて、コンパイラーがこれをループするように強制します。あなたはみかんの最適化を参照して、このような命令が使用されています。
a4: e4d35001 ldrb r5, [r3], #1
以降の命令の数と等しくない場合、彼らは一つの場所や枝にループレジスタの修正を行うアームであることを...彼らは可能性があるため。
これは数学関数ですが、floatを使用すると痛いです。マルチプルを使うのは苦痛で、分裂はずっと悪く、幸いにもシフトが使われました。幸いにも、これは符号なしであったので、シフトを使用することができました(コンパイラは、符号付きの数値との除算を使用した場合、算術シフトを使用することがわかっていたはずです)。
基本的には、内部ループが複数回実行されるため、マイクロオプティマイズに焦点を当てます。これを変更することが可能であれば、シフトや追加を行い、可能であればデータを並べ替えることができます。ループ(可能な場合は、廃棄物その他のコピーをいけない、これを行うために他の場所でループ)
const unsigned char* p = (const unsigned char*)(j*patch->step + aux);
あなたには、いくつかの速度を得ることができます。私はそれを試してみませんでしたが、それはループ内のループであるため、コンパイラはおそらくそのループをアンロールしません...
短い話ですが、ダンベルコンパイラに対する命令セットによってはいくらか利益が得られるかもしれませんが、このコードは実際には悪くないので、コンパイラは最適化することもできます。
パフォーマンスに影響する可能性のあることはたくさんあります(適切な処理時間を測定していると仮定します)。どうやらあなたはOpenCVを使用しているので、画像のサイズには大きな違いがあると言います。それはどれくらい大きいですか? – karlphillip
何よりも遅い?? –
ユニット「mms」は何を意味していますか?それは "ミリミリ秒"ですか? "ミリメートル秒"? – unwind