пятница, 25 сентября 2015 г.

#1165. Про исключения и производительность

Профайлил тут свой скрипты на предмет производительности (корни проблемы тут - http://programmingmindstream.blogspot.ru/2015/09/1164-aqtime.html) и "с удивлением" (который раз) столкнулся с тем, что бросание исключения (часто) в бизнес логике ведёт к потере производительности в разы.

Например код:

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 с проблемами ещё не столкнулись.

Так что будем считать, что это "мои личные фантомные боли".

Комментариев нет:

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