为什么PLSQL优化级别无法提供预期的结果? 1 PLSQL_OPTIMIZE_LEVEL如何工作? 2设置级别后,您要重新编译代码吗? 3您真的在测试内联吗? 4您的测试方法是否足够健壮? 5您甚至还关心PL / SQL优化吗?

问题描述

我试图深入了解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_typeINTERPRETED

plsql_code_type

因此,最初两个过程的优化级别均为2。当我执行过程CALL_PROC_ARITH时,大约需要05:00.866分钟。
稍后,我将会话级别的优化级别修改为3。当我执行过程CALL_PROC_ARITH时,大约需要00:05:05.011分钟,这增加了5秒。

有人可以告诉我为什么我看到这种偏离预期行为的情况吗?
如果我使用NATIVE编译,我会看到不同的结果吗?

注意:我是从IDE运行此程序,而不是直接从sqlPlus CLI运行。

DB:Oracle 18c XE

解决方法

您应该使用更好的资源来理解PLSQL_OPTIMIZE_LEVEL,并且应该确保以正确的方式测试正确的事物。

1。 PLSQL_OPTIMIZE_LEVEL如何工作?

了解任何参数的最佳方法是使用官方文档中的“数据库参考”。参数PLSQL_OPTIMIZE_LEVEL经常更改,因此请确保引用正确的版本。网络上有很多非官方的过时信息,但是here's the relevant text for 18c

0

维护评估顺序,从而保持副作用模式, 异常,以及Oracle9i和更早版本的程序包初始化 发布。还删除BINARY_INTEGER的新语义标识,并 PLS_INTEGER并恢复较早的评估规则 整数表达式。尽管代码的运行速度比它快 在Oracle9i中所做的,使用0级将丧失大部分性能 在Oracle数据库10g中获得PL / SQL。

1

对PL / SQL程序进行广泛的优化,包括 消除不必要的计算和异常,但通常 不会将源代码移出其原始源顺序。

2

应用了除 1级,包括可能会将源代码移至相对较远的更改 从其原始位置开始。

3

应用了除级别之外的各种优化技术 2,自动包含未明确要求的技术。

该描述使得很难分辨何时会发生内联。听起来内联可能发生在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。您的测试方法是否足够健壮?

您的测试应尝试补偿消耗CPU的其他活动。以交替的顺序运行多个小测试,扔掉最高和最低值,然后比较平均值。与两次进行5分钟的测试两次相差5秒并不重要。

我使用下面的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优化吗?

在大多数现实世界的PL / SQL程序中,内联过程不会产生有意义的变化。最佳实践是使用SQL进行尽可能多的繁重工作。但是,不管您的逻辑在哪里,都请确保您使用的是探查器,并且仅调整需要花费大量时间的程序部分。在调整PL / SQL程序的一部分之前,您应该掌握一些硬性数字,例如“如果我优化X行,则该程序可以更快地运行Y%。”