Профайлил тут свой скрипты на предмет производительности (корни проблемы тут - 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 с проблемами ещё не столкнулись.
Так что будем считать, что это "мои личные фантомные боли".
Комментариев нет:
Отправить комментарий