Странное поведение с большими типами объектов - PullRequest
4 голосов
/ 15 апреля 2010

Я понял, что вызов метода для Oracle Object Type занимает больше времени, когда экземпляр становится больше.

Приведенный ниже код просто добавляет строки в коллекцию, сохраненную в типе объекта, и вызывает пустую 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 .
Кто-нибудь может объяснить / воспроизвести это поведение?

Ответы [ 2 ]

2 голосов
/ 16 апреля 2010

Я могу воспроизвести поведение в моей базе данных 11.1.0.7.Я не уверен, что у меня есть объяснение, но у меня есть теория.

Если переместить вызов Extend за пределы цикла и просто добавить 200 элементов в коллекцию, снижение производительности исчезнет (см. Ниже).Это заставляет меня поверить, что проблема заключается не только в вызове метода объекта, поскольку существует некое взаимодействие с неэффективным расширением коллекции в 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.

В конце концов, мне кажется, что проблемное утверждение - это расширение, но оптимизатор достаточно умен.чтобы можно было избежать штрафа, если ничто в цикле не может изменить объект.

1 голос
/ 17 апреля 2010

Сам выяснил, проблема описана в Использование SELF IN OUT NOPOPY с процедурами участника :

В процедурах-членах, если 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
...