On 03/30/2011 08:42 AM, Kai Meyer wrote:

> we were talking about what sort of things you can do to
> increase performance

Sorry to change the topic. :) I am fortunate that what I currently work on does not require more than being careful about not using the wrong algorithms and data structures.

> if (good data)
> do work
> else
> report "can't do work"

That is helpful as long as the data can be checked up front, but we can't always be sure:

if (file_exists()) {
    use_file();   // <-- the file may not exist

or

if (server_is_up()) {
    talk_to_server();  // <-- the server may not be up

> Would be faster than:
>
> try
> do work
> catch
> report "can't do work"

Apparently that's what Denis' (spir) measurements show as well and that's very unfortunate. I would like to put that slowness under the "quality of implementation" category. Although, it's well known that exception handling has been slow (e.g. in C++) in general too.

(Note: I heard that scope(failure) is lowered to a try-catch block by the compiler; so it should be slow too.)

If exceptions are inherently slow, that must be because they provide more than what we compare them against. A good example is comparing old features of C to C++. Some people claim that calling virtual functions is slow due to jumping off the vtbl. Correct, but let's not forget that achieving the same in C would be slow too.

Your examples do have such a feature difference: the code that uses the try-catch block will always execute the code in the catch clause. On the other hand, the code that checks the data before hand may not report "can't do work", as "do work" may get complicated in the future and may call a function that may throw directly or indirectly. And suddenly the function doesn't work anymore and the changes that we've made are detached from this function. Bad bug! :)

We may argue that exceptions should not exist in D (or C++) but they are so helpful (hey, I know we all know these :)):

- exceptions make it impossible (or very hard) to continue with bad program state

- they allow functions to return objects by freeing the return value from always being an error code (return values are preferable to side effects)

- they eliminate boiler plate error management lines (To complicate matters, when a function needs to do cleanup after an error, it may call other functions that may return more error codes. We must be careful not to change the value of the original error code variable.)

- they result in less lines of code (bugs live in lines of code :))

I would like to show two functions written in C and C++. To my knowledge, they are well written and don't have any resource leaks:

// a C function

int bar_C(Resource ** in_out)
{
    int err = 0;

    Resource * r0 = NULL;
    Resource * r1 = NULL;

    err = allocate_resource(&r0);
    if (err) goto finally;

    err = allocate_resource(&r1);
    if (err) goto finally;

    /* ... use r0 and r1 here ...  */

    if (err) goto finally;

    /* transfer ownership */
    *in_out = r0;
    r0 = NULL;

finally:

    deallocate_resource(&r1);
    deallocate_resource(&r0);
    return err;
}

// The equivalent C++ function

Resource bar_CPP()
{
    Resource r0(/* ... */);
    Resource r1(/* ... */);

    /* ... use r0 and r1 here ... */

    /* transfer ownership */
    return r0;
}

Of course the latter takes advantage of other features of C++, but it also shows that there is no explicit error management when the code relies on exceptions. bar_CPP() just does what it is supposed to do. Yes, there may be errors but bar_CPP() doesn't care. And the callers may or may not catch the errors, but bar_CPP() doesn't care.

> I'm not sure D's exceptions are much different than C++'s.

Yeah, it must be the same as what Digital Mars C++ compiler uses. (Except, D also has the 'finally' clause.)

In summary: I hope I will never go back to pass-the-error-code style of coding.

Ali

Reply via email to