9

1 ~ 256 バイトを処理するように設計された関数がいくつかあり、組み込み C プラットフォームで実行されます。バイトを渡すと、int を渡すよりもはるかに高速でコンパクトになります (1 つの命令と 3 つの命令)。

  1. int を受け入れ、ゼロの場合は早期終了し、それ以外の場合はカウント値の LSB を unsigned char にコピーし、それを do {} while(--count); で使用します。ループ (256 のパラメーター値は 0 に変換されますが、256 回実行されます)
  2. unsigned char を受け入れ、ゼロの場合は早期終了し、256 バイトの特別なバージョンの関数を用意します (これらのケースは事前にわかります)。
  3. unsigned char を受け入れ、ゼロの場合は 256 回実行します。
  4. 上記のような関数を用意しますが、(0-255) および (256 のみ) として動作するラッパー関数を介して呼び出します。
  5. 上記のような関数を用意しますが、(0-255) および (256 のみ) として動作するラッパー マクロを介して呼び出します。

システムがビジー状態の場合、関数の内側のループはおそらくプロセッサ実行時間の 15% ~ 30% を占めると予想されます。小さいバイト数に使用されることもあれば、大きいバイト数に使用されることもあります。関数で使用されるメモリ チップにはトランザクションごとのオーバーヘッドがあり、メモリ アクセス関数にトランザクション開始/処理実行/トランザクション終了シーケンスを内部的に実行させることを好みます。

最も効率的なコードは、単純に unsigned char を受け入れ、パラメーター値 0 を 256 バイトを実行する要求と見なし、0 バイトを誤って読み取ろうとする試みを回避するために呼び出し元に依存することです。ちょっと危なそうですけどね。組み込みシステムでこのような問題に対処した人はいますか? 彼らはどのように扱われましたか?

編集 プラットフォームは PIC18Fxx (128K コード空間; 3.5K RAM) で、SPI フラッシュ チップに接続されています。256 バイトを読み取ると、PIC の読み取りバッファーがオーバーランする可能性があります。0 ではなく 256 バイトを書き込むと、フラッシュ チップのデータが破損します。ビジー状態をチェックしない場合、PIC の SPI ポートは 12 命令ごとに 1 バイトに制限されます。実行すると遅くなります。通常の書き込みトランザクションでは、受信するデータに加えて 4 バイトを送信する必要があります。読み取りには、「SPI ターンアラウンド」のために追加のバイトが必要です (SPI ポートにアクセスする最速の方法は、次のバイトを送信する直前に最後のバイトを読み取ることです)。

コンパイラは HiTech PICC-18std です。

私は一般的に、HiTech の PICC-16 コンパイラを気に入っています。HiTech は、PICC-18std 製品から、コンパイル時間がさらに遅い PICC-18pro ラインにエネルギーをそらしたようで、2 バイト ポインターではなく 3 バイトの「const」ポインターを使用する必要があるようです。メモリ割り当てに関する独自のアイデア。PICC-18pro をもっと調べたほうがいいかもしれませんが、PICC-18pro の評価版でプロジェクトをコンパイルしようとしたとき、うまくいかず、正確な理由がわかりませんでした。私の asm ルーチン - 私は PICC-18std を使い続けました。

ちなみに、PICC-18 が特に do {} while(--bytevar); を好むことを発見しました。特に嫌いなのは do {} while(--intvar); です。コンパイラが後者を生成するとき、コンパイラの「心」はどうなっているのだろうか?

  行う
  {
    local_test++;
    --lpw;
  } while(lpw);

  2533 ;newflashpic.c: 792: やる
  2534 ;newflashpic.c: 793: {
  2535 0144A8 2AD9 incf fsr2l、f、c
  2536 ;newflashpic.c: 795: } while(--lpw);
  2537 0144AA 0E00 movlw 低?_var_test
  2538 0144AC 6EE9 movwf fsr0l,c
  2539 0144AE 0E01 movlw 高?_var_test
  2540 0144B0 6EEA movwf fsr0h,c
  2541 0144B2 06EE decf postinc0,f,c
  2542 0144B4 0E00 movlw 0
  2543 0144B6 5AED subwfb postdec0、f、c
  2544 0144B8 50EE movf postinc0,w,c
  2545 0144BA 10ED iorwf postdec0,w,c
  2546 0144BC E1F5 bnz l242

コンパイラは、LFSR 命令 (2 ワードかかる) を使用するのではなく、MOVLW/MOVWF (4 ワードかかる) の組み合わせを使用して、変数へのポインターをロードします。次に、このポインターを使用してデクリメントと比較を行います。認めますが、do{}while(--wordvar); while(wordvar--); ほど優れたコードを生成することはできません。{} コードは、後者の形式が実際に生成するものよりも優れています。別のデクリメントと while テスト (たとえば while (--lpw,lpw)) を実行すると、適切なコードが生成されますが、少し見苦しく見えます。ポスト デクリメント演算子は、ダウン カウント ループに最適なコードを生成できます。

  decf_lpw
  btfss _STATUS,0 ; キャリーの場合は次のインスタンスをスキップします (つまり、ゼロではありませんでした)
   decf _lpw+1
  bcループ; lpw がゼロの場合にのみ、キャリーがクリアされます

しかし、代わりに --lpw より悪いコードを生成します。最適なコードは、アップカウント ループ用です。

  infsnz_lpw
   incfsz _lpw+1
   ブラループ

しかし、コンパイラはそれを生成しません。

編集 2 私が使用する可能性のある別のアプローチ: バイト数にグローバル 16 ビット変数を割り当て、終了前にカウンターが常にゼロになるように関数を記述します。次に、8 ビット値のみが必要な場合は、8 ビットをロードするだけで済みます。最高の効率を得るために微調整できるように、マクロを使用します。PIC では、0 であることがわかっている変数に対して |= を使用すると、= を使用するより遅くなることはなく、場合によっては高速になります。たとえば、intvar |= 15 または intvar |= 0x300 は 2 つの命令になります (どちらの場合も、結果の 1 バイトだけを処理する必要があり、他のバイトは無視できます)。intvar |= 4 (または任意の 2 の累乗) は 1 つの命令です。明らかに、他のプロセッサでは、intvar = 0x300 の方が intvar |= 0x300; よりも高速です。マクロを使用する場合は、必要に応じて微調整できます。

4

3 に答える 3

2

内部関数はcount + 1バイトをコピーする必要があります。

 do /* copy one byte */ while(count-- != 0);

ポストデクリメントが遅い場合、他の代替手段は次のとおりです。

 ... /* copy one byte */
 while (count != 0) { /* copy one byte */; count -= 1; }

また

 for (;;) { /* copy one byte */; if (count == 0) break; count -= 1; }

呼び出し元/ラッパーは次のことができます。

if (count > 0 && count <= 256) inner((uint8_t)(count-1))

また

if (((unsigned )(count - 1)) < 256u) inner((uint8_t)(count-1))

コンパイラの方が速い場合。

于 2010-08-19T16:28:32.637 に答える
0

intパラメーターのコストが3命令で、charパラメーターのコストが1の場合、欠落している余分な1ビットに対して追加のcharパラメーターを渡すことができます。あなたの(おそらく16ビットの)intが8ビットのcharの2倍以上の命令を受け取るのはかなりばかげているようです。

于 2010-08-19T17:49:53.193 に答える
0

FWIW、オプション#1のいくつかのバリアントを選択します。関数のインターフェイスは、適切で直感的であり、誤って呼び出される可能性が低いようです (256 より大きい値が渡された場合に何をしたいのかを考えてください。デバッグ ビルドのみのアサーションが適切な場合があります)。

8ビットカウンターを使用して正しい回数ループするためのマイナーな「ハック」/マイクロ最適化が実際にメンテナンスの問題になるとは思いません。それを正当化するためにかなりの分析を行ったようです。

誰かがラッパーを好むのであれば、ラッパーに反対するつもりはありませんが、個人的にはオプション 1 にやや傾いています。

ただし、パブリック インターフェイスでは、呼び出し元が読み取りたい値よりも 1 小さい値を渡す必要があることに反対します。

于 2010-08-19T18:20:13.597 に答える