Я могу воспроизвести поведение в моей базе данных 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.
В конце концов, мне кажется, что проблемное утверждение - это расширение, но оптимизатор достаточно умен.чтобы можно было избежать штрафа, если ничто в цикле не может изменить объект.