Аруп Нанда
Эта статья рассказывает о том, как inline-подмена, Real native compilation и использование simple_integer могут улучшить производительность кода. Oracle Database 11g представляет несколько отличных новых возможностей для повышения производительности PL/SQL-кода, однако наиболее существенные из них - это native compilation (компиляция с получением исполняемого кода) и intra-unit inlining (подмена).
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. Я измерил время компиляции для каждого случая, оно показано ниже (в секундах).
|
10 g |
11 g |
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
*
ERROR at line 1:
ORA-01426: numeric overflow
ORA-06512: at line 4
Ошибка очевидна и вполне уместна; вы попытались превысить максимальное значение, допустимое для типа данных. Если вместо pls_integer использовать simple_integer: declare 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
/* 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() на код calc_int(), получится более быстрая программа, как показано ниже:
create or replace procedure upd_int is
/* 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;
/
Этот переделанный код отличается от исходного только в части кода для вычисления баланса, который теперь внутри цикла, а не в процедуре calc_int().
Заметьте, что новая версия может быть быстрее, но это не очень хороший пример практики кодирования. Часть кода, выполняющая вычисление баланса, выполняется один раз для каждой итерации цикла для месяцев, а затем и для каждого номера счёта. Так как эта часть кода повторяется, она более удобна для размещения отдельно, как показано в предыдущей версии upd_int, в процедуре (calc_int). Этот подход делает код модульным, легким в поддержке, и реально читаемым - но также менее эффективным.
Поэтому как можно достичь примирения конфликтующих способов создания кода, сделав код модульным и одновременно быстрым? Так, а можно ли написать код, используя модульный подход (как в первой версии upd_int), а затем позволить компилятору PL/SQL "оптимизировать" его, чтобы он стал выглядеть, как во второй версии кода?
Это можно сделать в Oracle Database 11g. Всё, что требуется сделать - перекомпилировать процедуру с более высоким уровнем оптимизации PL/SQL. Этого можно достичь двумя способами:
create or replace procedure upd_int is
l_rate_type varchar2(1);
...
...
begin
pragma inline (calc_int, 'YES');
for ctr in 1..10000 loop
...
...
end;
Я добавил строку pragma inline (calc_int, 'YES'); для указания компилятору подменить в коде эту процедуру. Таким же образом можно указать "NO" в этом же месте, чтобы передать компилятору, что не надо подменять эту процедуру, даже если plsql_optimizer_level установлен в значение 3.
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
SP2-0804: Procedure created with compilation warnings
Обратите внимание на последнюю строку, которая подтверждает, что процедура calc_int была подменена.
SQL> show error
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
Если вы хотите найти объекты, которые были скомпилированы с этим уровнем, можно выполнить запрос к представлению USER_PLSQL_OBJECT_SETTINGS:
sql> select name, plsql_optimize_level
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
... и так далее ...
Есть и похожее представление для всех объектов: DBA_PLSQL_OBJECT_SETTINGS.
Помните, 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 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 - для обозначения Native compilation, чисел типа simple_integer, и inlined-кода, соответственно. "Y" в столбце означает, что код скомпилирован с использованием этой возможности. Итак, получается запись, которая выглядит так:
NATIVE SIMPLE INLINING CENTISECONDS
------ ------ -------- ------------
Y N N 100
Она показывает, что программа была скомпилирована в режиме Native compilation, но числа типа simple_integer не использовались, inlining не применялось, и что эта компиляция заняла 100 сёнтисекунд процессорного времени.
Чтобы использовать только одну копию пакета, для модификации пакета применим условную компиляцию (появилась в Oracle Database 10g Release 2). Ниже показано, как создать пакет:
-- подавляем следующие ожидаемые предупреждения:
-- 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
Пакет имеет достаточно однострочных комментариев для самодокументации кода, поэтому здесь подробные объяснения не требуются. А в общем, функция GCD() имеет два входных параметра и возвращает наибольший общий делитель. Функция exercise_gcd() вызывает функцию GCD() и возвращает время работы процессора (CPU time) в сёнтисекундах. И public-процедураTIME_IT() вызывает функцию EXERCISE_GCD() для соответствующей степени свободы и вставляет запись в таблицу TIMES.
Теперь надо несколько раз вызвать пакетную процедуру с различными параметрами и зафиксировать время работы процессора в каждом случае. Выполним это, изменив переменные условной компиляции перед тем, как компилировать пакет:
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
<<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
Ниже показан результат. Он показывает рейтинг процессорного времени по сравнению со значением по умолчанию: без inlining, interpreted compilation, и использование pls_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
Из показанных выше результатов видно, что процессорное время, потраченное на выполнение, было 14.49 для значения по умолчанию по сравнению с Native compilation с inlining и simple_integer - очень приятный результат, по любым стандартам.
Заключение
Теперь вы можете оценить важность и полезность этих новых возможностей. Итог:
- Тип данных 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 будет редким. Однако когда момент настанет, им следует воспользоваться.
Ссылки по теме