2010-04-15 68 views
4

我发现在实例变大时调用Oracle对象类型的方法需要更长的时间。大对象类型的奇怪行为

下面的代码只是将行添加到存储在对象类型中的集合中,并且在循环中调用dummydummy-过程。

当集合中有更多行时,调用需要更长的时间。当我刚刚删除调用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; 

我试图与这两个10g11g
任何人都可以解释/重现此行为?

回答

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 
2

我可以重现我的11.1.0.7数据库上的行为。我不确定我是否有解释,但我确实有一个理论。

如果您将循环外部的Extend调用移动到该集合中,只需将200个元素添加到集合中,则性能下降会消失(请参见下文)。这使我相信,这不仅仅是调用对象方法的问题,而且这种方法看起来与收集效率非常低的扩展相差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. 

最后,它看起来对我来说,有问题的语句的扩展,但优化器是足够聪明,是如果循环中没有内容能够修改对象,就能够避免惩罚。

+0

@Justin Cave:谢谢,但是我怕你的回答不正确。 'function'部分非常有趣,但是你的测试设置了'v_test_Type.tab(v_test_Type.tab.Last)',所以你总是覆盖id'200','400'和'600'中的值,而我的解决方案总是将收集量增加一个,所以我填充了每个ID。花了我一些时间来解决这个问题,虽然:) – 2010-04-16 09:50:45

+0

@Justin洞穴:再次感谢,发现了我自己 - 看到我的答案,如果你有兴趣。 – 2010-04-17 07:05:40