他にも良い説明がいくつかありますが、試してみてください。 (これはホワイトボードの方がはるかに簡単です!) ウィキペディアの例といくつかの表記法を次に示します。
20バイトをコピーしているとしましょう。最初のパスのプログラムのフロー制御は次のとおりです:
int count; // Set to 20
{
int n = (count + 7) / 8; // n is now 3. (The "while" is going
// to be run three times.)
switch (count % 8) { // The remainder is 4 (20 modulo 8) so
// jump to the case 4
case 0: // [skipped]
do { // [skipped]
*to = *from++; // [skipped]
case 7: *to = *from++; // [skipped]
case 6: *to = *from++; // [skipped]
case 5: *to = *from++; // [skipped]
case 4: *to = *from++; // Start here. Copy 1 byte (total 1)
case 3: *to = *from++; // Copy 1 byte (total 2)
case 2: *to = *from++; // Copy 1 byte (total 3)
case 1: *to = *from++; // Copy 1 byte (total 4)
} while (--n > 0); // N = 3 Reduce N by 1, then jump up
// to the "do" if it's still
} // greater than 0 (and it is)
}
次に、2 番目のパスを開始します。指定されたコードだけを実行します。
int count; //
{
int n = (count + 7) / 8; //
//
switch (count % 8) { //
//
case 0: //
do { // The while jumps to here.
*to = *from++; // Copy 1 byte (total 5)
case 7: *to = *from++; // Copy 1 byte (total 6)
case 6: *to = *from++; // Copy 1 byte (total 7)
case 5: *to = *from++; // Copy 1 byte (total 8)
case 4: *to = *from++; // Copy 1 byte (total 9)
case 3: *to = *from++; // Copy 1 byte (total 10)
case 2: *to = *from++; // Copy 1 byte (total 11)
case 1: *to = *from++; // Copy 1 byte (total 12)
} while (--n > 0); // N = 2 Reduce N by 1, then jump up
// to the "do" if it's still
} // greater than 0 (and it is)
}
次に、3 番目のパスを開始します。
int count; //
{
int n = (count + 7) / 8; //
//
switch (count % 8) { //
//
case 0: //
do { // The while jumps to here.
*to = *from++; // Copy 1 byte (total 13)
case 7: *to = *from++; // Copy 1 byte (total 14)
case 6: *to = *from++; // Copy 1 byte (total 15)
case 5: *to = *from++; // Copy 1 byte (total 16)
case 4: *to = *from++; // Copy 1 byte (total 17)
case 3: *to = *from++; // Copy 1 byte (total 18)
case 2: *to = *from++; // Copy 1 byte (total 19)
case 1: *to = *from++; // Copy 1 byte (total 20)
} while (--n > 0); // N = 1 Reduce N by 1, then jump up
// to the "do" if it's still
} // greater than 0 (and it's not, so bail)
} // continue here...
20 バイトがコピーされました。
注:元の Duff のデバイス (上に表示) は、to
で I/O デバイスにコピーされます 住所。したがって、ポインター *to
をインクリメントする必要はありませんでした。 . 2 つのメモリ バッファ間でコピーする場合は、 *to++
を使用する必要があります .
Dr. Dobb's Journal の説明は、このトピックに関して私が見つけた最高のものです。
これは私の AHA の瞬間です:
for (i = 0; i < len; ++i) {
HAL_IO_PORT = *pSource++;
}
になります:
int n = len / 8;
for (i = 0; i < n; ++i) {
HAL_IO_PORT = *pSource++;
HAL_IO_PORT = *pSource++;
HAL_IO_PORT = *pSource++;
HAL_IO_PORT = *pSource++;
HAL_IO_PORT = *pSource++;
HAL_IO_PORT = *pSource++;
HAL_IO_PORT = *pSource++;
HAL_IO_PORT = *pSource++;
}
n = len % 8;
for (i = 0; i < n; ++i) {
HAL_IO_PORT = *pSource++;
}
になります:
int n = (len + 8 - 1) / 8;
switch (len % 8) {
case 0: do { HAL_IO_PORT = *pSource++;
case 7: HAL_IO_PORT = *pSource++;
case 6: HAL_IO_PORT = *pSource++;
case 5: HAL_IO_PORT = *pSource++;
case 4: HAL_IO_PORT = *pSource++;
case 3: HAL_IO_PORT = *pSource++;
case 2: HAL_IO_PORT = *pSource++;
case 1: HAL_IO_PORT = *pSource++;
} while (--n > 0);
}
ダフのデバイスには 2 つの重要な要素があります。まず、これは理解しやすい部分だと思いますが、ループが展開されます。これは、ループが終了したかどうかを確認してループの先頭に戻ることに伴うオーバーヘッドの一部を回避することで、より大きなコード サイズと引き換えに速度を向上させます。 CPU は、ジャンプする代わりに直線的なコードを実行すると、より高速に実行できます。
2 つ目の側面は、switch ステートメントです。コードが途中にジャンプできるようにします ループの最初の時間。ほとんどの人にとって驚くべき部分は、そのようなことが許されているということです。まあ、それは許可されています。計算されたケース ラベルから実行が開始され、フォールスルー 他の switch ステートメントと同様に、連続する各代入ステートメントに適用されます。最後の case ラベルの後、実行はループの最後に到達し、その時点で最初にジャンプして戻ります。ループの上部は内側です これにより、スイッチは再評価されなくなります。
元のループは 8 回巻き戻されるため、反復回数は 8 で割られます。コピーするバイト数が 8 の倍数でない場合、いくつかのバイトが残っています。一度にバイトのブロックをコピーするほとんどのアルゴリズムは、残りのバイトを最後に処理しますが、Duff のデバイスはそれらを最初に処理します。この関数は count % 8
を計算します switch ステートメントで残りがどうなるかを判断し、そのバイト数の case ラベルにジャンプして、それらをコピーします。その後、ループは 8 バイトのグループをコピーし続けます。