6

連想配列で nocopy を使用した場合の効果を確認するために、Oracle ブロックを作成します。1000000 個の要素を持つ配列を作成し、それを引数として 2 つの同一のメソッドに渡します。最初は in out パラメータとして、2 回目は in out nocopy として渡します。コードを以下に示します。

declare
type my_type is table of varchar2(32767) index by binary_integer;
my_array my_type;
st number;
rt number;
procedure in_out(m1 in out my_type)
is
begin
dbms_output.put_line(my_array(1));
end in_out;
procedure in_out_nocopy(m1  in out nocopy my_type)
is
begin
dbms_output.put_line(my_array(1));
end in_out_nocopy;
begin
for i in 1..999999 loop
  my_array(i) := '123456789012345678901234567890123456789012345678901234567890abcd';
end loop;
st := dbms_utility.get_time;
in_out(my_array);
rt := (dbms_utility.get_time - st)/100;
dbms_output.put_line('Time needed for in out is: ' || rt || ' 100''ths of second!');
st := dbms_utility.get_time;
in_out_nocopy(my_array);
rt := (dbms_utility.get_time - st)/100;
dbms_output.put_line('Time needed for in out nocopy is: ' || rt || ' 100''ths of second!');
end;

これで、nocopy メソッドの方が 0.27 秒改善したことが報告されます。私は2つのことに困惑しています:

i) 両方のメソッドの本体を次のように変更した場合

begin
null;
end; 

時間の違いはありませんが、パラメータの受け渡しには違いがあります。なぜそれが起こるのですか?

ii) 手順本体をそのままにしておく場合

begin
null;
end;

今回は、パラメーターを in out および in out nocopy と定義するのではなく、out および out nocopy と定義します。時間差が発生します。とにかくパラメーターが再初期化されると思ったのに、なぜここで時間差が発生し、アウトの場合ではないのですか?

よろしく、クリストス

4

1 に答える 1

7

良いテスト ケースです。Oracle 11gR1 (11.1.0.7.0) でも同じ結果が得られました。

これは、ドキュメントが何を言わなければならないかNOCOPYです:

NOCOPY ヒント (「NOCOPY」で説明)。

デフォルトでは、PL/SQL は OUT および IN OUT サブプログラム パラメータを値で渡します。サブプログラムを実行する前に、PL/SQL は各 OUT および IN OUT パラメータを一時変数にコピーします。一時変数には、サブプログラムの実行中にパラメータの値が保持されます。サブプログラムが正常に終了すると、PL/SQLは一時変数の値を対応する実パラメータにコピーします。サブプログラムが未処理の例外で終了した場合、PL/SQL は実パラメータの値を変更しません。

OUT または IN OUT パラメーターがコレクション、レコード、ADT のインスタンスなどの大きなデータ構造を表す場合、それらをコピーすると実行が遅くなり、特に ADT のインスタンスの場合、メモリ使用量が増加します。

ADT メソッドを呼び出すたびに、PL/SQL は ADT のすべての属性をコピーします。メソッドが正常に終了すると、PL/SQL はメソッドが属性に加えた変更を適用します。メソッドが未処理の例外で終了した場合、PL/SQL は属性を変更しません。

サブプログラムが未処理の例外で終了した場合に OUT または IN OUT パラメータが呼び出し前の値を保持する必要がない場合は、パラメータ宣言に NOCOPY ヒントを含めます。NOCOPY ヒントは、コンパイラーが対応する実パラメーターを値ではなく参照によって渡すことを要求します (ただし、保証はしません)。

NOCOPYは単なるヒントとして記述されていることに注意してください(つまり、コマンドではありません)。守られない場合もあります。

とにかく、NOCOPY の動作はケース (1) と (3) の標準です (はい、PL/SQL はエラーの場合に OUT パラメータの値を復元します)。(2)はどうですか?

ケース(2)では、NULLプロシージャが最適化されていると思います。最適化をオフにして試してみましょう。

SQL> alter session set plsql_optimize_level=0;
 
Session altered

SQL> DECLARE
  2     TYPE my_type IS TABLE OF LONG INDEX BY BINARY_INTEGER;
  3     my_array my_type;
  4     st       NUMBER;
  5     rt       NUMBER;
  6     PROCEDURE in_out(m1 IN OUT my_type) IS
  7     BEGIN
  8        NULL;--dbms_output.put_line(my_array(1));
  9     END in_out;
 10     PROCEDURE in_out_nocopy(m1 IN OUT NOCOPY my_type) IS
 11     BEGIN
 12        NULL;--dbms_output.put_line(my_array(1));
 13     END in_out_nocopy;
 14  BEGIN
 15     FOR i IN 1 .. 9999999 LOOP
 16        my_array(i) :=
 17         '123456789012345678901234567890123456789012345678901234567890abcd';
 18     END LOOP;
 19     st := dbms_utility.get_time;
 20     in_out(my_array);
 21     rt := (dbms_utility.get_time - st) / 100;
 22     dbms_output.put_line('Time needed for in out is: '
 23                          || rt || ' seconds!');
 24     st := dbms_utility.get_time;
 25     in_out_nocopy(my_array);
 26     rt := (dbms_utility.get_time - st) / 100;
 27     dbms_output.put_line('Time needed for in out nocopy is: '
 28                          || rt || ' seconds!');
 29  END;
 30  /
 
Time needed for in out is: 5,59 seconds!
Time needed for in out nocopy is: 0 seconds!

予想通り、違いは魔法のように再現されます:)

于 2013-03-07T14:32:56.907 に答える