Using exceptions for early exit

J

Jonathan Bartlett

I ran across the following article on Java exceptions and noticed this
little tidbit:

"Sometimes developers decide to use exception for flow-control (i.e. to
throw an exception once a certain condition is met). The generation of
stack-traces is expensive and developers should realise that an
exception should only be used in exceptional cases."

(From http://javafoundry.ibscon.com/content/view/18/ )

A few questions:

(1) is this still true? Is a stack trace still expensive?
(2) do all exceptions do this? For example, if I just inherit from
"Throwable" rather than "Exception" is a stack trace still generated?
(3) is there any way around this?

The reason I'm asking is that exceptions would seem to be a very good
way to implement early-exit from certain deeply-nested computations.

Jon
 
C

Chris Smith

Jonathan Bartlett said:
"Sometimes developers decide to use exception for flow-control (i.e. to
throw an exception once a certain condition is met). The generation of
stack-traces is expensive and developers should realise that an
exception should only be used in exceptional cases."

(From http://javafoundry.ibscon.com/content/view/18/ )

A few questions:

(1) is this still true? Is a stack trace still expensive?

Yes, it's still true that generating a stack trace is a relatively
expensive operation, compared to other methods of accomplishing this.
Whether that justifies the generalization above is a different matter.
The number of situations in which small performance costs matters enough
to justify making your life harder is actually rather small.

On the other hand, just from a readability standpoint, use of exceptions
for minor flow control purposes is a poor idea. The ultimate test is
whether there is a substantial body of code in which the exception
actually makes sense and can be given a simple descriptive name.
Generally, when exceptions do get used, there is some context in which
is really IS an exceptional case. Exceptional, in the context of Java,
doesn't mean that it almost never happens, but rather than the code to
handle it is not relevant to the purpose of the method you're writing.
(2) do all exceptions do this? For example, if I just inherit from
"Throwable" rather than "Exception" is a stack trace still generated?
(3) is there any way around this?

Yes, and no.

--
www.designacourse.com
The Easiest Way To Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation
 
E

Eric Sosman

Jonathan Bartlett wrote On 12/12/05 09:57,:
I ran across the following article on Java exceptions and noticed this
little tidbit:

"Sometimes developers decide to use exception for flow-control (i.e. to
throw an exception once a certain condition is met). The generation of
stack-traces is expensive and developers should realise that an
exception should only be used in exceptional cases."

(From http://javafoundry.ibscon.com/content/view/18/ )

A few questions:

(1) is this still true? Is a stack trace still expensive?
(2) do all exceptions do this? For example, if I just inherit from
"Throwable" rather than "Exception" is a stack trace still generated?
(3) is there any way around this?

The reason I'm asking is that exceptions would seem to be a very good
way to implement early-exit from certain deeply-nested computations.

The first two seem the sort of questions you could
answer for yourself with a little experimentation. You'd
get not only answers, but quantitative answers.

The third is unclear to me. What is the "this" you're
worried about, and what would it mean to get "around" it?
That is, what is your problem and what are your criteria
for a successful solution?
 
A

Andrea Desole

Jonathan said:
The reason I'm asking is that exceptions would seem to be a very good
way to implement early-exit from certain deeply-nested computations.

If this is your need that probably means that you have to change your
code (for example, you can make methods out of your computations).
If you don't, have a look at break and continue. I personally never use
them (I consider them mostly an extreme solution), but that's what they
are for
 
R

ricky.clarkson

A particular example in the API of using exceptions for flow control is
Integer.parseInt. There's no way of asking whether the int is
parseable,so you have to detect it via an exception.

I think it would be better like this:

IntegerParseResult result=Integer.parseInt("5556");

if (result.hasValue())
doStuff(result.getValue());
else
handleTheBadStuff();

getValue() would throw an IllegalStateException ifcalled when
!result.hasValue().
 
J

Jonathan Bartlett

The third is unclear to me. What is the "this" you're
worried about, and what would it mean to get "around" it?

Producing stack traces.
That is, what is your problem and what are your criteria
for a successful solution?

Throw something that didn't generate a stack trace.

Jon
 
J

Jonathan Bartlett

If this is your need that probably means that you have to change your
code (for example, you can make methods out of your computations).
If you don't, have a look at break and continue. I personally never use
them (I consider them mostly an extreme solution), but that's what they
are for

I was actually looking for a means of a quick escape from
deeply-recursive functions. Just throw the exception to exit the recursion.

Jon
 
E

Eric Sosman

Jonathan Bartlett wrote On 12/12/05 14:02,:
Producing stack traces.




Throw something that didn't generate a stack trace.

As far as I know, every Throwable contains a stack
trace. You don't need to display it, but it'll be there
no matter what you do.

However, the stack trace is built when the Throwable
is constructed, not by the "throw" action. You could
therefore construct your Exception once at class-loading
time, and then throw the same Exception object as often
as you like:

class Perverse {

private static Exception perverseException
= new Exception();

public void somethingStupid(int n) throws Exception {
if (n <= 0)
throw perverseException;
else
somethingStupid(n-1);
}

public void moreStupidity(double p) throws Exception {
if (Math.random() < p)
throw perverseException;
}
}

Thus, you'd only incur the cost of creating and initializing
the Exception once, instead of at every throw.

I won't reveal what I think about the advisability of the
technique, but the names in the illustration above may provide
a tiny clue ... Have I just given a loaded pistol to a child?
 
O

Oliver Wong

Jonathan Bartlett said:
I was actually looking for a means of a quick escape from deeply-recursive
functions. Just throw the exception to exit the recursion.

This sounds like a "Bad Idea".

Does your recursive function return a value? Does it modify some other
data structure? You might be able to "get out of the recursion" more cleanly
by using a "return" statement.

- Oliver
 
R

ricky.clarkson

All recursive operations can be written as iterative. If stack
unwinding irritates you, feel free to rewrite this algorithm to use
iteration.
 
T

Thomas Hawtin

Jonathan said:
"Sometimes developers decide to use exception for flow-control (i.e. to
throw an exception once a certain condition is met). The generation of
stack-traces is expensive and developers should realise that an
exception should only be used in exceptional cases."

(From http://javafoundry.ibscon.com/content/view/18/ )

A few questions:

(1) is this still true? Is a stack trace still expensive?

That article is less than two years old. Yes, it appears to still be
true. It is apparently something not considered worth spending time on
optimising. IIRC, I measured around ten or twenty thousand cycles with a
shallow stack. The bigger the stack, the more work to do.
(2) do all exceptions do this? For example, if I just inherit from
"Throwable" rather than "Exception" is a stack trace still generated?

UTSL. If you look at the source to Throwable, you can see it calling
fillInStackTrace.
(3) is there any way around this?

Override fillInStackTrace or use the same exception over again.

I timed 1.5 (or 1.4?) with -server in a loop catching an exception
thrown through an interface. The benchmark got optimised away.
The reason I'm asking is that exceptions would seem to be a very good
way to implement early-exit from certain deeply-nested computations.

Probably not a good idea. Labeled break is good within the same method.
You can probably rearrange the algorithm to make the exception unnecessary.

In my years of Java programming I have never (seriously) used an
exception that way. Mind you when I write recursive code, I tend to
rewrite it properly before the first compile.

Tom Hawtin
 
C

Chris Uppal

All recursive operations can be written as iterative. If stack
unwinding irritates you, feel free to rewrite this algorithm to use
iteration.

Having spent the better part of two days recently rewriting a deeply recursive
algorithm into a pure (no local variables) iterative formulation, and /NEVER/
wanting to do so again, I cannot but feel that this poor advice...

-- chris
 
C

Chris Uppal

Eric said:
class Perverse {
public void somethingStupid(int n) throws Exception {
public void moreStupidity(double p) throws Exception {


Just curious. If the Java language had a non-local-return feature, with
appropriate syntax, and semantics that was (at worst) no harder to get right
than throwing an exception (i.e. /not/ C's setjmp()/longjmp()), would you still
feel the same way ?

Is the issue the "shape" of the control-flow or the syntax used to express it ?

(For me it's the latter -- I don't like the idiom either, but that's because I
would expect that people reading the code would /not/ expect exceptions to be
used in that way; I have no problems with a precisely controlled, but
non-local, flow of control in itself. Which, come to think of it, is just as
well since I don't object to exceptions...)

-- chris
 
R

Roedy Green

(1) is this still true? Is a stack trace still expensive?

Every time an exception is thrown the raw data to print a stack trace
has to be collected from the stack, interpretively wending its way
from top to bottom. It has to collect it before the exception
destroys the stack popping in off seeking a handler. There is even
more data collected that typically printed. see
http://mindprod.com/jgloss/trace.html

It takes several orders of magnitude more time that a simple flow of
control jump. Don't use exceptions for exceptional conditions.

I discovered that way back in 1969 when I decided to use PL/I ON units
for flow control, just to experiment. I could not believe how slow it
was.
 
R

ricky.clarkson

Chris,

Just because changing a recursive implementation into an iterative
implementation is awkward, doesn't mean that the iterative version is
wrong, or that it is bad advice to suggest to do so.

If the poster had written in an iterative way originally then I don't
think there'd be an issue.

I know from my own experience that changing recursive to iterative can
be a real pain, I've done it for something non-trivial.

I've never heard of this 'pure iterative' concept before, so I'm unsure
why you would aim to have no local variables. I've probably
misunderstood.
 
R

Roedy Green

If the Java language had a non-local-return feature, with
appropriate syntax, and semantics that was (at worst) no harder to get right
than throwing an exception (i.e. /not/ C's setjmp()/longjmp()), would you still
feel the same way ?

I invented such tools in my BBL and Abundance language. I used them
only for constructing early return verbs, not for jumping up many
levels.

e.g. for example in Abundance you can say

aBoolean ?EXIT>>>

instead of

aBoolean IF EXIT>>> THEN

or

aBoolean ?YES>>>

instead of

aBoolean IF YES EXIT>>> THEN

(returns value yes if true, otherwise carries on)
 
E

Eric Sosman

Chris Uppal wrote On 12/12/05 16:02,:
Eric Sosman wrote:





Just curious. If the Java language had a non-local-return feature, with
appropriate syntax, and semantics that was (at worst) no harder to get right
than throwing an exception (i.e. /not/ C's setjmp()/longjmp()), would you still
feel the same way ?

Is the issue the "shape" of the control-flow or the syntax used to express it ?

(For me it's the latter -- I don't like the idiom either, but that's because I
would expect that people reading the code would /not/ expect exceptions to be
used in that way; I have no problems with a precisely controlled, but
non-local, flow of control in itself. Which, come to think of it, is just as
well since I don't object to exceptions...)

The syntax doesn't bother me much, although there's a
certain amount of clumsiness about try/catch/finally. The
fact that each of the three blocks has its own scope means
that there's no single scope for the entire construct; this
makes me declare variables at a scope wider than they really
ought to have, or introduce an extra level of { } simply to
create a containing scope. And it's irritating to catch an
IOException, say, and then put yet another try/catch inside
the finally that closes the BufferedReader or whatever. But
these are just cosmetic issues, nothing I can't live with.

Nor is the shape of the control flow a problem. The route
taken by a Java exception has the right balance of unpredictable
(the thrower doesn't know the catcher) and predictable (the
catcher will necessarily be an active call frame). A goto style
(e.g., PL/I or Pascal) loses the flexibility of "anonymous" (or
else requires label variables, which are hard to validate), while
the trust-the-programmer style of C's longjmp() all too often
engenders really horrible bugs. Java exceptions unwind in a
calm and orderly fashion, which I find soothing.

My objections to control-flow-by-exception are twofold.
First, there's efficiency. Yes, I know it's unfashionable
to talk about efficiency, but those of us who programmed in
Olden Times formed the hard-to-shake habit of keeping it in
mind. I'm still an efficiency addict (on, er, method-done
treatment ;-), and cannot help but think of all the cycles
that must be spent on a throw/catch. Even if you pre-construct
the thrown exception, the JVM still needs to conduct a possibly
lengthy search up the stack to find candidate catch blocks; a
chain of returns -- even if a method result must be if'ed at
each level -- will almost certainly be faster.

My other objection is a "right job, wrong tool" feeling.
Sure, it's possible to use exceptions for control flow, but
Java's control flow constructs are there because they're more
convenient, more readable, and better suited to the task. One
can replace a "switch" with a try/catch/catch/catch/catch...
block, but that doesn't mean one ought to. Exceptions should
be, well, exceptional -- they're not called Normals, after all!
I like to think of a method as having a contract that in normal
circumstances is "contained" in its arguments, its returned
value, and the states of the objects it touches. When I call
the method, I want it to return with its contract fulfilled.
Exceptions are for when there's something that prevents the
method from fulfilling its contract: invalid arguments or state,
I/O errors, whatever.

So: My objections amount to an old-fashioned concern for
efficiency combined with a Puritanical fastidiousness. Neither
is binding on anyone else; follow your weird.
 
R

Roedy Green

A particular example in the API of using exceptions for flow control is
Integer.parseInt. There's no way of asking whether the int is
parseable,so you have to detect it via an exception.

that requires you to deal with errors at every use. With Exceptions
you can deal with them all in one place, e.g. if your numbers are
supposed to be generated by some other computer in perfect form where
even one being wrong indicates a program bug.
 
R

Roedy Green

I was actually looking for a means of a quick escape from
deeply-recursive functions. Just throw the exception to exit the recursion.

Even if you did, it would not be quick. An exception interpretively
combs the stack looking for a handler. Conceptually it looks as though
it pops the stack in one fell swoop, but if you benchmark the code I
think you will find it faster to just return recursively.

You might enjoy Abundance which has a feature to jaunt( leap backward
in time to a previous execution checkpoint). It does that very rapidly
by copying a stack snapshot in as the real one and restoring a few
crucial system variables.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,774
Messages
2,569,598
Members
45,150
Latest member
MakersCBDReviews
Top