2011-11-15 20 views
10

エンベデッドC++では、モジュロ演算子 "%"と除算演算子 "/"が非常に非効率的であると言われています。C++で%operatorおよび/ Operatorを使用する代わりに

a = b - c; 
while (a >= c) { 
    a = a - c; 
} 

しかし、私の質問は、しばらくループを含むこのコードです:私は、これは次のロジックを使用して達成することができることを理解

a = b % c; 

:どのように私は代わりに次の式を得ることができ

%operatorに比べて十分に効率的ですか?

おかげで、 Kirti

+0

"このコードは、%演算子と比較してwhileループが十分に効率的ですか?"あなたは、あなたがプログラムを使用していることを教えてください。それは遅い感じですか?あなたは気づくことができますか?プロファイリングして、これがまったく遅いことを発見しましたか? – GManNickG

+2

サイズによって異なります。 'b = 1000000000'と' c = 3'の場合。それはしばらく時間がかかります... – Mysticial

+0

ターゲットCPUとコンパイラを教えてください。それがなければ、どのアプローチも比較することは不可能です。 – fghj

答えて

7

何も%オペレータよりもかなり効率的になるだろうされていません。それを行うより良い方法があれば、合理的なコンパイラがそれを自動的に変換します。 %/が非効率的であると言われたら、それはそれらが困難な操作であるからです。モジュロを実行する必要がある場合は、そうしてください。

より良い方法がある特別なケースがあるかもしれません。例えば、modの2乗はバイナリとして書くことができますが、おそらくコンパイラによって最適化されます。

18

あなたが何をするにしても、除算とモジュラスは実際には高価なハードウェア操作です(これはハードウェアアーキテクチャに関連する言語やコンパイラよりも、おそらく加算の10倍です)。

しかし、現在のラップトップやサーバー、ハイエンドマイクロコントローラでは、cacheミスはしばしば分割よりもはるかに遅いです!

GCCコンパイラは、除数が定数のときに、それらを最適化できることがよくあります。

あなたの素朴なループは通常、ハードウェア除算命令(またはハードウェアで提供されていない場合はそれを実行するライブラリルーチン)を使用するよりもずっと遅くなります。私はあなたがループの代わりに&部門を避けることに間違っていると信じています。

アルゴリズムを調整する場合があります。あなたのコードを使用することはお勧めしません。 早すぎる最適化が悪いであることを覚えておいてください。まず、あなたのプログラムを正しいものにしてから、それをプロファイリングして問題点を見つけてください。

+0

+1は、最適化について心配する前にプログラムを取得するためのものです。多くの失敗したプロジェクトの原因。 – Dan

+1

完了時には引用符が良い:*時間の約97%を占める小さな効率を忘れるべきである。早すぎる最適化はすべての悪の根源だ。 –

5

あなたのプロセッサ/コンパイラがdivide/modを実行することを決定するよりも、そのコードはほとんど確実に遅くなります。一般的に、mcu/cpuデザイナーとコンパイラプログラマはほぼすべてのアプリケーションでこれを最適化しているので、ショートカットは基本的な算術演算子では非常に難しいです。

ビットシフト演算子を使用して乗算と除算を実行するために、すべてのサイクル/バイトが基本となる2つの点ですべてを維持することが一般的なショートカットの1つで、ビット単位と(&)モジュロを実行する。

例:除数がコンパイル時に知られている場合

unsigned int x = 100; 
unsigned int y1 = x << 4; // same as x * 2^4 = x*16 
unsigned int y2 = x >> 6; // same as x/2^6 = x/64 
unsigned int y3 = x & 0x07; // same as x % 8 
+3

正しいオペランドが2のべき乗定数である場合、まともなコンパイラは同じ最適化を行います。ビットシフト/マスキングのトリックは、コンパイラの最適化が吸い込まれた初期の段階から残されており、もはや必要ではありません。 –

+0

組み込みの世界では残念なことに、まともなコンパイラの贅沢を常に持っているわけではありません...一般的なケースでは同意しますが、疑義がある場合は、解体を素早くチェックすることでこれが役立つかどうかが決まります。 – shenles

1

は、動作は、いくつかの移行と、加算、および他の高速動作、逆数による乗算に変換することができます。現代のプロセッサであれば、たとえハードウェアで除算を実装していても、これは高速になります。組み込みターゲットは、除算/モジュロのためのルーチンが非常に最適化されています。

0

2のべき乗またはmulが他のもののためにシフトの組み合わせを加えると、シフトによって定数除算が達成できます。

http:// masm32.com/board/index.php?topic=9937.0には、最初の投稿からダウンロードしたx86アセンブリバージョンとCソースがあります。あなたのためにこのコードを生成します。

1

コードを慎重にプロファイリングして、モジュロ演算子が内側ループの主なコストであることが判明した場合は、最適化が役立ちます。あなたは(32ビット値のための)算術左シフトを使用して整数の符号を決定するためのトリックと既にお馴染みかもしれない:

sign = (x >> 31) | 1; 

これは単語を横切って符号ビットを拡張するので、負の値が得-1正値0を設定します。

モジュロ未満の量だけ値をインクリメントする場合、この同じトリックを使用して結果をラップすることができます。

val += inc; 
val -= modulo & (static_cast<int32_t>(((modulo - 1) - val)) >> 31); 

また、減少している場合以下モジュロ以上の値によって、関連するコードは次のとおりです。

int32_t signedVal = static_cast<int32_t>(val - dec); 
val = signedVal + (modulo & (signedVal >> 31)); 

私はのuint32_tに渡したので、私はstatic_castを演算子を追加しましたが、あなたは彼らが必要見つけない場合があります。

これは単純な%演算子とは対照的ですか?それはあなたのコンパイラとCPUアーキテクチャに依存します。 VS2012でコンパイルするとi3プロセッサでシンプルなループが60%高速に実行されていましたが、Raspberry PiのARM11チップではGCCでコンパイルすると20%の改善しか得られませんでした。

関連する問題