Профайлил тут свой скрипты на предмет производительности (корни проблемы тут - http://programmingmindstream.blogspot.ru/2015/09/1164-aqtime.html) и "с удивлением" (который раз) столкнулся с тем, что бросание исключения (часто) в бизнес логике ведёт к потере производительности в разы.
Например код:
Почему?
А потому, что BREAK на самом деле устроен так:
https://bitbucket.org/lulinalex/mindstream/src/7deb4ed1ebc5a138c2a90cc69f14bed0847b09a1/Examples/1165/BasicsPack.pas?at=B284&fileviewer=file-view-default
И основное время "съедает" создание/удаление объекта. Которое вообще говоря "очень недёшево".
Вот ассемблерный код:
Именно поэтому я когда-то делал собственное кеширование объектов.
Вот пример:
https://bitbucket.org/lulinalex/mindstream/src/7deb4ed1ebc5a138c2a90cc69f14bed0847b09a1/Examples/1165/l3UnknownPrim.imp.pas?at=B284&fileviewer=file-view-default
А в данном случае я сделал исключения синглетонами и использовал их так:
И это (отчасти) решило проблему с производительностью.
Всё это конечно "про скрипты" написано.
Да и нашёл я это лишь на больших объёмах обрабатываемых данных. Таких как модель большого проекта (десятки тысяч классов и 12-15 миллионов строк кода).
И казалось бы delphi-разработчика это не касается.
Но я хочу лишь подчеркнуть, что "бросание исключений" это дело "недешёвое". И если вы используете исключения в бизнес-логике без надобности (а я такое видел) в качестве "особого результата функции", то вы обречены на просаживание производительности.
Если исключения кидаются сравнительно часто по сравнению, с "обычным кодом".
Я бы ещё конечно про ARC и исключения написал бы. Но пожалуй не буду.
НЕ думаю, что буду понят правильно. У нас ведь в "мейнстриме" ARC.
Хотя судя по коду - исключения ТОЖЕ подвержены ARC'у.
Но похоже, что разработчики из Embarcadero с проблемами ещё не столкнулись.
Так что будем считать, что это "мои личные фантомные боли".
Например код:
ARRAY FUNCTION LIST
OBJECT IN anObject
^ IN aFunctor
OBJECT VAR l_Element
l_Element := anObject
Result := [
while true
begin
l_Element := ( l_Element aFunctor DO )
if ( l_Element pop:object:IsNil ) then
BREAK
l_Element
end
]
; // LIST
Работает в разы медленнее чем аналог:
ARRAY FUNCTION LIST
OBJECT IN anObject
^ IN aFunctor
OBJECT VAR l_Element
l_Element := anObject
BOOLEAN VAR l_NeedDo
l_NeedDo := true
Result := [
while l_NeedDo
begin
l_Element := ( l_Element aFunctor DO )
if ( l_Element pop:object:IsNil ) then
begin
l_NeedDo := false
end
else
l_Element
end
]
; // LIST
Почему?
А потому, что BREAK на самом деле устроен так:
https://bitbucket.org/lulinalex/mindstream/src/7deb4ed1ebc5a138c2a90cc69f14bed0847b09a1/Examples/1165/BasicsPack.pas?at=B284&fileviewer=file-view-default
procedure TkwBREAK.DoDoIt(const aCtx: TtfwContext);
//#UC START# *4DAEEDE10285_9FA400CD8713_var*
//#UC END# *4DAEEDE10285_9FA400CD8713_var*
begin
//#UC START# *4DAEEDE10285_9FA400CD8713_impl*
raise EtfwBreak.Create('Выход из цикла');
//#UC END# *4DAEEDE10285_9FA400CD8713_impl*
end;//TkwBREAK.DoDoIt
И основное время "съедает" создание/удаление объекта. Которое вообще говоря "очень недёшево".
Вот ассемблерный код:
Именно поэтому я когда-то делал собственное кеширование объектов.
Вот пример:
https://bitbucket.org/lulinalex/mindstream/src/7deb4ed1ebc5a138c2a90cc69f14bed0847b09a1/Examples/1165/l3UnknownPrim.imp.pas?at=B284&fileviewer=file-view-default
class function _l3UnknownPrim_.NewInstance: TObject;
//override;
{* - функция распределения памяти под экземпляр объекта. Перекрыта, для контроля за памятью на объекты. }
{$IfDef _UnknownNeedL3}
var
l_System : Tl3System;
{$EndIf _UnknownNeedL3}
begin
{$IfDef _UnknownNeedL3}
l_System := Tl3System(g_l3System);
if (l_System = nil) then
begin
if not l3MemUtilsDown{l3SystemDown} then
begin
l_System := l3System;
// if (l_System <> nil) then
// l_System.Stack2Log('Возможная непарность NewInstance/FreeInatance');
end;//not l3SystemDown
end;//l_System = nil
Assert((l_System <> nil) OR not Cacheable);
if (l_System <> nil) AND l_System.CanCache AND Cacheable then
begin
Result := GetFromCache;
if (Result <> nil) then
begin
_l3UnknownPrim_(Result).InitAfterAlloc;
Exit;
end;//Result <> nil
end;{l_System.CanCache}
{$EndIf _UnknownNeedL3}
Result := AllocInstanceMem;
_l3UnknownPrim_(Result).Use;
_l3UnknownPrim_(Result).InitAfterAlloc;
{$IfDef _UnknownNeedL3}
{$IfDef l3TraceObjects}
if (l_System <> nil) then
l_System.RegisterObject(Result, Cacheable);
{$EndIf l3TraceObjects}
{$EndIf _UnknownNeedL3}
end;
А в данном случае я сделал исключения синглетонами и использовал их так:
procedure TkwBREAK.DoDoIt(const aCtx: TtfwContext); //#UC START# *4DAEEDE10285_9FA400CD8713_var* //#UC END# *4DAEEDE10285_9FA400CD8713_var* begin //#UC START# *4DAEEDE10285_9FA400CD8713_impl* raise EtfwBreak.Instance; //#UC END# *4DAEEDE10285_9FA400CD8713_impl* end;//TkwBREAK.DoDoIt
И это (отчасти) решило проблему с производительностью.
Всё это конечно "про скрипты" написано.
Да и нашёл я это лишь на больших объёмах обрабатываемых данных. Таких как модель большого проекта (десятки тысяч классов и 12-15 миллионов строк кода).
И казалось бы delphi-разработчика это не касается.
Но я хочу лишь подчеркнуть, что "бросание исключений" это дело "недешёвое". И если вы используете исключения в бизнес-логике без надобности (а я такое видел) в качестве "особого результата функции", то вы обречены на просаживание производительности.
Если исключения кидаются сравнительно часто по сравнению, с "обычным кодом".
Я бы ещё конечно про ARC и исключения написал бы. Но пожалуй не буду.
НЕ думаю, что буду понят правильно. У нас ведь в "мейнстриме" ARC.
Хотя судя по коду - исключения ТОЖЕ подвержены ARC'у.
Но похоже, что разработчики из Embarcadero с проблемами ещё не столкнулись.
Так что будем считать, что это "мои личные фантомные боли".

Комментариев нет:
Отправить комментарий