воскресенье, декабря 24, 2006

Что мы теряем на Exception'ах в perl?

Наверняка многие интересовались насколько падает производительность при использовании Exception'ов по сравнению с обработкой ошибок по коду возращаемого значения.

Для начала возьмём к примеру Exception::Class и посмотрим сколько мы теряем используя генерацию исключений. Для этого напишем три функции в одной(Err) будет возвращать в случае ошибки код ошибки(в нашем случае 0), во второй(Exc) будем генерить исключение-родителя, в третьей(childExc) будем генерить исключение-потомка(просто в надежде увидеть какое-то чудо :) ).



#!/usr/bin/perl

use strict;
use warnings;
use Carp qw(croak);
use Benchmark qw(:all);
use Exception::Class( 'TestExceptionBase',
'TestExceptionChild' => { isa => 'TestExceptionBase' } );

sub ProcessDub {
my $dub;
for (my $i=0;$i<100;$i++) j =" $i**2;" dub =" $j;" ret =" (" ret =" (">new() ) if ( !$ret );

return $ret;
}

sub childExc {
my ($i,$delay) = @_;

my $ret;
$ret = ( $i % 2 );
ProcessDub() if ($delay);

croak( TestExceptionChild->new() ) if ( !$ret );

return $ret;
}

my $ok_value = 1;
my $err_value = 2;
my $delay;

print "Run with no delay (with error): \n\n";
$delay = 0;

cmpthese(
-2,
{ 'throw' => sub {
eval { Exc( $err_value, $delay); };
},
'errorCode' => sub {
Err( $err_value, $delay );
},
'throwChild' => sub {
eval { childExc( $err_value, $delay ); };
}
}
);

print "\n\n";
print "Run with no delay (without error): \n\n";

cmpthese(
-2,
{ 'throw' => sub {
eval { Exc( $ok_value, $delay ); };
},
'errorCode' => sub {
Err( $ok_value, $delay );
},
'throwChild' => sub {
eval { childExc( $ok_value, $delay ); };
}
}
);

print "\n\n";
print "Run with delay (with error): \n\n";
$delay = 1;

cmpthese(
-2,
{ 'throw' => sub {
eval { Exc( $err_value, $delay); };
},
'errorCode' => sub {
Err( $err_value, $delay );
},
'throwChild' => sub {
eval { childExc( $err_value, $delay ); };
}
}
);

print "\n\n";
print "Run with delay (without error): \n\n";

cmpthese(
-2,
{
'throw' => sub {
eval { Exc( $ok_value, $delay ); };
},
'errorCode' => sub {
Err( $ok_value, $delay );
},
'throwChild' => sub {
eval { childExc( $ok_value, $delay ); };
}
}
);


В результате запуска(запускал на резиновой женщине виртуальной машине, поэтому результаты просто для очень грубой оценки) получим следующие результаты:



Run with no delay (with error):

Rate throw throwChild errorCode
throw 821/s -- -6% -100%
throwChild 877/s 7% -- -100%
errorCode 628563/s 76506% 71541% --


Run with no delay (without error):

Rate throw throwChild errorCode
throw 438881/s -- -8% -24%
throwChild 475773/s 8% -- -18%
errorCode 579964/s 32% 22% --


Run with delay (with error):

Rate throw throwChild errorCode
throw 806/s -- -1% -93%
throwChild 813/s 1% -- -93%
errorCode 11817/s 1365% 1353% --


Run with delay (without error):

Rate errorCode throw throwChild
errorCode 11727/s -- -2% -3%
throw 11981/s 2% -- -1%
throwChild 12042/s 3% 1% --


Результаты с "пустой"(with no delay) функцией делались для оценки теоретических потерь - на практике у вас будут функции с телом. Итак в самом худшем случае который в реальном мире не встречается нам грозит просто запредельная потеря скорости(примерно в 700 раз) при генерации исключения, и замедление всего на 20-30% если ошибки не произошло.


Также проводились измерения для функции имеющей хоть какое-то отношение к реальному миру. В качестве заглушки там используется возведение чисел с 0 по 99 в квадрат. Для этой функции замедление при ошибке примерно в 10 раз, и всего на 2-3% если ошибки не произошло.


Вывод. Если у вас не какой-то узкоспециализированный случай, когда много функций/методов в которых почти нет тела как такового, и когда вы используете исключения с умом (я считаю, что в программе что-то непродуманно, если генерация исключений в штатном режиме превышает определённый порог), то можно не терзаться мыслью о потере скорости из-за исключений, а наоборот наслаждаться всеми плюсами их использования.


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