大型对象类型的奇怪行为

发布于 2024-08-28 16:26:33 字数 1213 浏览 5 评论 0原文

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

下面的代码只是将行添加到存储在对象类型中的集合中,并在循环中调用 empty 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;

我尝试了两者 10 克11 克
谁能解释/重现这种行为?

I recognized that calling a method on an Oracle Object Type takes longer when the instance gets bigger.

The code below just adds rows to a collection stored in the Object Type and calls the empty dummy-procedure in the loop.

Calls are taking longer when more rows are in the collection. When I just remove the call to dummy, performance is much better (the collection still contains the same number of records):

Calling dummy:               Not calling dummy:
11                           0
81                           0
158                          0

Code to reproduce:

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;

I tried with both 10g and 11g.
Can anyone explain/reproduce this behavior?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

栖竹 2024-09-04 16:26:33

我可以在 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,但优化器足够聪明,能够避免惩罚。在循环中能够修改对象。

I can reproduce the behavior on my 11.1.0.7 database. I'm not certain that I have an explanation, but I do have a theory.

If you move the Extend call outside the loop and just add 200 elements to the collection, the decrease in performance disappears (see below). That leads me to believe that it is not solely the act of calling the object method that is the problem-- there appears to be some interaction with the inefficient extension of the collection by 1 element 200 times rather than by 200 elements 1 time.

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.

Speculating here, but perhaps there is some optimization that the compiler is able to make to the calls to extend the collection that it cannot (or does not) make if a call to a procedure might modify the collection.

As a quick test of that speculation, I created a member function rather than a member procedure and called the function in the loop. Since functions don't modify object state, they wouldn't preclude the sort of optimizations I was speculating about. Sure enough, if I create the object type with a member function, the decline in performance disappears

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.

In the end, it looks to me like the problematic statement is the Extend but that the optimizer is smart enough to be able to avoid the penalty if nothing in the loop is able to modify the object.

空城之時有危險 2024-09-04 16:26:33

我自己发现了,问题在 使用中进行了描述SELF IN OUT NOCOPY 会员程序

在成员过程中,如果没有声明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

Found out myself, the problem is described in Using SELF IN OUT NOCOPY with Member Procedures:

In member procedures, if SELF is not declared, its parameter mode defaults to IN OUT.

So with every procedure call my whole object was copied twice, and as size was increasing this took longer and longer.


The solution is to use SELF IN OUT NOCOPY test_type as first parameter of my procedure declaration:

Create Type test_type As Object(
  tab t_tab,
  Member Procedure dummy(SELF IN OUT NOCOPY test_type)
);

and is still called without parameter

v_test_type.dummy();

Performance is back to normal:

0
0
0
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文