A Duff programozási eszköze a szekvenciális másolás optimalizált megvalósítása, ugyanazt a technikát alkalmazva, mint a hurok feltekercselésénél . Az első leírást 1983 novemberében Tom Duff ( eng. Tom Duff ) készítette, aki akkor a Lucasfilmnél dolgozott . Talán ez a legszokatlanabb felhasználása annak a ténynek, hogy a C nyelvben a blokkon belüli utasítások végrehajtása az összes címkén keresztül történik . switchcase
Meg kell jegyezni, hogy Duff nem állítja magát a hurok feloldás fogalmának felfedezésére, csak annak sajátos kifejezése van a C nyelvben.
A modern megoldásokban a Duff-módszer használatának hasznossága kétséges. Ez akadályozza a fordítók és az elágazás optimalizálásának munkáját. [1] Például miután eltávolította a Duff kódot az XFree86 4.0-s verziójából (2000), a binárisok száma körülbelül 0,5 MB-tal csökkent, és a szerver gyorsabban kezdett betölteni. [2]
A szekvenciális másolás hagyományos módja (Duff felfedezése előtt) így nézett ki:
do { /* Tegyük fel, hogy a szám > 0 */ * -tól = * -ig ++ ; /* Itt a mutató nem növekszik */ } while ( -- count > 0 );Ebben a példában to nem növekszik, mert Duff egyetlen memórialeképezett I/O regiszterbe másolt. A modern C-ben a változó definícióját toa kulcsszóval kell alátámasztani volatile.
A kód optimalizálása közben Duff rájött, hogy a hurok "letekercselt" változata megvalósítható a switch és a do / while konstrukciók átfedésével .
strcpy ( to , from , count ) regisztráció char * to , * from ; regiszterszám ; _ { regiszter n = ( szám + 7 ) / 8 ; if ( ! count ) return ; kapcsoló ( számlálás % 8 ) { 0. eset : do { * to = * from ++ ; 7. eset : * -tól = * -ig ++ ; 6. eset : * -tól = * -ig ++ ; 5. eset : * -tól = * -ig ++ ; 4. eset : * -tól = * -ig ++ ; 3. eset : * -tól = * -ig ++ ; 2. eset : * -tól = * -ig ++ ; 1. eset : * -tól = * -ig ++ ; } while ( -- n > 0 ); } } Magyarázatok példáulMaga az algoritmus korábban is széles körben ismert volt: az assemblerben programokat fejlesztő programozók ezzel csökkentették az összehasonlítások és elágazások számát másoláskor.
A Duff-módszer C nyelvben való megvalósítása viszont első pillantásra szokatlannak tűnik. Ez a kód azonban a C nyelv leírásának és szabványának megfelelően van megírva; A kapcsolószerkezet specifikációja lehetővé teszi a következő felhasználást:
E két tény kombinációja a fenti kódot az egymást követő címekről ( *from ) a leképezett kimeneti portra ( *to ) másolja a megadott számú alkalommal ( count [3] ).
Duff eredeti módszere egy I/O regiszterbe másolás volt. Ahhoz, hogy egyszerűen másolhasson egy memóriadarabot egyik helyről a másikra, automatikus növekedést kell hozzáadnia a következőhöz :
* -tól ++- ig = * ++- tól ;Duff módszerét ebben a formában gyakorlatként adjuk meg Bjorn Stroustrup The C++ programozási nyelvében [4] . Nyilvánvalóan a változás annak köszönhető, hogy a szerző nem várja el egy kezdő programozótól, hogy ismerje az I / O regisztereket. Ennek az opciónak nincs gyakorlati alkalmazása, mivel a C nyelv szabványos könyvtárában már van egy memóriaterület másolási funkciója ( memcpy).
A véges állapotú gépek programozók általi közvetlen használata gyakran nehézkes, mert a választott programozási nyelv nem teszi lehetővé a gép állapotának és átmeneteinek egyértelmű és egyszerű kódbeli ábrázolását (lásd a példákat az " Automatikus programozás " című cikkben).
Az egyik sikeres opciót Simon Tatham javasolta [5] , és ez egy implicit véges automata megvalósításának módja a Duff-módszerrel. Tatham viszont az állapotgépeket használta a korutinok megvalósítására , amelyek lehetővé tették számára a termelő-fogyasztó feladat végrehajtását a többszálú feldolgozás és az azzal járó problémák nélkül.
Többek között ugyanezt a megközelítést alkalmazta Adam Dunkels [ " protothreads " könyvtárában [6] , amely a pszeudo-többszálú feldolgozás implicit állapotú gépek segítségével történő megvalósításának különböző módjaival foglalkozik.
A javasolt megközelítés abból áll, hogy egy véges állapotú gépet részekre bontva, több C makrót használva. Az egyszerűsített makrók így néznek ki:
#define DECLARE() belső állapot #define INIT() állapot = 0 #define BEGIN kapcsoló (állapot) { \ case 0: #define YIELD(érték) do { \ state = __LINE__; \ visszatérési érték; \ eset __LINE__: \ ; \ } míg (0) #define VÉGE}Használati példa (C++ nyelven):
#include <iostream> névtér használata std ; osztály gép { NYILATKOZAT (); nyilvános : gép () { INIT (); } const char * következő () { BEGIN ; HOZAM ( "mama" ); HOZAM ( "szappan" ); YIELD ( "keret" ); VÉGE ; return NULL ; } }; int main () { gép m ; while ( const char * val = m . next ()) { cout << val << ' ' ; } return 0 ; }Ez a program kiírja az "anya mosta a keretet", minden egyes szót külön állapotgép lépéssel generálva.