Thunk - Thunk

I computerprogrammering er en thunk en underrutine, der bruges til at injicere en beregning i en anden underprogram. Thunks bruges primært til at forsinke en beregning, indtil dens resultat er nødvendigt, eller til at indsætte operationer i begyndelsen eller slutningen af ​​den anden underprogram. De har mange andre applikationer inden for generering af compiler -kode og modulær programmering .

Udtrykket opstod som en finurlig uregelmæssig form for "tænk". Det refererer til den oprindelige brug af thunks i ALGOL -kompilatorer, som krævede særlig analyse (tanke) for at afgøre, hvilken type rutine der skulle genereres.

Baggrund

De første år med kompilatorforskning oplevede bred eksperimentering med forskellige evalueringsstrategier . Et centralt spørgsmål var, hvordan man udarbejder et underprogramopkald, hvis argumenterne kan være vilkårlige matematiske udtryk frem for konstanter. En tilgang, kendt som " opkald efter værdi ", beregner alle argumenterne før opkaldet og sender derefter de resulterende værdier til underprogrammet. I den rivaliserende " call by name " tilgang modtager underrutinen det uvurderede argumentudtryk og skal evaluere det.

En simpel implementering af "opkald ved navn" kan erstatte koden for et argumentudtryk for hvert udseende af den tilsvarende parameter i underrutinen, men dette kan producere flere versioner af underrutinen og flere kopier af udtrykkskoden. Som en forbedring kan kompilatoren generere en hjælpesubrutine, kaldet en thunk , der beregner argumentets værdi. Adressen og miljøet for denne hjælper -underrutine overføres derefter til den originale underprogram i stedet for det originale argument, hvor den kan kaldes så mange gange som nødvendigt. Peter Ingerman beskrev først thunks med henvisning til programmeringssproget ALGOL 60 , som understøtter evaluering af opkald ved navn.

Ansøgninger

Funktionel programmering

Selvom softwareindustrien stort set standardiserede call-by-value og call-by-reference evaluering, fortsatte aktiv undersøgelse af call-by-name i det funktionelle programmeringsfællesskab . Denne forskning producerede en række dovne evalueringsprogrammeringssprog, hvor en eller anden variant af call-by-name er standardevalueringsstrategien. Kompilatorer til disse sprog, såsom Glasgow Haskell Compiler , har i høj grad stolet på thunks, med den ekstra funktion, at thunksne gemmer deres første resultat, så de kan undgå at genberegne det; dette er kendt som memoization eller call-by-need .

Funktionelle programmeringssprog har også givet programmerere mulighed for eksplicit at generere thunks. Dette gøres i kildekoden ved at indpakke et argumentudtryk i en anonym funktion, der ikke har egne parametre. Dette forhindrer udtrykket i at blive evalueret, indtil en modtagende funktion kalder den anonyme funktion og derved opnår den samme effekt som opkald ved navn. Vedtagelsen af ​​anonyme funktioner til andre programmeringssprog har gjort denne mulighed bredt tilgængelig.

Følgende er en simpel demonstration i JavaScript (ES6):

// 'hypot' is a binary function
const hypot = (x, y) => Math.sqrt(x * x + y * y);

// 'thunk' is a function that takes no arguments and, when invoked, performs a potentially expensive
// operation (computing a square root, in this example) and/or causes some side-effect to occur
const thunk = () => hypot(3, 4);

// the thunk can then be passed around without being evaluated...
doSomethingWithThunk(thunk);

// ...or evaluated
thunk(); // === 5

Objektorienteret programmering

Thunks er nyttige i objektorienterede programmeringsplatforme , der tillader en klasse at arve flere grænseflader , hvilket fører til situationer, hvor den samme metode kan kaldes via en af ​​flere grænseflader. Den følgende kode illustrerer en sådan situation i C ++ .

class A {
 public:
  virtual int Access() const { return value_; }

 private:
  int value_;
};

class B {
 public:
  virtual int Access() const { return value_; }

 private:
  int value_;
};

class C : public A, public B {
 public:
  int Access() const override { return better_value_; }

 private:
  int better_value_;
};

int use(B *b) { return b->Access(); }

int main() {
  // ...
  B some_b;
  use(&some_b);
  C some_c;
  use(&some_c);
}

I dette eksempel vil koden, der genereres for hver af klasserne A, B og C, indeholde en forsendelsestabel, der kan bruges til at påkalde Accesset objekt af denne type via en reference, der har samme type. Klasse C vil have en ekstra forsendelsestabel, der bruges til at påkalde Accesset objekt af type C via en reference af type B. Udtrykket b->Access()vil bruge Bs egen forsendelsestabel eller den ekstra C -tabel, afhængigt af typen af ​​objekt b henviser til. Hvis det refererer til et objekt af type C, skal kompilatoren sikre, at C's Accessimplementering modtager en forekomstadresse for hele C -objektet frem for den arvede B -del af objektet.

Som en direkte tilgang til dette pointerjusteringsproblem kan kompilatoren inkludere et heltal offset i hver forsendelsestabelindgang. Denne forskydning er forskellen mellem referenceens adresse og den adresse, der kræves af metodeimplementeringen. Koden, der genereres for hvert opkald gennem disse forsendelsestabeller, skal derefter hente forskydningen og bruge den til at justere forekomstadressen, før den kalder metoden.

Den netop beskrevne løsning har problemer, der ligner den naive implementering af opkald ved navn, der er beskrevet tidligere: Compileren genererer flere kopier af kode for at beregne et argument (forekomstadressen), samtidig med at størrelsen på afsendelsestabellen øges for at holde forskydningerne. Som et alternativ kan kompilatoren generere en justerings -tunk sammen med C's implementering af den, Accessder justerer forekomstadressen med det nødvendige beløb og derefter kalder metoden. Thunken kan vises i C's ekspeditionstabel for B, hvilket eliminerer behovet for opkaldere til selv at justere adressen.

Numeriske beregninger, der kræver evalueringer på flere punkter

Rutiner til beregninger såsom integration skal beregne et udtryk på flere punkter. Opkald ved navn blev brugt til dette formål på sprog, der ikke understøttede lukninger eller procedureparametre .

Interoperabilitet

Thunks er blevet udbredt til at give interoperabilitet mellem softwaremoduler, hvis rutiner ikke kan kalde hinanden direkte. Dette kan forekomme, fordi rutinerne har forskellige opkaldskonventioner , kører i forskellige CPU -tilstande eller adresserum , eller mindst en kører i en virtuel maskine . En kompilator (eller et andet værktøj) kan løse dette problem ved at generere en thunk, der automatiserer de yderligere trin, der er nødvendige for at kalde målrutinen, uanset om det er at transformere argumenter, kopiere dem til et andet sted eller skifte CPU -tilstand. En vellykket thunk minimerer det ekstra arbejde, den, der ringer, skal udføre i forhold til et normalt opkald.

Meget af litteraturen om interoperabilitetstunker vedrører forskellige Wintel- platforme, herunder MS-DOS , OS/2 , Windows og .NET , og til overgangen fra 16-bit til 32-bit hukommelsesadressering. Da kunderne er migreret fra en platform til en anden, har thunks været afgørende for at understøtte ældre software skrevet til de ældre platforme.

Overgangen fra 32-bit til 64-bit kode på x86 bruger også en form for thunking (WoW64). Fordi x86-64-adresserummet er større end det, der er tilgængeligt for 32-bit kode, kunne den gamle "generiske thunk" -mekanisme ikke bruges til at kalde 64-bit kode fra 32-bit kode. Det eneste tilfælde med 32-bit kode, der kalder 64-bit kode, er i WoW64's thunking af Windows API'er til 32-bit.

Overlejringer og dynamisk sammenkædning

På systemer, der mangler automatisk virtuel hukommelseshardware , kan thunks implementere en begrænset form for virtuel hukommelse kendt som overlays . Med overlays opdeler en udvikler et programs kode i segmenter, der kan indlæses og aflæses uafhængigt, og identificerer indgangspunkterne i hvert segment. Et segment, der kalder ind i et andet segment, skal gøre det indirekte via en filialtabel . Når et segment er i hukommelsen, springer dets grenbordsposter ind i segmentet. Når et segment er losset, erstattes dets poster med "reload thunks", der kan genindlæse det efter behov.

På samme måde kan systemer, der dynamisk forbinder moduler i et program sammen i løbetid, bruge thunks til at forbinde modulerne. Hvert modul kan kalde de andre gennem en tabel med tunker, som linkeren udfylder, når det indlæser modulet. På denne måde kan modulerne interagere uden forudgående viden om, hvor de er placeret i hukommelsen.

Se også

Thunk -teknologier

Relaterede koncepter

Noter

Referencer