e-learning、オラクル研修、LMS(学習管理システム)のiStudy

e-learning、オラクル研修、LMS(学習管理システム)のiStudy

第96回 「ネイティブコンパイル」

2014.05.15

こんにちは。インストラクターの蓑島です。

今回は、「ネイティブコンパイル」という機能をご紹介します。
これを用いることで、状況によりプログラムの実行速度を速くできる可能性があります。その効果も実際に測定してみます。

まずは前提知識の確認しましょう。
PL/SQLオブジェクト(ストアドプロシージャ、ストアドファンクション、パッケージなど)は、ソースコードだけでなく、コンパイルされたコードもデータベースに格納(ストアド)されています。

コンパイルされたコードというのは、一般的には機械が実行できるコードということです。
機械が直接実行できるコードのことを「ネイティブコード」あるいは「機械語」ともいいます。
ソースコードをひとつ、ひとつ、機械語に翻訳(INTERPRET)しながら実行するよりも、最初からソースコード全体を機械語に翻訳しておいて、それを実行するほうが速度がずっと速いわけです。

しかし、PL/SQLオブジェクトのコンパイルは、デフォルトでは、ソースコードを、機械語ではなく中間の状態までしかコンパイルしていません。このようなソースコードと機械語の中間の状態のコードのことを「中間コード」といいます。

中間コードは、実行時に機械語に翻訳しなければ実行できないので、ネイティブコードよりも実行速度が遅くなります。しかし、中間コードには環境に依存しないというメリットもあります。例えば、Java言語も中間コードの形でコンパイルされています。
Javaのソースコードをコンパイルしたクラスファイルがプラットホームに依存しないのは、中間コードでコンパイルされているからです。 PL/SQLオブジェクトもデフォルトでは中間コードの状態でコンパイルされているのです。

以上のことから、PL/SQLオブジェクトをネイティブコードでコンパイルすれば、一般に実行速度を速くできます。
しかし、PL/SQLオブジェクトの全ての部分を速く実行できるわけではありません。SQL文の実行の部分については、速くすることはできません。
なぜなら、PL/SQLは、PL/SQLエンジンで実行されますが、その中のSQL文についてはSQLエンジンという別の仕組みで実行されるからです。
※PL/SQLがふたつのエンジンを切り替えながら実行していることについては、バックナンバー第59回「バルク・バインド(DMLの場合)」で解説しています。

では早速、ネイティブコードでコンパイルする方法を解説します。
その方法はとても簡単であり、データベースの初期化パラメータ「PLSQL_CODE_TYPE」を「NATIVE」と設定して、PL/SQLオブジェクトを作成あるいはコンパイルするだけです。そのパラメータ設定はシステムレベルでも、セッションレベルでも可能です。

(注意)データベースがOracle 10g以前の場合は、上記のパラメータ設定だけでなく、Cコンパイラの設定も必要になります。
詳しくはマニュアルを参照してください。

では以下のプロシージャを例に解説します。

1
2
3
4
5
6
7
8
9
10
11
12
13
create or replace procedure proc96
is
   dummy  number := 0;
begin
   for i in 1..10000  loop
     for j in reverse 1..10000  loop
         dummy := dummy + i -j;
     end loop;
   end loop;
end ;
/
 
プロシージャが作成されました。

このプロシージャ「proc96」の処理内容でSQL文は実行していないことに注目してください。
いわば、SQL文は実行していないが、重たい処理をしているつもりです。
例えば、5行目のfor文で変数iを1~10000まで増やしながらループし、その内側のループ(6行目)で、変数jを10000~1まで減らしながら(reverse)ループします。したがって、7行目のdummy変数への計算式の代入は、10000×10000= 100000000 (1億回)行われることになります。
実際に実行してみるときはお使いの環境のスペックに合わせてループの回数を調整してみてください。

さて、この「proc96」プロシージャは、特段の指定をせず作成したので、中間コードでコンパイルされたはずです。
それを確認してみましょう。以下の問い合わせで確認できます。

1
2
3
4
5
6
7
8
SELECT NAME , PLSQL_CODE_TYPE
FROM USER_PLSQL_OBJECT_SETTINGS
WHERE NAME = 'PROC96'
/
 
NAME                 PLSQL_CODE_TYPE
-------------------- --------------------
PROC96               INTERPRETED

USER_PLSQL_OBJECT_SETTINGSビューは、各PL/SQLオブジェクトがどのようなコンパイルのされかたをしたかを情報として格納します。そして、PLSQL_CODE_TYPE が、 INTERPRETEDであるということが、中間コードであることを表しています。

では早速、中間コードの状態の「proc96」を実行して、時間を測定してみましょう。

1
2
3
4
5
6
SET TIMING ON   --時間の計測を行う指定
EXEC PROC96     --プロシージャの実行
 
PL/SQLプロシージャが正常に完了しました。
 
経過: 00:00:17.10

結果は、約17秒でした。

次にセッションレベルで、パラメータPLSQL_CODE_TYPEをNATIVEに設定して、「proc96」を再コンパイルします。 まずセッションレベルでパラメータを設定します。

1
2
3
ALTER SESSION SET PLSQL_CODE_TYPE=NATIVE;
 
セッションが変更されました。

つぎに「proc96」を再コンパイルします。

1
2
3
ALTER PROCEDURE PROC96 COMPILE;
 
プロシージャが変更されました。

そして実際にNATIVEコードかどうか確認します。先ほどの問い合わせをもう一度行います。

1
2
3
4
5
6
7
SELECT NAME , PLSQL_CODE_TYPE
FROM USER_PLSQL_OBJECT_SETTINGS
WHERE NAME = 'PROC96' ;
 
NAME                 PLSQL_CODE_TYPE
-------------------- --------------------
PROC96               NATIVE

ご覧のように、「PROC96」はネイティブ(NATIVE)コードであることがわかります。

では、「PROC96」を実行します。

1
2
3
4
5
EXEC PROC96
 
PL/SQLプロシージャが正常に完了しました。
 
経過: 00:00:13.30

今度は約13秒です。つまり約22%速くなったわけです。
劇的という程ではありませんが、確かにネイティブコードにすることで効果がありました。

最後に1点補足します。

PL/SQLオブジェクトは参照している表などの定義が変更されると、それにともない無効になることがあります。
そういったとき、そのPL/SQLを実行しようとすると、データベースにより自動的に再コンパイルされコンパイルが成功すれば実行できます。
そのような自動的なコンパイルでは、もとのコードタイプが保持されるので、ネイティブコードであれば必ずネイティブコードで再コンパイルされます。
しかし、人間がALTER~COMPILE文で手動でコンパイルを行うと、その時点でのPLSQL_CODE_TYPEパラメータの設定でコンパイルしますので、異なるコードタイプでコンパイルしてしまうことがありえます。
よって手動でのコンパイルは事前にコードタイプを確認するなど気を付けてください。

いかがでしたか? 
ネイティブコードは、SQL文の実行を速くするものではなく、SQL文以外のループなどを使った配列操作や、計算集中的な処理などは、ある程度速くすることはできることがお分かりいただけましたでしょうか。

それでは今回はここまでにいたします。また次回、ご期待ください。

先頭へ戻る