PL/SQL Inline Optimization in 11g

Недавно с Геннадием Сигалаевым мы обсуждали технологию inline-подстановки в хранимых PL/SQL-процедурах TimesTen. Но вообще говоря, эта технология появилась в Oracle Database 11g, и уже потом весь "движок" PL/SQL был портирован в TimesTen.

Inline-подстановка хорошо знакома программистам, которые пишут на C/C++ и Delphi.

Начиная с 11g теперь такая возможность есть и у PL/SQL-разработчиков.

Рассмотрим следующий пример кода:

create or replace procedure test

authid definer is

function getTrimLine(pLine in varchar2) return varchar2 is

begin
return '' trim(pLine) '';
end;

begin

for xIndex in 1..100

loop
dbms_output.put_line(getTrimLine(dbms_random.string('p',xIndex)));
end loop;

end;
Функция getTrimLine вызывается в теле цикла много раз, и мы несем заметные накладные расходы на вызов этой функции, передачу параметров и возврат результата. Хотелось бы сразу вставить тело функции в цикл не меняя PL/SQL-код. Для этого в 11g появилась директива PL/SQL-компилятора pragma inline.

Перед вызовом, который мы хотим заменить на тело вызываемой подпрограммы, нужно вставить эту директиву:
SQL> rem Включаем вывод предупреждений от PL/SQL-компилятора

SQL> alter session set plsql_warnings='ENABLE:ALL';

Session altered.

SQL> create or replace procedure test

authid definer is

function getTrimLine(pLine in varchar2) return varchar2 is

begin
return '' trim(pLine) '';
end;

begin

for xIndex in 1..100

loop
pragma inline(getTrimLine,YES);
dbms_output.put_line(getTrimLine(dbms_random.string('p',xIndex)));
end loop;

end;
/

show errors;

SP2-0804: Procedure created with compilation warnings

SQL> Errors for PROCEDURE TEST:

LINE/COL ERROR

-------- -----------------------------------------------------------------
4/3 PLW-06006: uncalled procedure "GETTRIMLINE" is removed.
13/5 PLW-06005: inlining of call of procedure 'GETTRIMLINE' was done
13/38 PLW-06004: inlining of call of procedure 'GETTRIMLINE' requested


SQL>
Как Вы видите, подстановка была успешно выполнена. Обратите внимание, что оптимизатор PL/SQL вообще удалил функцию getTrimLine, поскольку теперь она стала "мертвой", - то есть нигде не вызывается!

"Ну хорошо" - скажете Вы.
"Но ведь для того, чтобы этим воспользоваться, надо проанализировать весь код, и там, где это необходимо, вставить директиву pragma inline.
- Это все очень трудоемко !"

Не волнуйтесь - разработчки оптимизатора PL/SQL продумали это. :-)
В 11g появился новый уровень оптимизации PL/SQL - третий уровень, который собственно включает автоматическую inline-подстановку на фазе оптимизации кода. То есть оптимизатор сам принимает решение о необходимости подстановки того или иного вызова.
Проверим третий уровень на нашем примере (убираем из исходного кода директиву подстановки! ):
SQL> rem Включаем 3-ий уровень оптимизации PL/SQL

SQL> alter session set plsql_optimize_level=3;

Session altered.

SQL> create or replace procedure test

authid definer is

function getTrimLine(pLine in varchar2) return varchar2 is

begin
return '' trim(pLine) '';
end;

begin

for xIndex in 1..100

loop
dbms_output.put_line(getTrimLine(dbms_random.string('p',xIndex)));
end loop;

end;
/

show errors;

SP2-0804: Procedure created with compilation warnings

SQL> Errors for PROCEDURE TEST:

LINE/COL ERROR

-------- -----------------------------------------------------------------
4/3 PLW-06006: uncalled procedure "GETTRIMLINE" is removed.
12/5 PLW-06005: inlining of call of procedure 'GETTRIMLINE' was done


SQL>
Как видите оптимизатор "увидел", что вызов функции getTrimLine происходит в цикле и решил сделать ее подстановку вместо вызова.

Подстановка - очень полезная технология оптимизации ващего PL/SQL кода, для ее использования вам не надо модифицировать код приложения, а всего лишь нужно установить третий уровень оптимизации PL/SQL (plsql_optimize_level=3) и перекомпилировать свой код.

2 комментария:

  1. Игорь добрый день.
    Честно искал ответы в документации но не нашел :(
    1. Нужно ли ставить pragma inline перед каждым обращением к функции или достаточно один раз в начале вызывающей процедуры?
    2. Как кроме описанного в статье способа с plsql_warnings='ENABLE:ALL' как еще можно узнать в каких местах исходного кода была выполнена подстановка?

    ОтветитьУдалить
  2. 1. Нужно перед каждым вызовом процедуры указывать эту прагму

    2. Можно увидеть это через профилирование, к сожалению другого, более способа - нет, по крайней мере документированного ... :-)

    ОтветитьУдалить