为什么 PLSQL 优化级别没有提供预期的结果?
Why PLSQL optimize level is not providing expected result?
我正在尝试深入了解 plsql 中的编译器优化。理论上,默认优化级别 PLSQL_OPTIMIZE_LEVEL
设置为 2。为了获得更好的性能,我们可以将其设置为 3。为了向自己解释这一点,我使用了一个示例,其中一个过程正在调用另一个过程,以便优化可以使用级别 3(内联过程)的功能。
这是第一个程序:
create or replace procedure p1
is
n number:=0;
begin
for i in 1..500000000
loop
n:=n+1;
end loop;
end;
这是第二个:
create or replace procedure CALL_PROC_ARITH
IS
BEGIN
for i in 1..10
loop
P1;
end loop;
END;
这是两个程序的 plsql_code_type
,即 INTERPRETED
。
plsql_code_type
所以最初两个程序的优化级别都是 2。当我执行程序 CALL_PROC_ARITH
时,大约需要 05:00.866 分钟。
后来我在session级别修改优化级别为3。而当我执行程序CALL_PROC_ARITH
时,大约需要00:05:05.011分钟,增加了5秒。
有人可以告诉我为什么我看到这种偏离预期的行为吗?
如果我使用 NATIVE 编译,我会看到不同的结果吗?
注意:我 运行 这是来自 IDE,而不是直接来自 SQLPlus CLI。
数据库:Oracle 18c XE
您应该使用更好的资源来理解 PLSQL_OPTIMIZE_LEVEL
,并且您应该确保以正确的方式测试正确的东西。
1。 PLSQL_OPTIMIZE_LEVEL 是如何工作的?
了解任何参数的最佳方法是使用官方文档中的数据库参考。参数 PLSQL_OPTIMIZE_LEVEL
经常更改,因此请确保引用准确的版本。网络上有很多非官方的 out-date 信息,但是 here's the relevant text for 18c:
0
Maintains the evaluation order and hence the pattern of side effects,
exceptions, and package initializations of Oracle9i and earlier
releases. Also removes the new semantic identity of BINARY_INTEGER and
PLS_INTEGER and restores the earlier rules for the evaluation of
integer expressions. Although code will run somewhat faster than it
did in Oracle9i, use of level 0 will forfeit most of the performance
gains of PL/SQL in Oracle Database 10g.
1
Applies a wide range of optimizations to PL/SQL programs including the
elimination of unnecessary computations and exceptions, but generally
does not move source code out of its original source order.
2
Applies a wide range of modern optimization techniques beyond those of
level 1 including changes which may move source code relatively far
from its original location.
3
Applies a wide range of optimization techniques beyond those of level
2, automatically including techniques not specifically requested.
该描述使得很难判断何时会发生内联。听起来内联 可能 发生在级别 1 并且 可能 发生在级别 2。我下面的测试显示内联性能从 0 到 1 有很大差异, 从 1 到 2 的差异很小,从 2 到 3 没有差异。
但是很多行为都没有记录在案,所以很难说什么时候会发生哪种优化。
2。设置级别后是否重新编译代码?
仅仅设置会话值是不够的,还必须重新编译程序,像这样:
alter session set plsql_optimize_level=3;
alter procedure call_proc_arith compile;
alter procedure p1 compile;
3。你真的在测试内联吗?
你的程序包含很多循环和一个过程调用,但我认为你的数字是倒过来的。要测试内联,必须让大循环调用过程,让小循环进行计数。您永远不会注意到只有 10 个过程调用的编译器差异。
我使用这些程序进行测试:
create or replace procedure p2 is
n number:=0;
begin
for i in 1..5 loop
n:=n+1;
end loop;
end;
/
create or replace procedure CALL_PROC_ARITH2 is
begin
for i in 1..10000000 loop
p2;
end loop;
end;
/
--Check the PL/SQL optimize level for the objects.
select name, plsql_optimize_level, plsql_code_type
from all_plsql_object_settings
where owner = user
and name like 'CALL_PROC%' or name like 'P_';
4。你的测试方法够健壮吗?
您的测试应该尝试补偿其他 activity 消耗 CPU。 运行 多个小测试交替进行,抛出高值和低值,比较平均值。 运行两次五分钟的测试相差五秒并不重要。
我使用下面的 PL/SQL 块测试了 运行 次。 (您可以构建一个 PL/SQL 程序以 运行 随机顺序的块并记录时间。我手动完成了那部分。)级别 3 和 2 运行 相同的速度,级别 1 是稍微慢一点,0 级明显更慢。
--Level 3: 3.331, 3.403, 3.419
alter session set plsql_optimize_level = 3;
alter procedure call_proc_arith2 compile;
alter procedure p2 compile;
begin
call_proc_arith2;
end;
/
--Level 2: 3.383, 3.470, 3.444
alter session set plsql_optimize_level = 2;
alter procedure call_proc_arith2 compile;
alter procedure p2 compile;
begin
call_proc_arith2;
end;
/
--Level 1: 3.867, 3.859, 3.873
alter session set plsql_optimize_level = 1;
alter procedure call_proc_arith2 compile;
alter procedure p2 compile;
begin
call_proc_arith2;
end;
/
--Level 0: 6.286, 6.296, 6.315
alter session set plsql_optimize_level = 0;
alter procedure call_proc_arith2 compile;
alter procedure p2 compile;
begin
call_proc_arith2;
end;
/
5。您甚至关心 PL/SQL 优化吗?
在大多数 real-world PL/SQL 程序中,内联程序不会产生有意义的差异。最好的做法是用 SQL 做尽可能多的繁重工作。但是无论您的逻辑在哪里,请确保您使用的是分析器并且只调整程序中花费大量时间的部分。在调整 PL/SQL 程序的一部分之前,您应该有一些硬性数字,例如“如果我优化第 X 行,程序可以 运行 快达 Y%。”
我正在尝试深入了解 plsql 中的编译器优化。理论上,默认优化级别 PLSQL_OPTIMIZE_LEVEL
设置为 2。为了获得更好的性能,我们可以将其设置为 3。为了向自己解释这一点,我使用了一个示例,其中一个过程正在调用另一个过程,以便优化可以使用级别 3(内联过程)的功能。
这是第一个程序:
create or replace procedure p1
is
n number:=0;
begin
for i in 1..500000000
loop
n:=n+1;
end loop;
end;
这是第二个:
create or replace procedure CALL_PROC_ARITH
IS
BEGIN
for i in 1..10
loop
P1;
end loop;
END;
这是两个程序的 plsql_code_type
,即 INTERPRETED
。
plsql_code_type
所以最初两个程序的优化级别都是 2。当我执行程序 CALL_PROC_ARITH
时,大约需要 05:00.866 分钟。
后来我在session级别修改优化级别为3。而当我执行程序CALL_PROC_ARITH
时,大约需要00:05:05.011分钟,增加了5秒。
有人可以告诉我为什么我看到这种偏离预期的行为吗?
如果我使用 NATIVE 编译,我会看到不同的结果吗?
注意:我 运行 这是来自 IDE,而不是直接来自 SQLPlus CLI。
数据库:Oracle 18c XE
您应该使用更好的资源来理解 PLSQL_OPTIMIZE_LEVEL
,并且您应该确保以正确的方式测试正确的东西。
1。 PLSQL_OPTIMIZE_LEVEL 是如何工作的?
了解任何参数的最佳方法是使用官方文档中的数据库参考。参数 PLSQL_OPTIMIZE_LEVEL
经常更改,因此请确保引用准确的版本。网络上有很多非官方的 out-date 信息,但是 here's the relevant text for 18c:
0
Maintains the evaluation order and hence the pattern of side effects, exceptions, and package initializations of Oracle9i and earlier releases. Also removes the new semantic identity of BINARY_INTEGER and PLS_INTEGER and restores the earlier rules for the evaluation of integer expressions. Although code will run somewhat faster than it did in Oracle9i, use of level 0 will forfeit most of the performance gains of PL/SQL in Oracle Database 10g.
1
Applies a wide range of optimizations to PL/SQL programs including the elimination of unnecessary computations and exceptions, but generally does not move source code out of its original source order.
2
Applies a wide range of modern optimization techniques beyond those of level 1 including changes which may move source code relatively far from its original location.
3
Applies a wide range of optimization techniques beyond those of level 2, automatically including techniques not specifically requested.
该描述使得很难判断何时会发生内联。听起来内联 可能 发生在级别 1 并且 可能 发生在级别 2。我下面的测试显示内联性能从 0 到 1 有很大差异, 从 1 到 2 的差异很小,从 2 到 3 没有差异。
但是很多行为都没有记录在案,所以很难说什么时候会发生哪种优化。
2。设置级别后是否重新编译代码?
仅仅设置会话值是不够的,还必须重新编译程序,像这样:
alter session set plsql_optimize_level=3;
alter procedure call_proc_arith compile;
alter procedure p1 compile;
3。你真的在测试内联吗?
你的程序包含很多循环和一个过程调用,但我认为你的数字是倒过来的。要测试内联,必须让大循环调用过程,让小循环进行计数。您永远不会注意到只有 10 个过程调用的编译器差异。
我使用这些程序进行测试:
create or replace procedure p2 is
n number:=0;
begin
for i in 1..5 loop
n:=n+1;
end loop;
end;
/
create or replace procedure CALL_PROC_ARITH2 is
begin
for i in 1..10000000 loop
p2;
end loop;
end;
/
--Check the PL/SQL optimize level for the objects.
select name, plsql_optimize_level, plsql_code_type
from all_plsql_object_settings
where owner = user
and name like 'CALL_PROC%' or name like 'P_';
4。你的测试方法够健壮吗?
您的测试应该尝试补偿其他 activity 消耗 CPU。 运行 多个小测试交替进行,抛出高值和低值,比较平均值。 运行两次五分钟的测试相差五秒并不重要。
我使用下面的 PL/SQL 块测试了 运行 次。 (您可以构建一个 PL/SQL 程序以 运行 随机顺序的块并记录时间。我手动完成了那部分。)级别 3 和 2 运行 相同的速度,级别 1 是稍微慢一点,0 级明显更慢。
--Level 3: 3.331, 3.403, 3.419
alter session set plsql_optimize_level = 3;
alter procedure call_proc_arith2 compile;
alter procedure p2 compile;
begin
call_proc_arith2;
end;
/
--Level 2: 3.383, 3.470, 3.444
alter session set plsql_optimize_level = 2;
alter procedure call_proc_arith2 compile;
alter procedure p2 compile;
begin
call_proc_arith2;
end;
/
--Level 1: 3.867, 3.859, 3.873
alter session set plsql_optimize_level = 1;
alter procedure call_proc_arith2 compile;
alter procedure p2 compile;
begin
call_proc_arith2;
end;
/
--Level 0: 6.286, 6.296, 6.315
alter session set plsql_optimize_level = 0;
alter procedure call_proc_arith2 compile;
alter procedure p2 compile;
begin
call_proc_arith2;
end;
/
5。您甚至关心 PL/SQL 优化吗?
在大多数 real-world PL/SQL 程序中,内联程序不会产生有意义的差异。最好的做法是用 SQL 做尽可能多的繁重工作。但是无论您的逻辑在哪里,请确保您使用的是分析器并且只调整程序中花费大量时间的部分。在调整 PL/SQL 程序的一部分之前,您应该有一些硬性数字,例如“如果我优化第 X 行,程序可以 运行 快达 Y%。”