代码之家  ›  专栏  ›  技术社区  ›  Peter Lang

大对象类型的奇怪行为

  •  4
  • Peter Lang  · 技术社区  · 14 年前

    我认识到,当实例变大时,对Oracle对象类型调用方法需要更长的时间。

    下面的代码只是将行添加到存储在对象类型中的集合,并调用 空的 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 .
    有人能解释/再现这种行为吗?

    2 回复  |  直到 14 年前
        1
  •  2
  •   Justin Cave    14 年前

    我可以在我的11.1.0.7数据库中重现这种行为。我不确定我有什么解释,但我确实有一个理论。

    如果将extend调用移出循环并只向集合中添加200个元素,则性能降低将消失(请参见下文)。这让我相信问题不仅仅在于调用对象方法的行为——似乎存在一些交互作用,集合的低效扩展是1个元素200次,而不是200个元素1次。

    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,但是如果循环中没有任何东西能够修改对象,那么优化器就足够聪明,能够避免惩罚。

        2
  •  1
  •   Peter Lang    14 年前

    我发现了,问题描述在 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