Производительность PL/SQL
ОГЛАВЛЕНИЕ
Native compilation – это не совсем новая возможность, однако теперь нет «узких» мест её использования, например, установка компилятора C (Oracle назвал эту замечательную возможность "Real Native Compilation"). Кроме того, новый тип данных simple_integer делает выполнение кода лучше при Native compilation. Intra-unit inlining – это техника оптимизации, применяемая к PL/SQL-коду во время компиляции для создания эффективного кода. В этой статье будут рассмотрены некоторые случаи использования этих новых возможностей. Будет также проверена их производительность при различных сценариях: когда используется Native compilation, когда используются числа типа simple_integer, когда используется inlining и их различные комбинации. Real Native Compilation
Вспомните Native compilation в Oracle9i Database Release 2; она делает выполнение PL/SQL-программ намного быстрее по сравнению с интерпретируемыми формами. С другой стороны, освоение было медленным, так как многие системные администраторы сопротивлялись установке требуемого компилятора C на производственный сервер базы данных. Кроме того, такие компиляторы требуют установки параметра plsql_native_library_dir с директорией для промежуточных файлов OS. В Oracle Database 11g можно выполнять Native compilation без компилятора C на сервере и без установки параметра. Всё, что необходимо сделать, это установить параметр сессии перед созданием или перекомпиляцией хранимого кода:
alter session set plsql_code_type = native;
... здесь выполняется компиляция ...
Native compilation выполняется дольше, чем interpreted compilation, но так как этот процесс в Oracle Database 11g намного быстрее, то разница может оказаться незаметной. Лучше всего применять interpreted compilation во время обычного цикла разработки и Native compilation, когда разработка завершена.
Как часть миграции на 11g, я выполнил эксперимент с реальным кодом из диагностического приложения над очень большим пакетом из 5 827 строк. Я скомпилировал его в режиме Native compilation на существующей базе данных 10g и сделал то же самое на 11g, а затем повторил эти действия в режиме interpreted compilation. Каждая из этих компиляций была сделана с параметром plsql_optimize_level, равным 2. Я измерил время компиляции для каждого случая, оно показано ниже (в секундах).
| 10g | 11g |
Interpreted | 1.66 | 1.64 |
Native | 4.66 | 2.81 |
Результаты говорят сами за себя. При interpreted compilation время компиляции почти одинаковое. Однако при Native compilation время компиляции в 11g меньше примерно на 60% по сравнению с 10g, а это существенное улучшение. Итак, хотя Native compilation в 11g требует дополнительного времени, она намного быстрее, чем Native compilation в 10g.
Чтобы найти объекты, скомпилированные с помощью NATIVE, смотрите в представление USER_PLSQL_OBJECT_SETTINGS:
SQL> select name, PLSQL_code_type
2> from user_plsql_object_settings;
NAME PLSQL_CODE_TYPE
-------------------- ---------------
DO_CALC NATIVE
PERFECT_TRIANGLES NATIVE
PRIME_NUMBERS NATIVE
PRIME_NUMBERS NATIVE
SOME_MATH INTERPRETED
TR_BOOKINGS_TRACK INTERPRETED
TR_SALGRADE_COMP INTERPRETED
UPD_INT NATIVE
Есть и похожее представление для всех объектов, DBA_PLSQL_OBJECT_SETTINGS.
Новый тип данных: Simple_Integer
Достоинства Native compilation ещё больше очевидны при использовании нового типа данных, simple_integer. Технически это не настоящий тип данных, а скорее подтип pls_integer. Этот подтип создан для улучшения машинных вычислений по сравнению с программными вычислениями. Когда simple_integer используется одновременно с Native compilation, производительность будет намного лучше. В эксперименте, который будет показан дальше, вы увидите, почему.
Так как simple_integer является подтипом pls_integer, он наследует его свойства как 32-битного целого числа со знаком и может быть целым числом от - 2,147,483,648 до 2,147,483,647. Однако он отличается от pls_integer следующим: он не допускает значения null, но допускает переполнение — когда значение превышает максимум, оно сбрасывается, а ошибка не появляется.
Этот тип данных можно синтаксически использовать во всех тех же местах, где и pls_integer, но необходимо внимательно следить за разницей; дополнительные свойства simple_integer в некоторых случаях могут оказаться неуместными.
Давайте проверим несколько потенциальных задач, где следовало бы заменить pls_integer на simple_integer:
- Эта переменная не может быть null, поэтому если не написать так, как показано ниже:
num1 simple_integer:= 1;
а написать так:num1 simple_integer;
то будет получена ошибка компиляции:PLS-00218: a variable declared NOT NULL must have an initialization assignment
Если установить значение переменной в NULL внутри программы, например, так:
num1 := NULL;
будет получена ошибка компиляции:
PLS-00382: expression is of wrong type
Избегайте этих сообщений об ошибках, которые могут быть не видны с точки зрения точной сути ошибки. Если программа ожидает установки переменной в null, то не следует описывать переменную, как simple_integer.
- Другой важный момент в применении simple_integer заключается в том, что используется сброс значений при достижении максимальной и минимальной границ. Помните, максимальное положительное значение pls_integer, это 2147483647. Что случится, если попытаться сохранить значение, которое больше? Посмотрите демонстрационный пример:
declare v1 pls_integer := 2147483647;
Будет получена ошибка:
begin
v1 := v1 + 1;
dbms_output.put_line('v1='||v1);
end;
/declare
Ошибка очевидна и вполне уместна; вы попытались превысить максимальное значение, допустимое для типа данных. Если вместо pls_integer использовать simple_integer:
*
ERROR at line 1:
ORA-01426: numeric overflow
ORA-06512: at line 4declare v1
Результат будет следующим:
simple_integer := 2147483647;
begin
v1 := v1 + 1;
dbms_output.put_line('v1='||v1);
end;
/v1=-2147483648
Заметьте, что значение (-2147483648), это минимальное значение simple_integer. Когда вы прибавляете к максимальному значению (2147483647), значение просто сбрасывается до минимального — это особенность simple_integer. Помните об этом поведении.
Использование Native compilation с типом Simple_Integer
Как видите, simple_integer не могут использоваться в любом месте; необходимо быть внимательными при учёте дополнительных условий (особенно возможном сбросе значений) перед их применением. Поэтому simple_integer создан для Native compilation. В режиме interpreted compilation эффекта производительности может не быть (но он и вреда нет, как будет видно дальше). В режиме Native compilation, производительность simple_integer намного более существенна.
Большинство бизнес-приложений на PL/SQL жестко связаны с SQL, поэтому эти приложения не увидят значительного изменения производительности при Native compilation. В "прошлой жизни " я разрабатывал инструмент для планирования возможностей базы данных с использованием PL/SQL, включающего много числовых и статистических вычислений в несколько тысяч строк кода. При Native compilation было видно существенное увеличение в производительности. Числа типа Simple_integer не были доступны в то время, но если бы были, то они добавили бы ещё большей производительности.
Intra-unit Inlining
Intra-unit inlining представляет собой подмену вызова подпрограммы на копию кода этой подпрограммы. В результате модифицированный код выполняется быстрее. В Oracle Database 11g компилятор PL/SQL способен идентифицировать вызовы подпрограммы, которую необходимо скопировать (другими словами, подменить на неё) и делает изменения, улучшающие производительность.
Лучше всего это объяснить на примере. Код, показанный ниже, изменяет таблицу BALANCES, вычислив значения на основании баланса счёта. Код проходит по всем записям таблицы, вычисляет результат, и изменяет столбец таблицы с балансом.
create or replace procedure upd_int isФактически, вычисление результата одинаково для всех типов записей, и я поместил логику в отдельную процедуру calc_int() внутри основной процедуры. Это повышает читаемость и сопровождаемость кода, но, к сожалению, это неэффективно.
/* original version */
l_rate_type balances.rate_type%type;
l_bal balances.balance%type;
l_accno balances.accno%type;
l_int_rate number;
procedure calc_int (
p_bal in out balances.balance%type,
p_rate in number
) is
begin
if (p_rate >= 0) then
p_bal := p_bal * (1+(p_rate/12/100));
end if;
end;
begin
for ctr in 1..10000 loop
l_accno := ctr;
select balance, rate_type
into l_bal, l_rate_type
from balances
where accno = l_accno;
select decode(l_rate_type,
'C', 1, 'S', 3, 'M', 5, 0)
into l_int_rate
from dual;
for mth in 1..12 loop
calc_int (l_bal, l_int_rate);
update balances
set balance = l_bal
where accno = l_accno;
end loop;
end loop;
end;
/
Однако, если заменить вызов calc_int() на код calc_int(), получится более быстрая программа, как показано ниже:
create or replace procedure upd_int isЭтот переделанный код отличается от исходного только в части кода для вычисления баланса, который теперь внутри цикла, а не в процедуре calc_int().
/* revised version */
l_rate_type balances.rate_type%type;
l_bal balances.balance%type;
l_accno balances.accno%type;
l_int_rate number;
begin
for ctr in 1..10000 loop
l_accno := ctr;
select balance, rate_type
into l_bal, l_rate_type
from balances
where accno = l_accno;
select decode(l_rate_type,
'C', 1, 'S', 3, 'M', 5, 0)
into l_int_rate
from dual;
for mth in 1..12 loop
-- this is the int calc routine
if (l_int_rate >= 0) then
l_bal := l_bal * (1+(l_int_rate/12/100));
end if;
update balances
set balance = l_bal
where accno = l_accno;
end loop;
end loop;
end;
/
Заметьте, что новая версия может быть быстрее, но это не очень хороший пример практики кодирования. Часть кода, выполняющая вычисление баланса, выполняется один раз для каждой итерации цикла для месяцев, а затем и для каждого номера счёта. Так как эта часть кода повторяется, она более удобна для размещения отдельно, как показано в предыдущей версии upd_int, в процедуре (calc_int). Этот подход делает код модульным, легким в поддержке, и реально читаемым — но также менее эффективным.
Поэтому как можно достичь примирения конфликтующих способов создания кода, сделав код модульным и одновременно быстрым? Так, а можно ли написать код, используя модульный подход (как в первой версии upd_int), а затем позволить компилятору PL/SQL "оптимизировать" его, чтобы он стал выглядеть, как во второй версии кода?
Это можно сделать в Oracle Database 11g. Всё, что требуется сделать - перекомпилировать процедуру с более высоким уровнем оптимизации PL/SQL. Этого можно достичь двумя способами:
- Установить параметр уровня сессии и перекомпилировать процедуру:
SQL> alter session set plsql_optimize_level = 3;
Команда, показанная выше, инструктирует компилятор PL/SQL, чтобы он переписал код во встроенный код.
Session altered.
- Скомпилировать процедуру непосредственно с plsql-установкой.
SQL> alter procedure upd_int
На любую другую процедуру, компилируемую в этой же сессии, это не повлияет. Этот метод лучше применять для обработки inlining, если есть много процедур, которые необходимо скомпилировать в одной сессии.
2 compile
3 plsql_optimize_level = 3
4 reuse settings;
Procedure altered.
create or replace procedure upd_int isЯ добавил строку pragma inline (calc_int, 'YES'); для указания компилятору подменить в коде эту процедуру. Таким же образом можно указать "NO" в этом же месте, чтобы передать компилятору, что не надо подменять эту процедуру, даже если plsql_optimizer_level установлен в значение 3.
l_rate_type varchar2(1);
...
...
begin
pragma inline (calc_int, 'YES');
for ctr in 1..10000 loop
...
...
end;
Inlining делает выполнение кода быстрее. Точная степень улучшения будет зависеть, конечно же, от количества подмен, которые будут сделаны компилятором. В конце этой статьи мы рассмотрим пример с использованием inlining и увидим улучшение производительности в результате его применения.
Время компиляции
Конечно, этот процесс оптимизации затрудняет работу компилятора. Но насколько?
Чтобы ответить на этот вопрос, я взял код реального приложения, показанный ранее, и скомпилировал его в различных комбинациях с inlining/без inlining и interpreted/native. Вот время компиляции:
| Inlined | Not inlined |
Interpreted | 1.70 | 1.64 |
Native | 3.15 | 2.81 |
Результаты говорят сами за себя. При компиляции с использованием inline, время компиляции выше совсем чуть-чуть (около 4%) в режиме interpreted. В режиме native, оно вышеl,но и разница больше — около 12%. Таким образом, целесообразно компилировать приложение в режиме inline/interpreted во время разработки, а затем в режиме native на стадии окончания. Использование inlining добавляет время компиляции несущественно, поэтому при разработке это время ни на что не влияет.
Теперь мы подошли к важному вопросу: Раз код не менялся, как можно подтвердить, что код был подменён? Это можно сделать с помощью переменной сессии:
alter session set plsql_warnings = 'enable:all';Теперь, после пересоздания процедуры:
SQL> @upd_intОбратите внимание на последнюю строку, которая подтверждает, что процедура calc_int была подменена.
SP2-0804: Procedure created with compilation warnings
SQL> show errorЕсли вы хотите найти объекты, которые были скомпилированы с этим уровнем, можно выполнить запрос к представлению USER_PLSQL_OBJECT_SETTINGS:
Errors for PROCEDURE UPD_INT:
LINE/COL ERROR
-------- -----------------------------------------------------------------
7/5 PLW-06006: uncalled procedure "CALC_INT" is removed.
28/13 PLW-06005: inlining of call of procedure 'CALC_INT' was done
sql> select name, plsql_optimize_levelЕсть и похожее представление для всех объектов: DBA_PLSQL_OBJECT_SETTINGS.
2> from user_plsql_object_settings;
NAME PLSQL_OPTIMIZE_LEVEL
-------------------- --------------------
DO_CALC 2
PERFECT_TRIANGLES 2
TR_BOOKINGS_TRACK 2
TR_SALGRADE_COMP 2
UPD_INT 3
... и так далее ...
Помните, intra-unit inlining означает, что подменяются только те процедуры, которые расположены внутри блока. Внешние подпрограммы не подменяются.
Выполнение эксперимента
Теперь настало время проверить эти достоинства, повторяя эксперимент. Создадим базовую версию пакета, а затем модифицируем тип данных переменных и директивы компилятора в соответствии с концепциями, изученными перед этим.
Сначала реализуем Euclidean algorithm (алгоритм Евклида) для поиска наибольшего общего делителя для двух чисел. Вот логика функции, со страницы Wiki:
function gcd(a, b)Замерим процессорное время при выполнении пакета при различных комбинациях этих модификаций и сохраним затраченное время. Итак, создаём таблицу для сохранения процессорного времени:
if a = 0 return b
while b ? 0
if a > b
a := a - b
else
b := b - a
return a
create table times(Сделаем три столбца — native, simple и inlining — для обозначения Native compilation, чисел типа simple_integer, и inlined-кода, соответственно. "Y" в столбце означает, что код скомпилирован с использованием этой возможности. Итак, получается запись, которая выглядит так:
native char(1) check (native in ('Y', 'N')) enable,
simple char(1) check (simple in ('Y', 'N')) enable,
inlining char(1) check (inlining in ('Y', 'N')) enable,
centiseconds number not null,
constraint times_pk primary key (simple, inlining, native))
/
NATIVE SIMPLE INLINING CENTISECONDSОна показывает, что программа была скомпилирована в режиме Native compilation, но числа типа simple_integer не использовались, inlining не применялось, и что эта компиляция заняла 100 сёнтисекунд процессорного времени.
------ ------ -------- ------------
Y N N 100
Чтобы использовать только одну копию пакета, для модификации пакета применим условную компиляцию (появилась в Oracle Database 10g Release 2). Ниже показано, как создать пакет:
-- подавляем следующие ожидаемые предупреждения:Пакет имеет достаточно однострочных комментариев для самодокументации кода, поэтому здесь подробные объяснения не требуются. А в общем, функция GCD() имеет два входных параметра и возвращает наибольший общий делитель. Функция exercise_gcd() вызывает функцию GCD() и возвращает время работы процессора (CPU time) в сёнтисекундах. И public-процедураTIME_IT() вызывает функцию EXERCISE_GCD() для соответствующей степени свободы и вставляет запись в таблицу TIMES.
-- inlining of call of procedure 'gcd' was done
-- uncalled procedure "gcd" is removed.
-- unreachable code
-- keyword "native" used as a defined name
alter session set plsql_warnings = 'enable:all, disable:06002, disable:06005, disable:06006, disable:06010'
/
alter session set plsql_ccflags = 'simple:false'
/
create package gcd_test is
procedure time_it;
end gcd_test;
/
create package body gcd_test is
$if $$simple $then
subtype my_integer is simple_integer;
simple constant times.simple%type := 'y';
$else
subtype my_integer is pls_integer not null;
simple constant times.simple%type := 'n';
$end
function gcd(p1 in my_integer, p2 in my_integer) return my_integer is
v1 my_integer := p1; v2 my_integer := p2;
begin
while v2 > 0 loop
if v1 > v2 then
v1 := v1 - v2;
else
v2 := v2 - v1;
end if;
end loop;
return v1;
end gcd;
function exercise_gcd return number is
-- ожидается значение, зависящее от no_of_iterations.
expected_checksum my_integer := 74069926; -- 2475190;
no_of_iterations constant my_integer := 5000; -- 1000;
checksum my_integer := 0;
v my_integer := 0;
t0 number; t1 number;
begin
for warmup in 1..2 loop
checksum := 0;
t0 := dbms_utility.get_cpu_time();
for j in 1..no_of_iterations loop
v := gcd(j, j);
if v <> j then
raise_application_error(-20000, 'logic error: gcd(j, j) <> j');
end if;
checksum := checksum + v;
for k in (j + 1)..no_of_iterations loop
v := gcd(j, k);
if gcd(k, j) <> v then
raise_application_error(-20000, 'logic error: gcd(j, k) <> gcd(k, j)');
end if;
checksum := checksum + v;
end loop;
end loop;
if checksum <> expected_checksum then
raise_application_error(-20000, 'checksum <> expected_checksum: '||checksum);
end if;
t1 := dbms_utility.get_cpu_time();
end loop;
return t1 - t0;
end exercise_gcd;
procedure time_it is
inlining times.inlining%type;
native times.native%type;
centiseconds constant times.centiseconds%type := exercise_gcd();
begin
if lower($$plsql_code_type) = 'native' then
native := 'y';
else
native := 'n';
end if;
if $$plsql_optimize_level = 3 then
inlining := 'y';
else
inlining := 'n';
end if;
insert into times(native, simple, inlining, centiseconds)
values(time_it.native, gcd_test.simple, time_it.inlining, time_it.centiseconds);
commit;
end time_it;
end gcd_test;
/
show errors
Теперь надо несколько раз вызвать пакетную процедуру с различными параметрами и зафиксировать время работы процессора в каждом случае. Выполним это, изменив переменные условной компиляции перед тем, как компилировать пакет:
truncate table timesДля определения, насколько повышается производительность при каждом из этих сценариев, используем следующий код:
/
-- Interpreted ---------------------------------------------
-- Simple:false
-- No Inlining
alter package GCD_Test compile body
PLSQL_Code_Type = interpreted
PLSQL_CCFlags = 'Simple:false'
PLSQL_Optimize_Level = 2 /* no inlining */
reuse settings
/
begin GCD_Test.Time_It(); end;
/
-- inlining
alter package GCD_Test compile body
PLSQL_Code_Type = interpreted
PLSQL_CCFlags = 'Simple:false'
PLSQL_Optimize_Level = 3 /* inlined */
reuse settings
/
begin GCD_Test.Time_It(); end;
/
-- Числа типа simple_integer: используются
-- Без inlining
alter package GCD_Test compile body
PLSQL_Code_Type = interpreted
PLSQL_CCFlags = 'Simple:true'
PLSQL_Optimize_Level = 2
reuse settings
/
begin GCD_Test.Time_It(); end;
/
-- inlined
alter package GCD_Test compile body
PLSQL_Code_Type = interpreted
PLSQL_CCFlags = 'Simple:true'
PLSQL_Optimize_Level = 3
reuse settings
/
begin GCD_Test.Time_It(); end;
/
-- Native -------------------------------------------------
-- Числа типа simple_integer: не используются
-- Без inlining
alter package GCD_Test compile body
PLSQL_Code_Type = native
PLSQL_CCFlags = 'Simple:false'
PLSQL_Optimize_Level = 2
reuse settings
/
begin GCD_Test.Time_It(); end;
/
-- inlined
alter package GCD_Test compile body
PLSQL_Code_Type = native
PLSQL_CCFlags = 'Simple:false'
PLSQL_Optimize_Level = 3
reuse settings
/
begin GCD_Test.Time_It(); end;
/
-- Числа типа simple_integer: используются
-- Без linlining
alter package GCD_Test compile body
PLSQL_Code_Type = native
PLSQL_CCFlags = 'Simple:true'
PLSQL_Optimize_Level = 2
reuse settings
/
begin GCD_Test.Time_It(); end;
/
-- inlined
alter package GCD_Test compile body
PLSQL_Code_Type = native
PLSQL_CCFlags = 'Simple:true'
PLSQL_Optimize_Level = 3
reuse settings
/
begin GCD_Test.Time_It(); end;
/
spool timings.txtНиже показан результат. Он показывает рейтинг процессорного времени по сравнению со значением по умолчанию: без inlining, interpreted compilation, и использование pls_integer.
<<b>>declare
Interp_Pls_Integer_Noinline Times.Centiseconds%type;
Interp_Pls_Integer_Inline Times.Centiseconds%type;
Interp_Simple_Integer_Noinline Times.Centiseconds%type;
Interp_Simple_Integer_Inline Times.Centiseconds%type;
Native_Pls_Integer_Noinline Times.Centiseconds%type;
Native_Pls_Integer_Inline Times.Centiseconds%type;
Native_Simple_Integer_Noinline Times.Centiseconds%type;
Native_Simple_Integer_Inline Times.Centiseconds%type;
procedure Show_Caption(Caption in varchar2) is
begin
DBMS_Output.Put_Line(Chr(10)||Rpad('-', 60, '-')||Chr(10)||Chr(10)||Caption||Chr(10));
end Show_Caption;
procedure Show_Ratio(Var1 in varchar2, Var2 in varchar2, Ratio in number) is
begin
DBMS_Output.Put_Line(Rpad(Var1, 15)||'and '||Rpad(Var2, 14)||To_Char(Ratio, '99.99'));
end Show_Ratio;
begin
select a.Centiseconds
into b.Interp_Pls_Integer_Noinline
from Times a
where a.Native = 'N' and a.Simple = 'N' and a.Inlining = 'N';
select a.Centiseconds
into b.Interp_Pls_Integer_Inline
from Times a
where a.Native = 'N' and a.Simple = 'N' and a.Inlining = 'Y';
select a.Centiseconds
into b.Interp_Simple_Integer_Noinline
from Times a
where a.Native = 'N' and a.Simple = 'Y' and a.Inlining = 'N';
select a.Centiseconds
into b.Interp_Simple_Integer_Inline
from Times a
where a.Native = 'N' and a.Simple = 'Y' and a.Inlining = 'Y';
select a.Centiseconds
into b.Native_Pls_Integer_Noinline
from Times a
where a.Native = 'Y' and a.Simple = 'N' and a.Inlining = 'N';
select a.Centiseconds
into b.Native_Pls_Integer_Inline
from Times a
where a.Native = 'Y' and a.Simple = 'N' and a.Inlining = 'Y';
select a.Centiseconds
into b.Native_Simple_Integer_Noinline
from Times a
where a.Native = 'Y' and a.Simple = 'Y' and a.Inlining = 'N';
select a.Centiseconds
into b.Native_Simple_Integer_Inline
from Times a
where a.Native = 'Y' and a.Simple = 'Y' and a.Inlining = 'Y';
Show_Caption('Benefit of simple_integer');
Show_Ratio('Interpreted', 'no inlining', Interp_Pls_Integer_Noinline / Interp_Simple_Integer_Noinline);
Show_Ratio('Interpreted', 'inlining', Interp_Pls_Integer_Inline / Interp_Simple_Integer_Inline);
Show_Ratio('Native', 'no inlining', Native_Pls_Integer_Noinline / Native_Simple_Integer_Noinline);
Show_Ratio('Native', 'inlining', Native_Pls_Integer_Inline / Native_Simple_Integer_Inline);
Show_Caption('Benefit of inlining');
Show_Ratio('Interpreted', 'pls_integer', Interp_Pls_Integer_Noinline / Interp_Pls_Integer_Inline);
Show_Ratio('Interpreted', 'simple_integer', Interp_Simple_Integer_Noinline / Interp_Simple_Integer_Inline);
Show_Ratio('Native', 'pls_integer', Native_Pls_Integer_Noinline / Native_Pls_Integer_Inline);
Show_Ratio('Native', 'simple_integer', Native_Simple_Integer_NoInline / Native_Simple_Integer_Inline);
Show_Caption('Benefit of native');
Show_Ratio('pls_integer', 'no inlining', Interp_Pls_Integer_Noinline / Native_Pls_Integer_Noinline);
Show_Ratio('pls_integer', 'inlining', Interp_Pls_Integer_Inline / Native_Pls_Integer_Inline);
Show_Ratio('simple_integer', 'no inlining', Interp_Simple_Integer_Noinline / Native_Simple_Integer_Noinline);
Show_Ratio('simple_integer', 'inlining', Interp_Simple_Integer_Inline / Native_Simple_Integer_Inline);
end b;
/
spool off
------------------------------------------------------------Из показанных выше результатов видно, что процессорное время, потраченное на выполнение, было 14.49 для значения по умолчанию по сравнению с Native compilation с inlining и simple_integer — очень приятный результат, по любым стандартам.
Преимущества simple_integer
Интерпретирующая и без inlining 1.00
Интерпретирующая и c inlining 1.00
Native и без inlining 2.19
Native и с inlining 2.79
------------------------------------------------------------
Преимущества inlining
Интерпретирующая и pls_integer 1.07
Интерпретирующая и simple_integer 1.07
Native и pls_integer 1.16
Native и simple_integer 1.48
------------------------------------------------------------
Преимущества Native compilation
pls_integer и без inlining 4.78
pls_integer и inlining 5.18
simple_integer и без inlining 10.53
simple_integer и inlining 14.49
Заключение
Теперь вы можете оценить важность и полезность этих новых возможностей. Итог:
- Тип данных simple_integer может синтаксически использоваться там же, где и pls_integer, но возможен сброс значений и есть требование в отношении отсутствия null. Это означает, что числа типа simple_integer не могут использоваться везде. Если приложение нет превышения максимума или понижения ниже минимума, то их хорошо использовать с Native compilation.
- Достоинства simple_integer очень заметны при использовании Native compilation и минимальны при использовании interpreted compilation. Заметьте, что даже при interpreted compilation тип simple_integer больше полезен, чем вреден.
- Преимущества inlining также более заметны при использовании Native compilation по сравнению с interpreted compilation. Это труднее объяснить. Простейший, краткий код, позволяющий приблизиться к компиляции в машинные коды, имеет больше шансов на оптимизацию в режиме native, а в режиме interpreted она невозможна.
- Преимущества Native compilation больше, когда программа для этого подходит — это значит, что она не выполняет SQL и не использует Oracle number, date и так далее. Заметьте, что даже когда эти условия соблюдены, выигрыш может существенно различаться.
- Выбор Native compilation и inlining в Oracle Database 11g не требует умственных усилий. Есть только одна причина против, это дополнительное время компиляции (может быть в процессе разработки на ранних стадиях).
- В перспективе использование simple_integer будет редким. Однако когда момент настанет, им следует воспользоваться.