リエントラント・プログラムでは、書き込みを行うレジスター保管域や作業域はプログラムの外に確保するためGETMAINとFREEMAINマクロを利用します。しかし、頻繁に呼び出されるモジュールでは、呼び出しの度にこれらの領域の獲得と解放を繰り返すのはパフォーマンスの点で劣ります。そこで、このような場合は作業に使用する領域をまとめて確保しておき、必要の都度そこから切り出して使う方法が行われます。まとめて確保した領域をプール(POOL)、切り出す領域をセル(CELL)と呼びます。セルには固定長と可変長の両方の構造があります。可変長セルではストレージの利用効率は上がりますが実現方法はむずかしいです。ここでは、固定長のセルを使ったストレージ・プールの利用を解説します。
CPOOLマクロでストレージ・プールを作る
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
----+----1----+----2----+----3----+----4----+----5----+----6----+----7-- CPOOL BUILD, BUILD LOCAL WORKAREA POOL + PCELLCT=64, PRIMARY CELL = 64 ENTRIES + SCELLCT=8, SECONDARY CELL = 8 ENTRIES + CSIZE=1024, CELL SIZE + LOC=(BELOW,ANY), LOCATION IS BELOW + HDR='SUB-ROUTINES LOCAL WORKAREA POOL' ST R0,POOLID SAVE CELL POOL ID : : POOLID DC A(0) CELL POOL ID CPOOL DELETE, DELETE LOCAL WORKAREA POOL + CPID=POOLID |
CPOOL BUILDマクロはセル・プールの作成を行い、CPOOL DELETEマクロはセル・プールの削除を行います。このサンプルでは、1KBの固定長セル64個分のプールを16MB未満のリージョンに作成します。作成されたプールの識別子がGR0に返ります。この識別子(セル・プールID)は、以降のGET、FREE及びDELETEの各サービスで使用します。64個以上のセルが要求された場合は、8個単位でプールが拡張されます(*1)。
CPOOLマクロでセルを切り出す
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
----+----1----+----2----+----3----+----4----+----5----+----6----+----7-- 呼び出すプログラム L R0,POOLID GR0 <-- OUR POOL ID LA R1,PLIST GR1 <-- PARAMETER LIST L RF,=V(SUB1) LOAD SUB ROUTINE ENTRY BASR RE,RF CALL IT : : 呼び出されるプログラム SUB1 CSECT , DEFINE CONTROL SECTION USING *,RC DEFINE BASE REGISTER STM RE,RC,12(RD) SAVE CALLER REGISTERS LR RC,RF GR12 -> OUR 1ST BASE ADDRESS + GR0 --> CELLPOOL ID + GR1 --> PARAMETER LIST SPACE , CPOOL GET,UNCOND, GET LOCAL WORKAREA FROM CPOOL + CPID=(0), CPOOL ID IN GR0 + REGS=USE ST RD,4(,R1) SAVE CALLER SAVEAREA ST R1,8(,RD) CHAIN OUR SAVEAREA LR RD,R1 GR13 -> OUR SAVEAREA AND + LOCAL WORKAREA(1024BYTES) USING DWORK,RD ADDRESS IT : : : : : LR R2,RD GR2 --> OUR LOCAL WORKAREA L RD,4(,RD) LOAD CALLER SAVEAREA ST RF,16(,RD) PASS RETURN CODE IN GR15 L R0,20(,RD) LOAD CELLPOOL ID CPOOL FREE, BACK USED CELL TO OUR CPOOL + CPID=(0), + CELL=(2), + REGS=USE LM RE,RC,12(RD) RESTORE CALLER GPRS BR RE RETURN TO CALLER SPACE , DWORK DSECT , OUR LOCAL WORKAREA MAP DC 18F'0' OUR GPR SAVEAREA DOUBLE DC D'0' DOUBLE WORD WORKAREA WORKAREA DC XL128'00' 128BYTES WORKAREA |
セル・プールは、サブルーチンを呼び出す親プログラムがあらかじめ作成しておくものとします。サンプルでは親プログラムはサブルーチンを呼び出す時、GR0にセル・プールIDを、GR1にパラメーター・リストを設定しています。呼び出されたサブルーチンは、プログラムの先頭でCPOOL GETマクロを使ってプールからセルを切り出します。切り出されるセルの長さは、親プログラムがプール作成時に定義します。レジスター保管域の目的なら72バイトあればいいのですが、リエントラント・プログラムでは作業域も必要になりますからそれらの長さも考慮して適当な大きさのセルを定義する方がいいでしょう。サンプルではレジスター保管域の後ろにプログラム作業域をDSECTでマッピングしています。GETMAINの代わりにCPOOL GETを使うと考えればいいでしょう。
呼び出し元プログラムへ復帰する際に切り出したセルを返却します。セル・プールIDは、入口点で保管したGR0を復元して指定しています。返却するセルのアドレスはGR2に設定しています。セル返却前に、呼び出し元のレジスター保管域アドレスをGR13にロードしておきます。こちらも、FREEMAINの代わりにCPOOL FREEを使うと考えればいいでしょう。
プールを各サブルーチンで作成する方法も考えられますが、プールIDをどこに保管するか?という問題がありますので、サンプルのように親プログラムでプールを作成する方が簡単です。なお、サンプルのような使い方では影響ありませんが、GETではGR2~4が、FREEではGR2~3が破壊されます。REGS=SAVEパラメーターを指定すれば内容は保証されますが、GR13がレジスター保管域をポイントしていなければなりません。したがって、サンプルのようにリエントラント・プログラム用レジスター保管域のためのセルのGETやFREEであれば、REGS=SAVEパラメーターは利用できません。
マルチタスク構造のプログラムで各タスクが同じサブルーチンを頻繁に呼び出すならば、GETMAINとFREEMAINよりCPOOLでセルを切り出す方がオーバーヘッドが少なくなります。