Try vs If

Померяем, как сказывается Try на производительности.

 Дисклеймер. 
Надеюсь, вы понимаете, что к любым цифрам в Интернете
следует относиться с осторожностью.

Для начала, проверим, а медленнее ли работает код в внутри try.

program SpeedTest;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.Classes,
  System.SysUtils,
  System.Diagnostics;

const
  cTestSize  = 1000000000;

var
  t : TStopwatch;

function ClearCode:integer;
var
  i, j : Integer;
  k, m : integer;
begin
  j := 1;
  k := 2;
  m := 3;
  t := TStopwatch.StartNew;
  for i := 1 to cTestSize do
  begin
    k := m+k;
    m := k-m;
    k := k-m;
  end;
  k:=0;
  m:=0;
  t.Stop;
  result := t.ElapsedMilliseconds;
end;

function TryFinallyCode:integer;
var
  i, j : Integer;
  k, m : integer;
begin
  j := 1;
  k := 2;
  m := 3;
  t := TStopwatch.StartNew;
  try
    for i := 1 to cTestSize do
    begin
      k := m+k;
      m := k-m;
      k := k-m;
    end;
  finally
    k:=0;
    m:=0;
  end;
  t.Stop;
  result := t.ElapsedMilliseconds;
end;

function TryExceptCode:integer;
var
  i, j : Integer;
  k, m : integer;
begin
  j := 1;
  k := 2;
  m := 3;
  t := TStopwatch.StartNew;
  try
    for i := 1 to cTestSize do
    begin
      k := m+k;
      m := k-m;
      k := k-m;
    end;
  except

  end;
  k:=0;
  m:=0;

  t.Stop;
  result := t.ElapsedMilliseconds;
end;

begin

  try
    WriteLn('Clear code    : ', FormatFloat('0.000', ClearCode / 1000));
    WriteLn('Try finally   : ', FormatFloat('0.000', TryFinallyCode / 1000));
    WriteLn('Try except    : ', FormatFloat('0.000', TryExceptCode / 1000));
  except
    on e: exception do
      writeln(e.Message);
  end;
  writeln('I''m finished.');
  readln;
end.

Получаем:

Clear code    : 6.823
Try finally   : 6.841
Try except    : 6.819
I'm finished.

Разницу менее 0.5% считаем несущественной.  Код внутри Try не замедляется.

Добавим функцию, с Try внутри цикла:

function TryExceptInCode:integer;
var
  i, j : Integer;
  k, m : integer;
begin
  j := 1;
  k := 2;
  m := 3;
  t := TStopwatch.StartNew;

  for i := 1 to cTestSize do
  begin
    try
      k := m+k;
      m := k-m;
      k := k-m;
    except

    end;
  end;

  k:=0;
  m:=0;

  t.Stop;
  result := t.ElapsedMilliseconds;
end;

На выходе получаем:

Clear code    : 6.792
Try finally   : 6.790
Try except    : 6.823
Try except in : 7.159
I'm finished.

Миллиард входов в блок Try замедлил код на 0.3-0.4 сек. Уже что-то. Если счёт идёт на милисекунды, try внутри цикла - это место, которое можно оптимизировать.

И наконец, насколько выгоднее избегать еxception-ов, а не обрабатывать их.
Добавим ещё две функции:

const   
      cDivSize = 1000000001; 

function IfCode:integer;
var
  i, j : Integer;
  k, m : integer;
begin
  k := 2;
  m := 3000;

  t := TStopwatch.StartNew;

  for i := 1 to cTestSize do
  begin
    j:= i mod cDivSize;
    try
      m := 3000;
      if j<>0 then k := m div j
              else k := 1;
    except
    end;
  end;

  t.Stop;
  result := t.ElapsedMilliseconds;
end;

function ExceptCode:integer;
var
  i, j : Integer;
  k, m : integer;
begin
  k := 2;
  m := 3000;

  t := TStopwatch.StartNew;

  for i := 1 to cTestSize do
  begin
    j:= i mod cDivSize;
    try
      m := 3000;
      k := m div j
    except
      k := 1;
    end;
  end;

  t.Stop;
  result := t.ElapsedMilliseconds;
end;

Миллиард циклов вычислений, на каждой cDivSize-й итерации потенциальное деление на ноль.

В первой функции ситуация обходится if-ом. Дополнительный миллиард if-ов. 

В ExceptCode обрабатывается исключение. 

Сначала выставим cDivSize = 1000000001;. Исключений не произойдёт, убедимся, что миллиард if-ов вносят вклад:

If        : 10.534
Exception : 10.259
I'm finished.

Наблюдаем разницу в 0.3 сек.

Теперь подберём такое сDivSize, чтобы время работы функций стало близким.

При cDivSize = 17000000; получаем:

If        : 10.830
Exception : 10.847
I'm finished.

По временным затратам, одно обработанное исключение, эквивалентно 17000000 (семнадцати миллионам) If-ов... 

 

Все тесты компилировались на Delphi 10.1 с отключенной оптимизацией.

 

Метки: