From: Volkan YAZICI
Subject: SIGNAL vs. Closures for Control Passing
Date: 
Message-ID: <cc5a66cf-17a7-499c-87bf-eb802d60d49f@s8g2000prg.googlegroups.com>
Hi,

I've switched from SIGNAL to closures for passing produced parser
results to the caller in meta-sexp parser generator. (No restarts,
just plain transfer of result to an upper stack.) And I suprised by
the x4 resultant performance boost. (This even gets better on more
complicated parsing routines.)

I searched c.l.l archives, but couldn't find anything related with the
overhead and implementation difference between using SIGNAL and
closures. (I'm not asking about their functional difference, that's
explained quite detailed in CLHS. What I wonder really is why SIGNAL
performs worse in my case despite I expect closures to have a more
complicated implementation. Aren't they?) Would anybody mind
explaining a bit about this subject?


Regards.

From: Kent M Pitman
Subject: Re: SIGNAL vs. Closures for Control Passing
Date: 
Message-ID: <uwsr9hnqy.fsf@nhplace.com>
Volkan YAZICI <·············@gmail.com> writes:

> I've switched from SIGNAL to closures for passing produced parser
> results to the caller in meta-sexp parser generator. (No restarts,
> just plain transfer of result to an upper stack.) And I suprised by
> the x4 resultant performance boost. (This even gets better on more
> complicated parsing routines.)
> 
> I searched c.l.l archives, but couldn't find anything related with the
> overhead and implementation difference between using SIGNAL and
> closures. (I'm not asking about their functional difference, that's
> explained quite detailed in CLHS. What I wonder really is why SIGNAL
> performs worse in my case despite I expect closures to have a more
> complicated implementation. Aren't they?) Would anybody mind
> explaining a bit about this subject?

I've never heard of anyone wanting to compare these.  They're really 
very different.

SIGNAL operates on condition objects, which are more complicated in
general than closures, so first off, they take more work to cons.

Second, SIGNAL manages a bunch of extra baggage for doing the handshake
between a general list of callees and callers.  That whole handshake
protocol can be omitted in ANY situation where you know who you want to
talk to and are willing to establish a special purpose protocol.  The whole
point of a protocol is to act in the absence of cooperation, which makes
it flexible. 

SIGNAL has to stop at every handler on the stack (not just the ones you
set up, but others that may decline) and ask whether they are the right
ones.  And to receive a signal, you have to set up handlers, which can cost
a bit of stack setup, too.

Whenever you hear "flexible" you should be suspicious of the idea that
it is as efficient as "inflexible".  Closures are useful, but are not
remarkably flexible, by comparison.

Although the condition system evolved since the finalization of the
condition system, many of its aspects are illustrated in approximate
form in the original sample implementation [1] that I created during the
standarization process.  If you look at the definitions of SIGNAL and
of HANDLER-BIND in there and compare it to what you're doing, I think
you'll see the answer yourself, and you'll understand why you're
invoking a mechanism that isn't really designed to be the ordinary way
a fast program is intended to compute its inner loop, which it sounds
like you're trying to do.  It's intended to be a very robust general
way of getting around places in your program where you don't know who
you'll be talking to.  Another way to think about it is as a
reflective facility (like reflection capabilities in the .net
languages); not something you want to be doing for casual inner loops,
but very important when you do need it.

In using direct closures, you bypass
 * creating handlers
 * creating condition to signal
 * traversing a list of handlers
 * testing types associated with handlers to see what wants to be called
 * calling handlers that want to decline

Incidentally, individual vendors may have more optimal versions of some of
this than the sample implementation shows.  But basically, they still have
a bunch of bookkeeping to do, so most of that will happen in some way or
another.

Hope that helps.

[1] http://www.nhplace.com/kent/CL/Revision-18.lisp.txt
From: Volkan YAZICI
Subject: Re: SIGNAL vs. Closures for Control Passing
Date: 
Message-ID: <8aa4f2d8-19ab-40b8-b463-13ece8fd26c9@t1g2000pra.googlegroups.com>
On Dec 20, 8:41 am, Kent M Pitman <······@nhplace.com> wrote:
> ...
> Hope that helps.
>
> [1]http://www.nhplace.com/kent/CL/Revision-18.lisp.txt

Thanks so much for both the detailed explanation and reference file.
Revision-18.lisp.txt made me really understand the underlying concept
behind condition system.


Regards.
From: Kaz Kylheku
Subject: Re: SIGNAL vs. Closures for Control Passing
Date: 
Message-ID: <7dad4e14-61e0-482d-8244-0c34ea19d24f@1g2000hsl.googlegroups.com>
On Dec 19, 5:53 pm, Volkan YAZICI <·············@gmail.com> wrote:
> I searched c.l.l archives, but couldn't find anything related with the
> overhead and implementation difference between using SIGNAL and
> closures.

SIGNAL is closures plus additional overhead: setting up handlers,
allocating and initializing an instance of the condition, and
searching for the handler. The handler is a function. So it's hard to
imagine in what situation or implementation SIGNAL could possibly
perform better, or even as well, as just using a function.

>(I'm not asking about their functional difference, that's
> explained quite detailed in CLHS. What I wonder really is why SIGNAL
> performs worse in my case despite I expect closures to have a more
> complicated implementation. Aren't they?)

The HANDLER-BIND construct associates conditions with functions
(often, closures are used). E.g.:

 (handler-bind ((error #'(lambda (condition) ...)))
   ;; signal error here
   ... )

SIGNAL has to search through the dynamic activation chain, looking at
all the installed handlers to find one that matches the condition
being signaled, by type, and funcall it. If the handler returns, the
search continues. Often, closures are used for these handlers.

HANDLER-CASE still writes closures behind the scenes. These perform
non-local exits to the handling blocks of code. (I would imagine you
aren't using HANDLER-CASE, since you want your parser to continue
parsing after passing up pieces of extracted data?)