コンピュータ プログラミング において、サンクは、別のサブルーチンに計算を挿入するために使用されるサブルーチンです。サンクは主に、計算の結果が必要になるまで計算を遅らせたり、他のサブルーチンの先頭または末尾に操作を挿入したりするために使用されます。コンパイラ コード生成やモジュール プログラミングでは、他にも多くの用途があります。
この用語は、動詞thinkの気まぐれな不規則形として生まれました。これは、 ALGOL 60コンパイラーでのサンクの元々の使用法を指し、どのようなタイプのルーチンを生成するかを決定するために特別な分析 (thought) が必要でした。[1] [2]
背景
コンパイラ研究の初期には、さまざまな評価戦略の幅広い実験が行われました。重要な問題は、引数が定数ではなく任意の数式である場合にサブルーチン呼び出しをコンパイルする方法でした。「値による呼び出し」と呼ばれる 1 つのアプローチでは、呼び出しの前にすべての引数を計算し、結果の値をサブルーチンに渡します。もう 1 つの「名前による呼び出し」アプローチでは、サブルーチンは評価されていない引数式を受け取り、それを評価する必要があります。
「名前による呼び出し」の単純な実装では、サブルーチン内の対応するパラメータが出現するたびに、引数式のコードが置き換えられるが、これによりサブルーチンの複数のバージョンと式コードの複数のコピーが生成される可能性がある。改善策として、コンパイラは引数の値を計算するthunkと呼ばれるヘルパー サブルーチンを生成できる。このヘルパー サブルーチンのアドレスと環境[a]は、元の引数の代わりに元のサブルーチンに渡され、必要に応じて何度でも呼び出すことができる。ピーター インガーマンは、名前による呼び出し評価をサポートする ALGOL 60 プログラミング言語を参照して、最初に thunk について説明した。[4]
アプリケーション
関数型プログラミング
ソフトウェア業界では値渡しと参照渡しの評価がほぼ標準化されていましたが、[5]関数型プログラミングコミュニティでは名前渡しの評価の積極的な研究が続けられていました。この研究により、名前渡しの何らかのバリエーションが標準的な評価戦略となっている一連の遅延評価プログラミング言語が生まれました。これらの言語のコンパイラ、たとえばGlasgow Haskell Compiler はサンクに大きく依存しており、サンクが初期結果を保存するという追加機能により、再計算を回避できます。[6]これはメモ化または必要に応じた呼び出しとして知られています。
関数型プログラミング言語では、プログラマが明示的にサンクを生成することもできます。これは、ソースコードで、引数式を、それ自身のパラメータを持たない匿名関数でラップすることによって行われます。これにより、受け取り関数が匿名関数を呼び出すまで式が評価されなくなり、名前による呼び出しと同じ効果が得られます。[7]他のプログラミング言語に匿名関数が採用されたことで、この機能は広く利用できるようになりました。
オブジェクト指向プログラミング
サンクは、クラスが複数のインターフェースを継承できるオブジェクト指向プログラミングプラットフォームで役立ち、同じメソッドが複数のインターフェースのいずれかを介して呼び出される状況につながります。次のコードは、 C++でのこのような状況を示しています。
クラスA { public : virtual int Access () const {戻り値_ ; }
プライベート:
int value_ ; };
クラスB { public : virtual int Access () const {戻り値_ ; }
プライベート:
int value_ ; };
クラスC : public A 、public B { public : int Access () const override { return better_value_ ; }
プライベート:
int better_value_ ; };
int use ( B * b ) { return b -> Access (); }
int main () { // ... B some_b ; use ( & some_b ); C some_c ; use ( & some_c ); }
この例では、クラス A、B、C のそれぞれに対して生成されたコードには、同じ型の参照を介してその型のオブジェクトを呼び出すために使用できるディスパッチ テーブルが含まれます。クラス C には、型 B の参照を介して型 C のオブジェクトを呼び出すために使用される追加のディスパッチ テーブルがあります。式は、b が参照するオブジェクトの型に応じて、B 独自のディスパッチ テーブルまたは追加の C テーブルを使用します。型 C のオブジェクトを参照する場合、コンパイラは、C の実装が、そのオブジェクトの継承された B 部分ではなく、Cオブジェクト全体のインスタンス アドレスを受け取るようにする必要があります。[8]AccessAccessb->Access()Access
このポインタ調整の問題への直接的なアプローチとして、コンパイラは各ディスパッチ テーブル エントリに整数オフセットを含めることができます。このオフセットは、参照のアドレスとメソッド実装に必要なアドレスの差です。これらのディスパッチ テーブルを介して各呼び出しに対して生成されたコードは、オフセットを取得し、メソッドを呼び出す前にそれを使用してインスタンス アドレスを調整する必要があります。
先ほど説明した解決策には、前述の名前による呼び出しの単純な実装と同様の問題があります。コンパイラは、引数 (インスタンス アドレス) を計算するコードのコピーを複数生成し、同時に、オフセットを保持するためにディスパッチ テーブルのサイズも増加します。別の方法として、コンパイラは、C の実装とともに調整サンクAccessを生成し、インスタンス アドレスを必要な量だけ調整してからメソッドを呼び出すことができます。サンクは、C の B のディスパッチ テーブルに出現できるため、呼び出し側がアドレスを自分で調整する必要がなくなります。[9]
相互運用性
サンクは、ルーチンが直接互いを呼び出すことができないソフトウェア モジュール間の相互運用性を実現するために広く使用されています。これは、ルーチンの呼び出し規約が異なる、異なるCPU モードまたはアドレス空間で実行される、または少なくとも 1 つが仮想マシンで実行されるために発生する可能性があります。コンパイラ (またはその他のツール) は、引数の変換、別の場所へのコピー、CPU モードの切り替えなど、ターゲット ルーチンの呼び出しに必要な追加の手順を自動化するサンクを生成することで、この問題を解決できます。サンクが成功すると、通常の呼び出しと比較して、呼び出し側が行う必要のある余分な作業が最小限に抑えられます。
相互運用性サンクに関する文献の多くは、MS-DOS、OS/2、[10] Windows [11] [12] [13] [14] 、. NETなどのさまざまなWintelプラットフォーム、および16ビットから32ビットのメモリアドレッシングへの移行に関連しています。顧客がプラットフォーム間を移行するにつれて、サンクは古いプラットフォーム用に書かれたレガシーソフトウェアをサポートするために不可欠になってきました。
x86 上の 32 ビット コードから 64 ビット コードへの移行でも、サンク形式が使用されます ( WoW64 )。ただし、x86-64 のアドレス空間は 32 ビット コードで使用できるものよりも大きいため、古い「汎用サンク」メカニズムを使用して 32 ビット コードから 64 ビット コードを呼び出すことはできませんでした。[15] 32 ビット コードが 64 ビット コードを呼び出す唯一のケースは、WoW64 が Windows API を 32 ビットにサンクする場合です。
オーバーレイと動的リンク
自動仮想メモリハードウェアがないシステムでは、サンクはオーバーレイと呼ばれる限定的な仮想メモリを実装できます。オーバーレイを使用すると、開発者はプログラムのコードを個別にロードおよびアンロードできるセグメントに分割し、各セグメントへのエントリポイントを識別します。別のセグメントを呼び出すセグメントは、ブランチテーブルを介して間接的に呼び出す必要があります。セグメントがメモリ内にある場合、そのブランチテーブルのエントリはセグメントにジャンプします。セグメントがアンロードされると、そのエントリは、要求に応じて再ロードできる「リロードサンク」に置き換えられます。[16]
同様に、実行時にプログラムのモジュールを動的にリンクするシステムでは、モジュールを接続するためにサンクを使用できます。各モジュールは、リンカーがモジュールをロードするときに埋め込むサンクのテーブルを介して他のモジュールを呼び出すことができます。この方法により、モジュールはメモリ内のどこに配置されているかを事前に知らなくても相互作用できます。[17]
参照
サンクテクノロジー
- DOS 保護モード インターフェース(DPMI)
- DOS 保護モード サービス(DPMS)
- J/ダイレクト
- Unicode 用 Microsoft レイヤー
- プラットフォーム呼び出しサービス
- Win32s
- Windows 上の Windows
- ウォーワールド64
- libffi
関連概念
注記
- ^サンクは クロージャの初期の限定型です。サンクに渡される環境は、呼び出されたルーチンの環境ではなく、式の環境です。[3]
参考文献
- ^ エリック・レイモンドは、「この用語の起源について広まっているいくつかの擬音語の神話」を否定し、サンクの発明者が「この用語は、(何時間もの議論の末に)Algol-60の引数の型はコンパイル時に少し考えれば事前にわかることに気付いた後に造られた」と回想していることを引用している [...] 言い換えれば、「すでに考えられていた」ため、サンクと名付けられた。これは「午前2時に考える」の過去形である。参照:レイモンド、エリック・S. (1996)。レイモンド、エリック・S. (編)。『The New Hacker's Dictionary』。MIT Press。p. 445。ISBN 9780262680929. 2015年5月25日閲覧。
- ^ Ingerman (1961) を参照: 「トランスレータは、実際のパラメータの構成と以前にスキャンされた宣言を考慮して、作成するサンクの種類を認識します。… プロシージャ宣言がコンパイルされるとき、トランスレータは、構文を観察することによって、サンクから期待されるアドレスの種類を認識します。」
- ^ ET Irons (1961-01-01). 「ALGOL の再帰的手順とブロックの実装に関するコメント」Communications of the ACM . 4 (1). Association for Computing Machinery (ACM): 65– 69. doi : 10.1145/366062.366084 . ISSN 0001-0782. S2CID 14646332.
- ^ Ingerman, PZ (1961-01-01). 「サンク: プロシージャ宣言に関するコメント付きのプロシージャ ステートメントのコンパイル方法」Communications of the ACM . 4 (1). Association for Computing Machinery (ACM): 55– 58. doi : 10.1145/366062.366084 . ISSN 0001-0782. S2CID 14646332.
- ^ スコット、マイケル (2009).プログラミング言語語用論. p. 395.
- ^ Marlow, Simon (2013). Haskell での並列および並行プログラミング. p. 10.
- ^ Queinnec, Christian (2003). Lisp in Small Pieces . p. 176.
- ^ Stroustrup, Bjarne (1989 年秋)。「C++ の多重継承」(PDF)。コンピューティング システム。1 (4) 。USENIX。2014年 8 月 4日閲覧。
- ^ Driesen, Karel; Hölzle, Urs (1996). 「C++ における仮想関数呼び出しの直接コスト」(PDF)。1996 ACM SIGPLAN 会議オブジェクト指向プログラミング システム、言語、アプリケーションに関する議事録、OOPSLA 1996、カリフォルニア州サンノゼ、米国、1996 年 10 月 6 ~ 10 日。第 11 回 OOPSLA 1996: カリフォルニア州サンノゼ、米国。ACM。ISBN 0-89791-788-X. 2019年12月29日時点のオリジナル(PDF)からアーカイブ。2011年2月24日閲覧。[リンク切れ ]
- ^ Calcote, John (1995 年 5 月). 「Thunking: OS/2 2.0 での 16 ビット ライブラリの使用」. OS/2 Developer Magazine . 7 (3): 48– 56.
- ^ キング、エイドリアン (1994)。Inside Microsoft Windows 95 (第 2 版)。ワシントン州レドモンド、Microsoft Press。ISBN 1-55615-626-X。
- ^ 『 Microsoft Windows 95 プログラマーズ ガイド: Microsoft Windows 開発チームによる Windows プログラミングの主要トピック (第 1 版)』。ワシントン州レドモンド、米国: Microsoft Press。1995-07-01。ISBN 1-55615-834-3. 2016年5月26日閲覧。
{{cite book}}:|work=無視されました (ヘルプ) - ^ Hazzah, Karen (1997). Writing Windows VxDs and Device Drivers - Programming Secrets for Virtual Device Drivers (第 2 刷、第 2 版). Lawrence, Kansas, USA: R&D Books / Miller Freeman, Inc. ISBN 0-87930-438-3。
- ^ Kauler, Barry (1997 年 8 月)。Windowsアセンブリ言語とシステム プログラミング - PC と Windows 用の 16 ビットおよび 32 ビット低レベル プログラミング(第 2 版)。ローレンス、カンザス州、米国: R&D Books / Miller Freeman, Inc. ISBN 0-87930-474-X。
- ^ 「なぜ 32 ビット Windows と 64 ビット Windows 間で thunk できないのか?」The Old New Thing . 2008-10-20。
- ^ Bright, Walter (1990-07-01). 「640K DOS の仮想メモリ」. Dr. Dobb's Journal . 2014-03-06閲覧。
- ^ Levine, John R. (2000) [ 1999年 10 月]。リンカーとローダー。Morgan Kaufmann ソフトウェア エンジニアリングおよびプログラミング シリーズ (第 1 版)。サンフランシスコ、米国: Morgan Kaufmann。ISBN 1-55860-496-0. OCLC 42413382. 2012年12月5日にオリジナルからアーカイブ。2020年1月12日閲覧。コード: [1][2] エラッタ: [3]
