View mode: basic / threaded / horizontal-split · Log in · Help
June 29, 2005
Re: Are D Exceptions Fast and Efficient?
"AJG" <AJG_member@pathlink.com> wrote in message
news:d9t6a2$2n1h$1@digitaldaemon.com...
> >> What is the cost (performance & memory-wise) of throwing and catching
an
> >> exception in D? First, why do I ask this:
> >
> >D's exception handling mechanism is the same as C++'s is, as D and C++
share
> >the same back end. It'll still be an order or magnitude or more slower
than,
> >say, returning an error code.
>
> Thanks for the info. I was wondering whether this has to be the case.
>
> - Is there a way to make exceptions somewhat lighter (and faster)?

I won't say no, but a lot of engineers have looked at the way C++ exceptions
are done, and if there was an easier way then someone probably would have
implemented it by now.

> - Will D always share the C++ backend for exceptions, or is it at least
> theoretically possible to eventually substitute it with a more performant
> version?

It's possible, but it would have to be a big improvement to justify it.

> - What about making the Error class a full-blown stack unwind with stack
trace
> and everything (i.e. slow), and the Exception class something lighter akin
to an
> error code (i.e. fast)? I think this would be _immensely_ powerful and
useful;
> it would be a clean, simple way to unify all error handling into one
framework.

I'm reluctant to have two different exception schemes active in the
compiler. It took me long enough to understand and debug the one!

>
> Thanks for listening,
> --AJG.
>
> PS: I will post an example re: exceptions in a second, could you take a
> look-see?
>
>
>
>
> ================================
> 2B || !2B, that is the question.
June 29, 2005
Re: Are D Exceptions Fast and Efficient?
Ok, so I figured why not try and time the exception handling. Here is what  
I came up with:

import std.stdio;
import std.string;
import std.c.time;
extern(C)
{
	struct timeb
	{
		time_t time;
		ushort millitm;
		short timezone, dstflag;
	}

	void _ftime(timeb *);
	alias _ftime ftime;
}

double diff(timeb* start, timeb* finish)
{
	double d;
	
	d =  finish.time-start.time;
	d += (cast(double)(finish.millitm-start.millitm))/1000.0;
	return d;
}

const int ITERATIONS = 1000000;

void throws(int i)
{
	throw new Exception(toString(i));
}

void intermediate(int i)
{
	throws(i);
}

void intermediate2(int i)
{
	intermediate(i);
}

void intermediate3(int i)
{
	intermediate2(i);
}

int returns(int i)
{
	return i;
}

void main()
{
	timeb start;
	timeb finish;
	int r;
	
	ftime(&start);	
	for(int i = 0; i < ITERATIONS; i++) {
		try {
			r = returns(i);
		}
		catch (Exception e) {
		}
	}
	ftime(&finish);
	writefln(ITERATIONS," error codes returned in  
(",diff(&start,&finish),")");
	
	ftime(&start);
	for(int i = 0; i < ITERATIONS; i++) {
		try {
			throws(i);
		}
		catch (Exception e) {
		}
	}
	ftime(&finish);
	writefln(ITERATIONS," exceptions thrown and caught in  
(",diff(&start,&finish),")");
	
	ftime(&start);
	for(int i = 0; i < ITERATIONS; i++) {
		try {
			intermediate(i);
		}
			catch (Exception e) {
		}
	}
	ftime(&finish);
	writefln(ITERATIONS," exceptions thrown and caught in  
(",diff(&start,&finish),")");

	ftime(&start);
	for(int i = 0; i < ITERATIONS; i++) {
		try {
			intermediate2(i);
		}
			catch (Exception e) {
		}
	}
	ftime(&finish);
	writefln(ITERATIONS," exceptions thrown and caught in  
(",diff(&start,&finish),")");

	ftime(&start);
	for(int i = 0; i < ITERATIONS; i++) {
		try {
			intermediate3(i);
		}
			catch (Exception e) {
		}
	}
	ftime(&finish);
	writefln(ITERATIONS," exceptions thrown and caught in  
(",diff(&start,&finish),")");
}

Giving me these results:

C:\Library\D\src\temp>extime
1000000 error codes returned in (0.016)
1000000 exceptions thrown and caught in (7.125)
1000000 exceptions thrown and caught in (6.969)
1000000 exceptions thrown and caught in (6.969)
1000000 exceptions thrown and caught in (7.109)

In other words approx 7s for exception handling and 16 milliseconds for  
return codes (likely this equals the time required to call the functions  
in both cases). Exception handling is then approximately 437 times slower.  
A single exception took approx 7/1000 of a milliseccond.

Regan

On Wed, 29 Jun 2005 05:06:02 +0000 (UTC), AJG <AJG_member@pathlink.com>  
wrote:

> Hi Jarrett (and others too, please do read on),
>
> Thanks for the info. I was hoping some of the things in the spec were  
> outdated,
> and I still hope it's the case, because exceptions are too powerful to  
> relagate
> to exceptional situations only.
>
> I think in a way the spec contradicts itself. First, it list all the  
> things it
> wants to eliminate:
> - Returning a NULL pointer.
> - Returning a 0 value.
> - Returning a non-zero error code.
> - Requiring errno to be checked.
> - Requiring that a function be called to check if the previous function  
> failed
>
> But then it goes on to say what you quoted, which makes it all  
> irrelevant,
> because you are almost never going to encounter these "errors" anyway.
>
>> But really, the question is - why would you design an application to  
>> throw
>> an exception for anything _other_ than absolutely exceptional  
>> circumstances?
>> I think it's more of a design issue than anything.
>
> I disagree here. I think errors happen _all the time_, and they _are_  
> part of
> the normal program flow. If we use exceptions only as recommended (a very
> limited subset of errors, the exceptional "catastrophic" ones), we are  
> back to
> the very "tedious error handling code" we set out to remove in the first  
> place.
> It's back to the same ol' dirty clutter.
>
> Let me give you an example. Suppose I am building an application that  
> requires
> the user to input a sequence of integers, and it does some  processing  
> with
> them; say, from the command-line. Here it is, with no "handling" in  
> place at
> all:
>
> # import std.conv;
> # alias char[] string;
> #
> # int processInt(int input) {
> #     int output;
> #     // Processing.
> #     return (output);
> # }
> #
> # main(string[] args) {
> #     foreach (int index, string arg; args[1 .. length]) {
> #         int temp = toInt(arg);
> #         int result = processInt(temp);
> #         printf("[#%d] %d => %d\n", index, temp, result);
> #     }
> # }
>
> Now, in an ideal world, the user will enter perfectly formatter integers,
> without a single mistake, and it  would all be dandy. But that's not  
> going to
> happen. And you _know_ that, so you _expect_ an error of this kind. It  
> _is_ part
> of  the regular program flow.
>
> Luckily for us, exceptions come into play and save this "quick-and-dirty"
> utility. If you enter a bunch of letters as an integer, toInt will not  
> choke and
> segfault. It will throw an exception, which prevents it from going  
> further and
> damaging anything or producing erroneous output.
>
> Since we did not set up any handling (a catch), the program will exit if  
> any
> input is badly formatted. However, we can take this a step further and  
> make the
> utility more robust and more useful:
>
> # import std.conv;
> # alias char[] string;
> #
> # int processInt(int input) {
> #     int output;
> #     // Processing.
> #     return (output);
> # }
> #
> # main(string[] args) {
> #     foreach (int index, string arg; args[1 .. length]) {
> #         try {
> #             int temp = toInt(arg);
> #             int result = processInt(temp);
> #             printf("[#%d] %d => %d\n", index, temp, result);
> #         catch (Exception e) {
> #             printf("Badly formatted input [#%d]=%.*s\n", index, arg);
> #             printf("Exception says: %.*s\n", e.toString);
> #         }
> #     }
> # }
>
> Now, the program will catch any errors neatly, and continue normal  
> operation
> (which is perfectly valid). Now ask yourself, how many times will a  
> human being
> type a number incorrectly? I think we know the answer is _all the time_.
>
> What does this mean? We should _use_ the exception mechanism to replace  
> all
> error handling and checking, but in order to do this, it must be  
> relatively
> fast. Maybe not as fast as a simple return code, but not an order of  
> magnitude
> slower either. In this example it doesn't matter much, but what if it's a
> million inputs? or what if it's a server handling hundreds of thousands  
> of
> requests?
>
> This is all just IMHO, but I think it makes sense. Look at the  
> traditional
> alternative:
>
> You must manually check all inputs beforehand, before even attempting to  
> convert
> them, to see if they are perfectly formatted. You must introduce an  
> error code
> return and move the output to its own ref parameter, because the error  
> code
> could be mistaken for correct output.
>
> I think this is futile and redundant; you would be essentially  
> re-writing the
> toInt function yourself just so that it won't throw an exception. Must  
> one
> re-invent the wheel?
>
> The point of this whole thing: Exceptions are good. They are simple to  
> use and
> powerful. They make you code neater and reduce useless clutter.  
> Moreover, they
> represent things that occur commonly. So, we should make use of them  
> whenever
> possible, and D should make them fast so that we can do this without  
> incurring a
> penalty.
>
> Thanks for listening,
> --AJG.
>
>>
>> "AJG" <AJG_member@pathlink.com> wrote in message
>> news:d9s10t$1lid$1@digitaldaemon.com...
>>> What is the cost (performance & memory-wise) of throwing and catching  
>>> an
>>> exception in D?
>>
>> I refer you to the "Error handling" section of D's spec
>> (http://www.digitalmars.com/d/index.html):
>>
>> "
>> Errors are not part of the normal flow of a program. Errors are  
>> exceptional,
>> unusual, and unexpected.
>> D exception handling fits right in with that.
>> Because errors are unusual, execution of error handling code is not
>> performance critical.
>> Exception handling stack unwinding is a relatively slow process.
>> The normal flow of program logic is performance critical.
>> Since the normal flow code does not have to check every function call  
>> for
>> error returns, it can be realistically faster to use exception handling  
>> for
>> the errors.
>> "
>>
>> For one, D assumes that exceptions are very unusual circumstances.  For  
>> two,
>> it says that "error handling code is not performance critical."
>>
>> Now, it won't pause your app for a few seconds when throwing an  
>> exception
>> like in .NET, but it won't be _as fast_ as the rest of your code.
>
>
June 29, 2005
Re: Are D Exceptions Fast and Efficient?
I have no idea of the validity of these findings. I really have no idea  
what exactly exception handling does behind the scenes, I was going to  
experiment with adding variables to the stack, and heap in the functions  
to see if it made any difference.

Regan

On Wed, 29 Jun 2005 21:03:53 +1200, Regan Heath <regan@netwin.co.nz> wrote:
> Ok, so I figured why not try and time the exception handling. Here is  
> what I came up with:
>
> import std.stdio;
> import std.string;
> import std.c.time;
> extern(C)
> {
> 	struct timeb
> 	{
> 		time_t time;
> 		ushort millitm;
> 		short timezone, dstflag;
> 	}
>
> 	void _ftime(timeb *);
> 	alias _ftime ftime;
> }
>
> double diff(timeb* start, timeb* finish)
> {
> 	double d;
> 	
> 	d =  finish.time-start.time;
> 	d += (cast(double)(finish.millitm-start.millitm))/1000.0;
> 	return d;
> }
>
> const int ITERATIONS = 1000000;
>
> void throws(int i)
> {
> 	throw new Exception(toString(i));
> }
>
> void intermediate(int i)
> {
> 	throws(i);
> }
>
> void intermediate2(int i)
> {
> 	intermediate(i);
> }
>
> void intermediate3(int i)
> {
> 	intermediate2(i);
> }
>
> int returns(int i)
> {
> 	return i;
> }
>
> void main()
> {
> 	timeb start;
> 	timeb finish;
> 	int r;
> 	
> 	ftime(&start);	
> 	for(int i = 0; i < ITERATIONS; i++) {
> 		try {
> 			r = returns(i);
> 		}
> 		catch (Exception e) {
> 		}
> 	}
> 	ftime(&finish);
> 	writefln(ITERATIONS," error codes returned in  
> (",diff(&start,&finish),")");
> 	
> 	ftime(&start);
> 	for(int i = 0; i < ITERATIONS; i++) {
> 		try {
> 			throws(i);
> 		}
> 		catch (Exception e) {
> 		}
> 	}
> 	ftime(&finish);
> 	writefln(ITERATIONS," exceptions thrown and caught in  
> (",diff(&start,&finish),")");
> 	
> 	ftime(&start);
> 	for(int i = 0; i < ITERATIONS; i++) {
> 		try {
> 			intermediate(i);
> 		}
> 			catch (Exception e) {
> 		}
> 	}
> 	ftime(&finish);
> 	writefln(ITERATIONS," exceptions thrown and caught in  
> (",diff(&start,&finish),")");
>
> 	ftime(&start);
> 	for(int i = 0; i < ITERATIONS; i++) {
> 		try {
> 			intermediate2(i);
> 		}
> 			catch (Exception e) {
> 		}
> 	}
> 	ftime(&finish);
> 	writefln(ITERATIONS," exceptions thrown and caught in  
> (",diff(&start,&finish),")");
>
> 	ftime(&start);
> 	for(int i = 0; i < ITERATIONS; i++) {
> 		try {
> 			intermediate3(i);
> 		}
> 			catch (Exception e) {
> 		}
> 	}
> 	ftime(&finish);
> 	writefln(ITERATIONS," exceptions thrown and caught in  
> (",diff(&start,&finish),")");
> }
>
> Giving me these results:
>
> C:\Library\D\src\temp>extime
> 1000000 error codes returned in (0.016)
> 1000000 exceptions thrown and caught in (7.125)
> 1000000 exceptions thrown and caught in (6.969)
> 1000000 exceptions thrown and caught in (6.969)
> 1000000 exceptions thrown and caught in (7.109)
>
> In other words approx 7s for exception handling and 16 milliseconds for  
> return codes (likely this equals the time required to call the functions  
> in both cases). Exception handling is then approximately 437 times  
> slower. A single exception took approx 7/1000 of a milliseccond.
>
> Regan
>
> On Wed, 29 Jun 2005 05:06:02 +0000 (UTC), AJG <AJG_member@pathlink.com>  
> wrote:
>
>> Hi Jarrett (and others too, please do read on),
>>
>> Thanks for the info. I was hoping some of the things in the spec were  
>> outdated,
>> and I still hope it's the case, because exceptions are too powerful to  
>> relagate
>> to exceptional situations only.
>>
>> I think in a way the spec contradicts itself. First, it list all the  
>> things it
>> wants to eliminate:
>> - Returning a NULL pointer.
>> - Returning a 0 value.
>> - Returning a non-zero error code.
>> - Requiring errno to be checked.
>> - Requiring that a function be called to check if the previous function  
>> failed
>>
>> But then it goes on to say what you quoted, which makes it all  
>> irrelevant,
>> because you are almost never going to encounter these "errors" anyway.
>>
>>> But really, the question is - why would you design an application to  
>>> throw
>>> an exception for anything _other_ than absolutely exceptional  
>>> circumstances?
>>> I think it's more of a design issue than anything.
>>
>> I disagree here. I think errors happen _all the time_, and they _are_  
>> part of
>> the normal program flow. If we use exceptions only as recommended (a  
>> very
>> limited subset of errors, the exceptional "catastrophic" ones), we are  
>> back to
>> the very "tedious error handling code" we set out to remove in the  
>> first place.
>> It's back to the same ol' dirty clutter.
>>
>> Let me give you an example. Suppose I am building an application that  
>> requires
>> the user to input a sequence of integers, and it does some  processing  
>> with
>> them; say, from the command-line. Here it is, with no "handling" in  
>> place at
>> all:
>>
>> # import std.conv;
>> # alias char[] string;
>> #
>> # int processInt(int input) {
>> #     int output;
>> #     // Processing.
>> #     return (output);
>> # }
>> #
>> # main(string[] args) {
>> #     foreach (int index, string arg; args[1 .. length]) {
>> #         int temp = toInt(arg);
>> #         int result = processInt(temp);
>> #         printf("[#%d] %d => %d\n", index, temp, result);
>> #     }
>> # }
>>
>> Now, in an ideal world, the user will enter perfectly formatter  
>> integers,
>> without a single mistake, and it  would all be dandy. But that's not  
>> going to
>> happen. And you _know_ that, so you _expect_ an error of this kind. It  
>> _is_ part
>> of  the regular program flow.
>>
>> Luckily for us, exceptions come into play and save this  
>> "quick-and-dirty"
>> utility. If you enter a bunch of letters as an integer, toInt will not  
>> choke and
>> segfault. It will throw an exception, which prevents it from going  
>> further and
>> damaging anything or producing erroneous output.
>>
>> Since we did not set up any handling (a catch), the program will exit  
>> if any
>> input is badly formatted. However, we can take this a step further and  
>> make the
>> utility more robust and more useful:
>>
>> # import std.conv;
>> # alias char[] string;
>> #
>> # int processInt(int input) {
>> #     int output;
>> #     // Processing.
>> #     return (output);
>> # }
>> #
>> # main(string[] args) {
>> #     foreach (int index, string arg; args[1 .. length]) {
>> #         try {
>> #             int temp = toInt(arg);
>> #             int result = processInt(temp);
>> #             printf("[#%d] %d => %d\n", index, temp, result);
>> #         catch (Exception e) {
>> #             printf("Badly formatted input [#%d]=%.*s\n", index, arg);
>> #             printf("Exception says: %.*s\n", e.toString);
>> #         }
>> #     }
>> # }
>>
>> Now, the program will catch any errors neatly, and continue normal  
>> operation
>> (which is perfectly valid). Now ask yourself, how many times will a  
>> human being
>> type a number incorrectly? I think we know the answer is _all the time_.
>>
>> What does this mean? We should _use_ the exception mechanism to replace  
>> all
>> error handling and checking, but in order to do this, it must be  
>> relatively
>> fast. Maybe not as fast as a simple return code, but not an order of  
>> magnitude
>> slower either. In this example it doesn't matter much, but what if it's  
>> a
>> million inputs? or what if it's a server handling hundreds of thousands  
>> of
>> requests?
>>
>> This is all just IMHO, but I think it makes sense. Look at the  
>> traditional
>> alternative:
>>
>> You must manually check all inputs beforehand, before even attempting  
>> to convert
>> them, to see if they are perfectly formatted. You must introduce an  
>> error code
>> return and move the output to its own ref parameter, because the error  
>> code
>> could be mistaken for correct output.
>>
>> I think this is futile and redundant; you would be essentially  
>> re-writing the
>> toInt function yourself just so that it won't throw an exception. Must  
>> one
>> re-invent the wheel?
>>
>> The point of this whole thing: Exceptions are good. They are simple to  
>> use and
>> powerful. They make you code neater and reduce useless clutter.  
>> Moreover, they
>> represent things that occur commonly. So, we should make use of them  
>> whenever
>> possible, and D should make them fast so that we can do this without  
>> incurring a
>> penalty.
>>
>> Thanks for listening,
>> --AJG.
>>
>>>
>>> "AJG" <AJG_member@pathlink.com> wrote in message
>>> news:d9s10t$1lid$1@digitaldaemon.com...
>>>> What is the cost (performance & memory-wise) of throwing and catching  
>>>> an
>>>> exception in D?
>>>
>>> I refer you to the "Error handling" section of D's spec
>>> (http://www.digitalmars.com/d/index.html):
>>>
>>> "
>>> Errors are not part of the normal flow of a program. Errors are  
>>> exceptional,
>>> unusual, and unexpected.
>>> D exception handling fits right in with that.
>>> Because errors are unusual, execution of error handling code is not
>>> performance critical.
>>> Exception handling stack unwinding is a relatively slow process.
>>> The normal flow of program logic is performance critical.
>>> Since the normal flow code does not have to check every function call  
>>> for
>>> error returns, it can be realistically faster to use exception  
>>> handling for
>>> the errors.
>>> "
>>>
>>> For one, D assumes that exceptions are very unusual circumstances.   
>>> For two,
>>> it says that "error handling code is not performance critical."
>>>
>>> Now, it won't pause your app for a few seconds when throwing an  
>>> exception
>>> like in .NET, but it won't be _as fast_ as the rest of your code.
>>
>>
>
June 29, 2005
Re: Are D Exceptions Fast and Efficient?
According to the shootout the answer is NO!

http://shootout.alioth.debian.org/sandbox/benchmark.php?test=except&lang=all&sort=fullcpu

D use 10 times as much time as the fastest language 
on the exception test.



On Tue, 28 Jun 2005 17:22:37 +0000, AJG wrote:

> Hi there,
> 
> What is the cost (performance & memory-wise) of throwing and catching an
> exception in D? First, why do I ask this:
> 
> 1) Exceptions are actually a great way to handle errors.
> 2) Thus, it would be great to handle ALL errors via exceptions.
> Gets rid of return codes, errnos, null-checking, etc.
> 3) However, error-handling MUST be fast and efficient.
> Particularly errors that you expect will happen relatively commonly.
> 4) Hence, none of this will work if exceptions are slow.
> 
> Why I'm I concerned?
> 
> Well, in C#, which is pretty cool (but managed) and has structured exception
> handling, there is one nasty restriction:
> 
> Exceptions are _clearly_ only meant for "exceptional" situations. Things that
> shouldn't happen a lot: running out of memory, database connection failed, etc.
> 
> This restriction is due to [a] various authors recommending such design;
> understandable, given [b] the fact that when an exception is thrown, your
> program halts execution literally for a couple of _human_ seconds.
> 
> This is simply unacceptable for a simple thing like an "invalid input string."
> So in C#, we are forced to do "efficient" error checking _before_ a potential
> exception is thrown. In essence, exceptions in C# are a last resort.
> 
> So, to recap, is it possible to handle all errors in D via exceptions (without a
> speed cost), or should the C# approach be taken: using exceptions only for
> exceptional stuff (a limitation, IMHO)?
> 
> Thanks!
> --AJG.
> 
> 2B || !2B, that is the question.
> ================================
June 29, 2005
Re: Are D Exceptions Fast and Efficient?
Regan Heath wrote:

> In other words approx 7s for exception handling and 16 milliseconds for  
> return codes (likely this equals the time required to call the 
> functions  in both cases). Exception handling is then approximately 437 
> times slower.  A single exception took approx 7/1000 of a milliseccond.
> 
> Regan

From those numbers, assuming that the functions you call do nothing - 
if you expect to get errors only 1 in 437 runs then it is the same to 
check for error as it is to throw an exception.

Regan, would you mind running those tests again & _NOT_ throwing any 
exceptions?  I'd like to confirm that code that can throw exceptions 
(but doesn't) is faster in the usual case than checking error codes.

I'm from a C background though & checking error code still feels more 
natural to me :)

Brad
June 29, 2005
Re: Are D Exceptions Fast and Efficient?
In article <opss4junyl23k2f5@nrage.netwin.co.nz>, Regan Heath says...
>
>I have no idea of the validity of these findings. I really have no idea  
>what exactly exception handling does behind the scenes, I was going to  
>experiment with adding variables to the stack, and heap in the functions  
>to see if it made any difference.

As in most things, there's a size/speed tradeoff in exception handling
strategies.  I've seen claims on comp.lang.c++.moderated that exception handling
can be made almost as fast as returning from a function normally, though most
implementations I've used are not quite so fast.  I'll try your example in D and
in VC++ 2003 on my machine and see what the numbers look like.


Sean
June 29, 2005
Re: Are D Exceptions Fast and Efficient?
Knud Sørensen wrote:
> According to the shootout the answer is NO!
> 
> http://shootout.alioth.debian.org/sandbox/benchmark.php?test=except&lang=all&sort=fullcpu
> 
> D use 10 times as much time as the fastest language 
> on the exception test.
> 
> 
> 
> On Tue, 28 Jun 2005 17:22:37 +0000, AJG wrote:
> 
> 
>>Hi there,
>>
>>What is the cost (performance & memory-wise) of throwing and catching an
>>exception in D? First, why do I ask this:
>>
>>1) Exceptions are actually a great way to handle errors.
>>2) Thus, it would be great to handle ALL errors via exceptions.
>>Gets rid of return codes, errnos, null-checking, etc.
>>3) However, error-handling MUST be fast and efficient.
>>Particularly errors that you expect will happen relatively commonly.
>>4) Hence, none of this will work if exceptions are slow.
>>
>>Why I'm I concerned?
>>
>>Well, in C#, which is pretty cool (but managed) and has structured exception
>>handling, there is one nasty restriction:
>>
>>Exceptions are _clearly_ only meant for "exceptional" situations. Things that
>>shouldn't happen a lot: running out of memory, database connection failed, etc.
>>
>>This restriction is due to [a] various authors recommending such design;
>>understandable, given [b] the fact that when an exception is thrown, your
>>program halts execution literally for a couple of _human_ seconds.
>>
>>This is simply unacceptable for a simple thing like an "invalid input string."
>>So in C#, we are forced to do "efficient" error checking _before_ a potential
>>exception is thrown. In essence, exceptions in C# are a last resort.
>>
>>So, to recap, is it possible to handle all errors in D via exceptions (without a
>>speed cost), or should the C# approach be taken: using exceptions only for
>>exceptional stuff (a limitation, IMHO)?
>>
>>Thanks!
>>--AJG.
>>
>>2B || !2B, that is the question.
>>================================
> 
> 

Compare apples to apples. D is 10 times FASTER than the intel C++ 
compiler, which is widely reguarded as the best optimizing C++ compiler.

All the languages above it are using a different programming paradigm 
entirely, functional programming.  The C examples are laughable, as they 
are basically using int error codes and setjmp/longjmp.


-DavidM
June 29, 2005
Re: Are D Exceptions Fast and Efficient?
Regan Heath wrote:

> I have no idea of the validity of these findings. I really have no idea
> what exactly exception handling does behind the scenes, I was going to
> experiment with adding variables to the stack, and heap in the functions
> to see if it made any difference.

There is no point in messuring function that throws exception every time. If
you expect Error to happen every time than this no error but return value.
Throwing Exception *is* slower - you have to allocate object for exception,
unwind stack and such. But errors are not happening every time. Obvoiusly -
whole mechanism is called "exceptions" because errors are exceptional.

Here goes speed test for more real-like code.

import std.stdio;
import std.string;
import std.c.time;

enum {
       ERROR = 666
}

const int ITERATIONS = 30000;

time_t  sTime, eTime;

void printOut(char[] msg, int t){
       writef(msg ~ " in ", t, "\n");
}

/* we are testing these: */
int foo1Re(){
       return 0;
}

int foo1Ex(){
       return 0;
}

int foo2Re(){
       static int foo2ReCounter;
       if(foo2ReCounter++ == ITERATIONS/4){
               foo2ReCounter=0;
               return ERROR;
       }
       return 0;
}

int foo2Ex(){
       static int foo2ExCounter;
       if(foo2ExCounter++ == ITERATIONS/4){
               foo2ExCounter=0;
               throw new Exception("");
       }
       return 0;
}


void main() {

       sTime = time(null);
       for(int i = 0; i < ITERATIONS; i++) {
               for(int j = 0; j < ITERATIONS; j++)
                       if(ERROR == foo1Re()){
                       }
       }
       eTime = time(null);
       printOut("foo1Re",eTime - sTime);



       sTime = time(null);
       try {
               for(int i = 0; i < ITERATIONS; i++)
                       for(int j = 0; j < ITERATIONS; j++)
                               foo1Ex();
       }
       catch (Exception e) {
       }
       eTime = time(null);
       printOut("foo1Ex",eTime - sTime);



       sTime = time(null);
       for(int i = 0; i < ITERATIONS*10; i++) {
               while(ERROR != foo2Re()){};
       }
       eTime = time(null);
       printOut("foo2Re",eTime - sTime);



       sTime = time(null);
       for(int i = 0; i < ITERATIONS*10; i++){
               try {
                       while(true)
                               foo2Ex();
               }
               catch (Exception e) {
               }
       }
       eTime = time(null);
       printOut("foo2Ex",eTime - sTime);




}

dmd  -O -release except.d && ./except
gcc except.o -o except -lphobos -lpthread -lm
foo1Re in 4
foo1Ex in 4
foo2Re in 13
foo2Ex in 17

As you can see - when there is no throwing - exception can be as fast as
returning errorcode. And even when they are thrown code is not much slower.
And advantages of using exceptions are realy huge. Resignation from using
exception can be justified only by deep analys of profiler output at the
end of developing software in execution time critical parts of code.
-- 
Dawid Ciężarkiewicz | arael
June 29, 2005
Re: Are D Exceptions Fast and Efficient?
In article <d9uf7e$1ive$1@digitaldaemon.com>, Sean Kelly says...
>
>In article <opss4junyl23k2f5@nrage.netwin.co.nz>, Regan Heath says...
>>
>>I have no idea of the validity of these findings. I really have no idea  
>>what exactly exception handling does behind the scenes, I was going to  
>>experiment with adding variables to the stack, and heap in the functions  
>>to see if it made any difference.
>
>As in most things, there's a size/speed tradeoff in exception handling
>strategies.  I've seen claims on comp.lang.c++.moderated that exception handling
>can be made almost as fast as returning from a function normally, though most
>implementations I've used are not quite so fast.  I'll try your example in D and
>in VC++ 2003 on my machine and see what the numbers look like.

Okay, I rewrote the test code a bit, but the basic idea is the same.  Test code
is available here:

http://home.f4.ca/sean/d/except.d
http://home.f4.ca/sean/d/except.cpp
http://home.f4.ca/sean/d/except.txt (dump of test results)

I tested DMD, DMC, and VC++.  Each test had 2 runs to eliminate caching issues.
First, I tested a plain non-optimized build, then I tested an optimized build.
I tried to have inlining turned off in all tests as it would collapse the deeply
nested function calls down to nothing.

For VC++ (since I pre-built these exes through the IDE), "standard" is just the
"release" template with these changes:

Optimization: Disabled
Inline Function Expansion: Only __inline

"optimized" is also the "release" template, but with these changes:

Optimization: Maximize Speed (/O2)
Inline Function Expansion: Only __inline

I won't vouch for the accuracy of these tests, but from the results it's clear
that DM compilers blow the doors off VC++ with the build options as they were.
The difference for normal function returns is such that I almost suspect DM of
inlining function calls even though I attempted to disable this feature.  Also,
DMA had far less impact on run time than I expected it to (especially for DM
compilers where run time with and without was almost identical).  It's also
worth noting that VC++ is supposed to have a fairly fast exception handling
implementation, but it was slower than DM in pretty much all tests.


########## Digital Mars ##########


C:\code\gen\except\dm>test_dm
************************************************************

C:\code\gen\except\dm>dmd -release except.d
C:\bin\dmd\bin\..\..\dm\bin\link.exe except,,,user32+kernel32/noi;
********************

C:\code\gen\except\dm>except
********** testing normal return **********
depth 0: 1000000 iterations completed in (0.010000)
depth 50: 1000000 iterations completed in (0.000000)
depth 100: 1000000 iterations completed in (0.010000)
********** testing throw ref **********
depth 0: 1000000 iterations completed in (4.593000)
depth 50: 1000000 iterations completed in (4.663000)
depth 100: 1000000 iterations completed in (4.633000)
********** testing throw new **********
depth 0: 1000000 iterations completed in (5.807000)
depth 50: 1000000 iterations completed in (5.876000)
depth 100: 1000000 iterations completed in (5.696000)
********************

C:\code\gen\except\dm>except
********** testing normal return **********
depth 0: 1000000 iterations completed in (0.010000)
depth 50: 1000000 iterations completed in (0.010000)
depth 100: 1000000 iterations completed in (0.000000)
********** testing throw ref **********
depth 0: 1000000 iterations completed in (4.854000)
depth 50: 1000000 iterations completed in (4.834000)
depth 100: 1000000 iterations completed in (4.623000)
********** testing throw new **********
depth 0: 1000000 iterations completed in (5.706000)
depth 50: 1000000 iterations completed in (5.766000)
depth 100: 1000000 iterations completed in (5.606000)
************************************************************

C:\code\gen\except\dm>dmd -release -O except.d
C:\bin\dmd\bin\..\..\dm\bin\link.exe except,,,user32+kernel32/noi;
********************

C:\code\gen\except\dm>except
********** testing normal return **********
depth 0: 1000000 iterations completed in (0.010000)
depth 50: 1000000 iterations completed in (0.010000)
depth 100: 1000000 iterations completed in (0.000000)
********** testing throw ref **********
depth 0: 1000000 iterations completed in (4.532000)
depth 50: 1000000 iterations completed in (4.533000)
depth 100: 1000000 iterations completed in (4.553000)
********** testing throw new **********
depth 0: 1000000 iterations completed in (5.596000)
depth 50: 1000000 iterations completed in (5.726000)
depth 100: 1000000 iterations completed in (5.706000)
********************

C:\code\gen\except\dm>except
********** testing normal return **********
depth 0: 1000000 iterations completed in (0.010000)
depth 50: 1000000 iterations completed in (0.010000)
depth 100: 1000000 iterations completed in (0.010000)
********** testing throw ref **********
depth 0: 1000000 iterations completed in (4.503000)
depth 50: 1000000 iterations completed in (4.583000)
depth 100: 1000000 iterations completed in (4.984000)
********** testing throw new **********
depth 0: 1000000 iterations completed in (5.887000)
depth 50: 1000000 iterations completed in (5.655000)
depth 100: 1000000 iterations completed in (5.596000)
************************************************************

C:\code\gen\except\dm>dmc -Ae -C except.cpp
except.cpp(41) : Warning 18: implied return of throw_ref at closing '}' does not
return value
except.cpp(55) : Warning 18: implied return of throw_new at closing '}' does not
return value
except.cpp(103) : Warning 18: implied return of throw_ref at closing '}' does no
t return value
except.cpp(103) : Warning 18: implied return of throw_new at closing '}' does no
t return value
link except,,,user32+kernel32/noi;

********************

C:\code\gen\except\dm>except
********** testing normal return **********
depth 0: 1000000 iterations completed in (0.010000)
depth 50: 1000000 iterations completed in (0.571000)
depth 100: 1000000 iterations completed in (1.284000)
********** testing throw ref **********
depth 0: 1000000 iterations completed in (6.007000)
depth 50: 1000000 iterations completed in (6.037000)
depth 100: 1000000 iterations completed in (7.230000)
********** testing throw new **********
depth 0: 1000000 iterations completed in (6.238000)
depth 50: 1000000 iterations completed in (6.659000)
depth 100: 1000000 iterations completed in (6.669000)
********************

C:\code\gen\except\dm>except
********** testing normal return **********
depth 0: 1000000 iterations completed in (0.000000)
depth 50: 1000000 iterations completed in (0.561000)
depth 100: 1000000 iterations completed in (1.284000)
********** testing throw ref **********
depth 0: 1000000 iterations completed in (6.308000)
depth 50: 1000000 iterations completed in (6.458000)
depth 100: 1000000 iterations completed in (6.587000)
********** testing throw new **********
depth 0: 1000000 iterations completed in (6.206000)
depth 50: 1000000 iterations completed in (6.668000)
depth 100: 1000000 iterations completed in (6.647000)
************************************************************

C:\code\gen\except\dm>dmc -Ae -o -C except.cpp
except.cpp(41) : Warning 18: implied return of throw_ref at closing '}' does not
return value
except.cpp(55) : Warning 18: implied return of throw_new at closing '}' does not
return value
except.cpp(103) : Warning 18: implied return of throw_ref at closing '}' does no
t return value
except.cpp(103) : Warning 18: implied return of throw_new at closing '}' does no
t return value
link except,,,user32+kernel32/noi;

********************

C:\code\gen\except\dm>except
********** testing normal return **********
depth 0: 1000000 iterations completed in (0.010000)
depth 50: 1000000 iterations completed in (0.601000)
depth 100: 1000000 iterations completed in (1.384000)
********** testing throw ref **********
depth 0: 1000000 iterations completed in (5.735000)
depth 50: 1000000 iterations completed in (6.436000)
depth 100: 1000000 iterations completed in (6.317000)
********** testing throw new **********
depth 0: 1000000 iterations completed in (6.136000)
depth 50: 1000000 iterations completed in (6.657000)
depth 100: 1000000 iterations completed in (7.118000)
********************

C:\code\gen\except\dm>except
********** testing normal return **********
depth 0: 1000000 iterations completed in (0.010000)
depth 50: 1000000 iterations completed in (0.622000)
depth 100: 1000000 iterations completed in (1.524000)
********** testing throw ref **********
depth 0: 1000000 iterations completed in (5.855000)
depth 50: 1000000 iterations completed in (6.457000)
depth 100: 1000000 iterations completed in (6.316000)
********** testing throw new **********
depth 0: 1000000 iterations completed in (6.226000)
depth 50: 1000000 iterations completed in (6.667000)
depth 100: 1000000 iterations completed in (6.657000)


########## MSVC++ ##########



C:\code\gen\except>test_vc
************************************************************

C:\code\gen\except>standard\except
********** testing normal return **********
depth 0: 1000000 iterations completed in (0.010000)
depth 50: 1000000 iterations completed in (0.732000)
depth 100: 1000000 iterations completed in (1.374000)
********** testing throw ref **********
depth 0: 1000000 iterations completed in (5.363000)
depth 50: 1000000 iterations completed in (5.785000)
depth 100: 1000000 iterations completed in (5.966000)
********** testing throw new **********
depth 0: 1000000 iterations completed in (7.078000)
depth 50: 1000000 iterations completed in (7.900000)
depth 100: 1000000 iterations completed in (7.530000)
********************

C:\code\gen\except>standard\except
********** testing normal return **********
depth 0: 1000000 iterations completed in (0.010000)
depth 50: 1000000 iterations completed in (0.612000)
depth 100: 1000000 iterations completed in (1.373000)
********** testing throw ref **********
depth 0: 1000000 iterations completed in (5.374000)
depth 50: 1000000 iterations completed in (5.755000)
depth 100: 1000000 iterations completed in (5.975000)
********** testing throw new **********
depth 0: 1000000 iterations completed in (7.139000)
depth 50: 1000000 iterations completed in (7.339000)
depth 100: 1000000 iterations completed in (7.449000)
************************************************************

C:\code\gen\except>optimized\except
********** testing normal return **********
depth 0: 1000000 iterations completed in (0.010000)
depth 50: 1000000 iterations completed in (0.010000)
depth 100: 1000000 iterations completed in (0.010000)
********** testing throw ref **********
depth 0: 1000000 iterations completed in (5.113000)
depth 50: 1000000 iterations completed in (5.374000)
depth 100: 1000000 iterations completed in (5.705000)
********** testing throw new **********
depth 0: 1000000 iterations completed in (7.258000)
depth 50: 1000000 iterations completed in (6.627000)
depth 100: 1000000 iterations completed in (6.989000)
********************

C:\code\gen\except>optimized\except
********** testing normal return **********
depth 0: 1000000 iterations completed in (0.010000)
depth 50: 1000000 iterations completed in (0.010000)
depth 100: 1000000 iterations completed in (0.010000)
********** testing throw ref **********
depth 0: 1000000 iterations completed in (5.093000)
depth 50: 1000000 iterations completed in (5.374000)
depth 100: 1000000 iterations completed in (5.404000)
********** testing throw new **********
depth 0: 1000000 iterations completed in (6.887000)
depth 50: 1000000 iterations completed in (6.668000)
depth 100: 1000000 iterations completed in (6.998000)
June 29, 2005
Re: Are D Exceptions Fast and Efficient?
My intention was to measure the time it took to throw an exception, not to  
compare 2 real-life examples. I already know exception handling is slower,  
I was simply trying to put some numbers down as to how much slower.

IMO exceptions are "fast enough". 7/1000th's of a millisecond is not  
something you're going to notice with the human eye/brain etc.

Regan

On Wed, 29 Jun 2005 18:22:03 +0200, Dawid Ciężarkiewicz <arael@fov.pl>  
wrote:

> Regan Heath wrote:
>
>> I have no idea of the validity of these findings. I really have no idea
>> what exactly exception handling does behind the scenes, I was going to
>> experiment with adding variables to the stack, and heap in the functions
>> to see if it made any difference.
>
> There is no point in messuring function that throws exception every  
> time. If
> you expect Error to happen every time than this no error but return  
> value.
> Throwing Exception *is* slower - you have to allocate object for  
> exception,
> unwind stack and such. But errors are not happening every time.  
> Obvoiusly -
> whole mechanism is called "exceptions" because errors are exceptional.
>
> Here goes speed test for more real-like code.
>
> import std.stdio;
> import std.string;
> import std.c.time;
>
> enum {
>         ERROR = 666
> }
>
> const int ITERATIONS = 30000;
>
> time_t  sTime, eTime;
>
> void printOut(char[] msg, int t){
>         writef(msg ~ " in ", t, "\n");
> }
>
> /* we are testing these: */
> int foo1Re(){
>         return 0;
> }
>
> int foo1Ex(){
>         return 0;
> }
>
> int foo2Re(){
>         static int foo2ReCounter;
>         if(foo2ReCounter++ == ITERATIONS/4){
>                 foo2ReCounter=0;
>                 return ERROR;
>         }
>         return 0;
> }
>
> int foo2Ex(){
>         static int foo2ExCounter;
>         if(foo2ExCounter++ == ITERATIONS/4){
>                 foo2ExCounter=0;
>                 throw new Exception("");
>         }
>         return 0;
> }
>
>
> void main() {
>
>         sTime = time(null);
>         for(int i = 0; i < ITERATIONS; i++) {
>                 for(int j = 0; j < ITERATIONS; j++)
>                         if(ERROR == foo1Re()){
>                         }
>         }
>         eTime = time(null);
>         printOut("foo1Re",eTime - sTime);
>
>
>
>         sTime = time(null);
>         try {
>                 for(int i = 0; i < ITERATIONS; i++)
>                         for(int j = 0; j < ITERATIONS; j++)
>                                 foo1Ex();
>         }
>         catch (Exception e) {
>         }
>         eTime = time(null);
>         printOut("foo1Ex",eTime - sTime);
>
>
>
>         sTime = time(null);
>         for(int i = 0; i < ITERATIONS*10; i++) {
>                 while(ERROR != foo2Re()){};
>         }
>         eTime = time(null);
>         printOut("foo2Re",eTime - sTime);
>
>
>
>         sTime = time(null);
>         for(int i = 0; i < ITERATIONS*10; i++){
>                 try {
>                         while(true)
>                                 foo2Ex();
>                 }
>                 catch (Exception e) {
>                 }
>         }
>         eTime = time(null);
>         printOut("foo2Ex",eTime - sTime);
>
>
>
>
> }
>
> dmd  -O -release except.d && ./except
> gcc except.o -o except -lphobos -lpthread -lm
> foo1Re in 4
> foo1Ex in 4
> foo2Re in 13
> foo2Ex in 17
>
> As you can see - when there is no throwing - exception can be as fast as
> returning errorcode. And even when they are thrown code is not much  
> slower.
> And advantages of using exceptions are realy huge. Resignation from using
> exception can be justified only by deep analys of profiler output at the
> end of developing software in execution time critical parts of code.
1 2 3
Top | Discussion index | About this forum | D home