Next: , Previous: , Up: Threading   [Contents][Index]


15.2.3 Dynamic Superinstructions

gforth エンジンと gforth-fast エンジンは、 別の最適化、 つまり、 レプリケーションを伴う動的スーパー命令(Dynamic superinstructions with replication)を使用します。 例として、 以下のコロン定義について考えてみましょう:

: squared ( n1 -- n2 )
  dup * ;

Gforth はこれを以下のスレッド化コード・シーケンスにコンパイルします

dup
*
;s

simple-see (see Examining compiled code)を使用して、 コロン定義のスレッド化コードを確認します。

通常の直接スレッド化コードでは、 上記のプリミティブごとに 1 つのセルを占めるコード・アドレスがあります。 各コード・アドレスはマシン・コード・ルーチンを指し、 インタプリタはプリミティブを実行するためにこのマシン・コードにジャンプします。 上記 3 つのプリミティブのルーチンは以下のとおりです(386 の gforth-fast の場合):

Code dup  
( $804B950 )  add     esi , # -4  \ $83 $C6 $FC 
( $804B953 )  add     ebx , # 4  \ $83 $C3 $4 
( $804B956 )  mov     dword ptr 4 [esi] , ecx  \ $89 $4E $4 
( $804B959 )  jmp     dword ptr FC [ebx]  \ $FF $63 $FC 
end-code
Code *  
( $804ACC4 )  mov     eax , dword ptr 4 [esi]  \ $8B $46 $4 
( $804ACC7 )  add     esi , # 4  \ $83 $C6 $4 
( $804ACCA )  add     ebx , # 4  \ $83 $C3 $4 
( $804ACCD )  imul    ecx , eax  \ $F $AF $C8 
( $804ACD0 )  jmp     dword ptr FC [ebx]  \ $FF $63 $FC 
end-code
Code ;s  
( $804A693 )  mov     eax , dword ptr [edi]  \ $8B $7 
( $804A695 )  add     edi , # 4  \ $83 $C7 $4 
( $804A698 )  lea     ebx , dword ptr 4 [eax]  \ $8D $58 $4 
( $804A69B )  jmp     dword ptr FC [ebx]  \ $FF $63 $FC 
end-code

動的なスーパー命令とレプリケーションを使用すると、 コンパイラーはスレッド化されたコードを配置しないだけでなく、 コード断片のコピーも行い、 通常は各コード断片最後で行うジャンプを行いません。

( $4057D27D )  add     esi , # -4  \ $83 $C6 $FC 
( $4057D280 )  add     ebx , # 4  \ $83 $C3 $4 
( $4057D283 )  mov     dword ptr 4 [esi] , ecx  \ $89 $4E $4 
( $4057D286 )  mov     eax , dword ptr 4 [esi]  \ $8B $46 $4 
( $4057D289 )  add     esi , # 4  \ $83 $C6 $4 
( $4057D28C )  add     ebx , # 4  \ $83 $C3 $4 
( $4057D28F )  imul    ecx , eax  \ $F $AF $C8 
( $4057D292 )  mov     eax , dword ptr [edi]  \ $8B $7 
( $4057D294 )  add     edi , # 4  \ $83 $C7 $4 
( $4057D297 )  lea     ebx , dword ptr 4 [eax]  \ $8D $58 $4 
( $4057D29A )  jmp     dword ptr FC [ebx]  \ $FF $63 $FC 

スレッド化コードの制御フローに変更が発生した場合(;s など)にのみジャンプが追加されます。 この最適化により、 これらのジャンプの多くが排除され、 残りの部分がより予測可能になります。 高速化はプロセッサとアプリケーションによって異なります。 Athlon および Pentium III では、 この最適化により通常 2 倍の速度向上が得られます。

直接スレッド化コード内のコード・アドレスは、 コピーされたマシン・コード内の適切なポイントを指すように設定されます。 この例では以下のようになります:

primitive  code address
   dup       $4057D27D
   *         $4057D286
   ;s        $4057D292

したがって、 このコード部分の任意の場所にスレッド化コードからのジャンプ先が存在する可能性があります。 これにより、 逆コンパイルもかなり簡素化されます。

See-code (see Examining compiled code)は、 動的スーパー命令(dynamic superinstructions)のネイティブ・コードと混在するスレッド化コードを示します。 最近では、 動的に生成されたネイティブ・コードに追加の最適化が適用されているため、 特定の AMD64 インストール上の gforth-fast での see-code squared の出力は以下のようになります:

$7FB689C678C8 dup    1->2 
7FB68990C1B2:   mov     r15,r8
$7FB689C678D0 *    2->1 
7FB68990C1B5:   imul    r8,r15
$7FB689C678D8 ;s    1->1 
7FB68990C1B9:   mov     rbx,[r14]
7FB68990C1BC:   add     r14,$08
7FB68990C1C0:   mov     rax,[rbx]
7FB68990C1C3:   jmp     eax

--no-dynamic を使用してこの最適化を無効にできます。 --no-super を使用すると、 ジャンプを排除せずにコピーを使用できます(つまり、 動的レプリケーション、 しかしスーパー命令は使用しません)。 これは分岐予測のみに利点をもたらします。 パフォーマンスへの影響は CPU によって異なります。 Athlon および Pentium III では、 レプリケーションを伴う動的スーパー命令(dynamic superinstructions with replication)よりも高速化が若干損なわれます。

これらのオプションの用途の 1 つは、 スレッド化されたコードにパッチを適用する場合です。 スーパー命令を使用すると、 ディスパッチ・ジャンプの多くが削除されるため、 パッチを当てても効果がなくなることがよくあります。 これらのオプションは、 すべてのディスパッチ・ジャンプを保持します。

一部のマシンでは動的スーパー命令(dynamic superinstructions)は安全ではないため、 デフォルトで無効になっています。 しかしながら、 あなたが冒険してみたい場合は、 --dynamic を使用して有効にすることができます。


Next: DOES>, Previous: Direct or Indirect Threaded?, Up: Threading   [Contents][Index]