我认识到,当实例变大时,在Oracle对象类型上调用方法需要更长的时间。
下面的代码只是将行添加到存储在对象类型中的集合中,并在循环中调用空 dummy
- 过程。
当集合中有更多行时,调用会花费更长时间。当我删除对dummy
的调用时,性能好多了(该集合仍包含相同数量的记录):
Calling dummy: Not calling dummy:
11 0
81 0
158 0
重现的代码:
Create Type t_tab Is Table Of VARCHAR2(10000);
Create Type test_type As Object(
tab t_tab,
Member Procedure dummy
);
Create Type Body test_type As
Member Procedure dummy As Begin
Null; --# Do nothing
End dummy;
End;
Declare
v_test_type test_type := New test_type( New t_tab() );
Procedure run_test As
start_time NUMBER := dbms_utility.get_time;
Begin
For i In 1 .. 200 Loop
v_test_Type.tab.Extend;
v_test_Type.tab(v_test_Type.tab.Last) := Lpad(' ', 10000);
v_test_Type.dummy(); --# Removed this line in second test
End Loop;
dbms_output.put_line( dbms_utility.get_time - start_time );
End run_test;
Begin
run_test;
run_test;
run_test;
End;
我尝试了 10g 和 11g 任何人都可以解释/重现这种行为吗?
答案 0 :(得分:2)
我可以在我的11.1.0.7数据库上重现该行为。我不确定我有解释,但我确实有一个理论。
如果将Extend调用移到循环外部,只需向集合中添加200个元素,性能下降就会消失(见下文)。这让我相信它不仅仅是调用对象方法的行为 - 这似乎与集合的低效扩展有1次200次而不是200次元素的一次互动。
SQL> ed
Wrote file afiedt.buf
1 Declare
2 v_test_type test_type := New test_type( New t_tab() );
3 Procedure run_test As
4 start_time NUMBER := dbms_utility.get_time;
5 Begin
6 v_test_Type.tab.Extend(200);
7 For i In 1 .. 200 Loop
8 v_test_Type.tab(v_test_Type.tab.Last) := Lpad(' ', 10000);
9 v_test_Type.dummy(); --# Removed this line in second test
10 End Loop;
11 dbms_output.put_line( dbms_utility.get_time - start_time );
12 End run_test;
13 Begin
14 run_test;
15 run_test;
16 run_test;
17* End;
SQL> /
11
9
10
PL/SQL procedure successfully completed.
在这里推测,但是如果对过程的调用可能会修改集合,那么编译器可能会对扩展集合的扩展集合进行一些优化,而这些集合不能(或不会)生成。
作为对该推测的快速测试,我创建了一个成员函数而不是成员过程,并在循环中调用了该函数。由于函数不会修改对象状态,因此它们不会排除我推测的那种优化。果然,如果我使用成员函数创建对象类型,性能下降就会消失
SQL> ed
Wrote file afiedt.buf
1 Create or replace Type test_type As Object(
2 tab t_tab,
3 Member Procedure dummy,
4 Member Function dummy2 return number
5* );
SQL> /
Type created.
SQL> ed
Wrote file afiedt.buf
1 Create or replace Type Body test_type As
2 Member Procedure dummy As Begin
3 Null; --# Do nothing
4 End dummy;
5 Member Function dummy2
6 return number
7 Is
8 Begin
9 Return 1;
10 End dummy2;
11* End;
12 /
Type body created.
SQL> ed
Wrote file afiedt.buf
1 Declare
2 v_test_type test_type := New test_type( New t_tab() );
3 Procedure run_test As
4 start_time NUMBER := dbms_utility.get_time;
5 l_num NUMBER;
6 Begin
7 For i In 1 .. 200 Loop
8 v_test_Type.tab.Extend;
9 v_test_Type.tab(v_test_Type.tab.Last) := Lpad(' ', 10000);
10 l_num := v_test_Type.dummy2(); --# Removed this line in second test
11 End Loop;
12 dbms_output.put_line( dbms_utility.get_time - start_time );
13 End run_test;
14 Begin
15 run_test;
16 run_test;
17 run_test;
18* End;
19 /
11
9
9
PL/SQL procedure successfully completed.
最后,我认为有问题的语句是Extend,但是如果循环中没有任何东西能够修改对象,那么优化器足够聪明,能够避免惩罚。
答案 1 :(得分:1)
我自己发现,问题在Using SELF IN OUT NOCOPY with Member Procedures中描述:
在成员程序中,如果未声明
SELF
,则其参数模式默认为IN OUT
。
因此,对于每个过程调用,我的整个对象被复制了两次,随着大小的增加,这需要更长时间。
解决方案是使用SELF IN OUT NOCOPY test_type
作为我的过程声明的第一个参数:
Create Type test_type As Object(
tab t_tab,
Member Procedure dummy(SELF IN OUT NOCOPY test_type)
);
仍然在没有参数的情况下调用
v_test_type.dummy();
表现恢复正常:
0
0
0