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Функция getTrimLine вызывается в теле цикла много раз, и мы несем заметные накладные расходы на вызов этой функции, передачу параметров и возврат результата. Хотелось бы сразу вставить тело функции в цикл не меняя PL/SQL-код. Для этого в 11g появилась директива PL/SQL-компилятора pragma inline.
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;
Перед вызовом, который мы хотим заменить на тело вызываемой подпрограммы, нужно вставить эту директиву:
SQL> rem Включаем вывод предупреждений от PL/SQL-компилятораКак Вы видите, подстановка была успешно выполнена. Обратите внимание, что оптимизатор PL/SQL вообще удалил функцию getTrimLine, поскольку теперь она стала "мертвой", - то есть нигде не вызывается!
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>
"Ну хорошо" - скажете Вы.
"Но ведь для того, чтобы этим воспользоваться, надо проанализировать весь код, и там, где это необходимо, вставить директиву pragma inline.
- Это все очень трудоемко !"
Не волнуйтесь - разработчки оптимизатора PL/SQL продумали это. :-)
В 11g появился новый уровень оптимизации PL/SQL - третий уровень, который собственно включает автоматическую inline-подстановку на фазе оптимизации кода. То есть оптимизатор сам принимает решение о необходимости подстановки того или иного вызова.
Проверим третий уровень на нашем примере (убираем из исходного кода директиву подстановки! ):
SQL> rem Включаем 3-ий уровень оптимизации PL/SQLКак видите оптимизатор "увидел", что вызов функции getTrimLine происходит в цикле и решил сделать ее подстановку вместо вызова.
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>
Подстановка - очень полезная технология оптимизации ващего PL/SQL кода, для ее использования вам не надо модифицировать код приложения, а всего лишь нужно установить третий уровень оптимизации PL/SQL (plsql_optimize_level=3) и перекомпилировать свой код.
Игорь добрый день.
ОтветитьУдалитьЧестно искал ответы в документации но не нашел :(
1. Нужно ли ставить pragma inline перед каждым обращением к функции или достаточно один раз в начале вызывающей процедуры?
2. Как кроме описанного в статье способа с plsql_warnings='ENABLE:ALL' как еще можно узнать в каких местах исходного кода была выполнена подстановка?
1. Нужно перед каждым вызовом процедуры указывать эту прагму
ОтветитьУдалить2. Можно увидеть это через профилирование, к сожалению другого, более способа - нет, по крайней мере документированного ... :-)