From: Vladimir Zolotykh
Subject: cmucl mp: where is simultaneousness?
Date: 
Message-ID: <20060527151304.40b7f183.gsmith@eurocom.od.ua>
Aren't threads is meant to intervene? I thought they were. However, if
they do in ACL and SBCL (the ones being tested) they don't in CMUCL. Or
at least I don't know how to instruct CMUCL properly.

The following code

  (defparameter *count* 50000000)
  (defun foo (id)
    (let* ((n *count*)
	   (j (/ n 5))
	   (k 0)
	   (start (get-universal-time)))
      (dotimes (i n)
	(when (zerop (mod i j))
	  (format t "~&[~D] i=~D~%" id i))
	(incf k))
      (print k)
      (format t "~&[~D] ~D secs." id (- (get-universal-time) start))))
  (defun test ()
    (dotimes (i 2)
      #+allegro (mp:process-run-function "A test" #'foo i)
      #+cmucl
      (let ((k i))
	(mp:make-process #'(lambda () (foo k))))
      #+sbcl
      (let ((k i))
        (sb-thread:make-thread #'(lambda () (foo k))))))

will help me to state the question. When the function TEST is being
run either in Allegro or SBCL, it outputs something like (fragment)

  [0] i=0
  [1] i=0
  [0] i=10000000
  [1] i=10000000
  ....

However in CMUCL 19c (Using 2.6.16.18-piii kernel GNU/Linux) I see

  * (test)

  NIL
  * 
  [1] i=0
  [1] i=10000000
  [1] i=20000000
  [1] i=30000000
  [1] i=40000000

  50000000 
  [1] 10 secs.
  [0] i=0
  [0] i=10000000
  [0] i=20000000
  [0] i=30000000
  [0] i=40000000

  50000000 
  [0] 10 secs.

Until the one thread is done, no chance for the other to get
processed.

I tried MP::STARTUP-IDLE-AND-TOP-LEVEL-LOOPS, but saw no
difference. Is it possible to get threads in CMUCL to run
"simultaneously"? If so I would appreciate if you tell me how.


-- 
Vladimir Zolotykh

From: Thibault Langlois
Subject: Re: cmucl mp: where is simultaneousness?
Date: 
Message-ID: <1148733941.968361.216830@j73g2000cwa.googlegroups.com>
You have to call mp:process-yield at some point:

CL-USER> (describe 'mp:process-yield)
PROCESS-YIELD is an external symbol in the MULTIPROCESSING package.
Function: #<Function MULTIPROCESSING:PROCESS-YIELD {100EE1B1}>
Function arguments:
  There are no arguments.
Function documentation:
  Allow other processes to run.
Its defined argument types are:
  NIL
Its result type is:
  NULL
On Thursday, 11/17/05 02:07:41 pm GMT it was compiled from:
target:code/multi-proc.lisp
  Created: Tuesday, 7/5/05 02:12:50 pm [+1]
  Comment: $Header: /project/cmucl/cvsroot/src/code/multi-proc.lisp,v
1.43 2005/07/05 13:12:50 rtoy Exp $

Thibault
From: Pascal Bourguignon
Subject: Re: cmucl mp: where is simultaneousness?
Date: 
Message-ID: <87bqtj39o6.fsf@thalassa.informatimago.com>
Vladimir Zolotykh <······@eurocom.od.ua> writes:
> Aren't threads is meant to intervene? I thought they were. However, if

Depends on the kind of threads, the time slice, etc.


> they do in ACL and SBCL (the ones being tested) they don't in CMUCL. Or
> at least I don't know how to instruct CMUCL properly.
> [...]
>       #+cmucl

This is not the right keyword!

> [...]
> I tried MP::STARTUP-IDLE-AND-TOP-LEVEL-LOOPS, but saw no
> difference. Is it possible to get threads in CMUCL to run
> "simultaneously"? If so I would appreciate if you tell me how.


This works:

* (defparameter *count* 5000)

(defun foo (id)
  (let* ((n *count*)
         (j (/ n 5))
         (k 0)
         (start (get-universal-time)))
    (dotimes (i n)
      (when (zerop (mod i j))
        (format t "~&[~D] i=~D~%" id i)
        (sleep 1))
      (incf k))
    (print k)
    (format t "~&[~D] ~D secs." id (- (get-universal-time) start))))

(defun test ()
  (dotimes (i 2)
    (let ((k i))
      ((lambda (fun)
         #+allegro (mp:process-run-function "A test" fun nil)
         #+cmu     (mp:make-process                  fun)
         #+sbcl    (sb-thread:make-thread            fun))
       (lambda () (foo k))))))


(test)

*COUNT*
* 
FOO
* 
TEST
* 
NIL
* 
[1] i=0
[0] i=0
[1] i=1000
[0] i=1000
[1] i=2000
[0] i=2000
[1] i=3000
[0] i=3000
[1] i=4000
[0] i=4000

5000 
[1] 7 secs.
5000 


You can also (sleep 0). 
Perhaps you'll want to use bordeaux-mp instead of all these #+.

-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

There is no worse tyranny than to force a man to pay for what he does not
want merely because you think it would be good for him. -- Robert Heinlein
From: Vladimir Zolotykh
Subject: Re: cmucl mp: where is simultaneousness?
Date: 
Message-ID: <20060527165757.420ac85e.gsmith@eurocom.od.ua>
On Sat, 27 May 2006 14:59:53 +0200
Pascal Bourguignon <···@informatimago.com> wrote:

> >       #+cmucl
> 
> This is not the right keyword!

Sorry, my mistake, the code actually contained #+cmu, when composing
email made a typo.

> This works:

Indeed it does, you are a magician! Could you now please explain 
why it does? I mean what's the big difference between

  (mp:make-process #'(lambda () (foo k)))
[mine]

  ((lambda (fun)
	   (mp:make-process fun))
	 (lambda () (foo k)))
[yours]

Except the additional binding I see no other difference and am at my
wits end to guess why this additional binding so dramatically improves
the result.

-- 
Vladimir Zolotykh
From: Pascal Bourguignon
Subject: Re: cmucl mp: where is simultaneousness?
Date: 
Message-ID: <874pzb33b0.fsf@thalassa.informatimago.com>
Vladimir Zolotykh <······@eurocom.od.ua> writes:

> On Sat, 27 May 2006 14:59:53 +0200
> Pascal Bourguignon <···@informatimago.com> wrote:
>
>> >       #+cmucl
>> 
>> This is not the right keyword!
>
> Sorry, my mistake, the code actually contained #+cmu, when composing
> email made a typo.
>
>> This works:
>
> Indeed it does, you are a magician! Could you now please explain 
> why it does? I mean what's the big difference between
>
>   (mp:make-process #'(lambda () (foo k)))
> [mine]
>
>   ((lambda (fun)
> 	   (mp:make-process fun))
> 	 (lambda () (foo k)))
> [yours]

This is irrelevant to your problem.
I only wanted to isolate the implementation specific stuff from your program.

Ideally, you'd use bordeaux-mp which gives name to these (lambda (fun)
#+...) functions.


> Except the additional binding I see no other difference and am at my
> wits end to guess why this additional binding so dramatically improves
> the result.

What made it work on cmucl is the SLEEP function, which probably calls
MP:PROCESS-YIELD in threads.

-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

"Specifications are for the weak and timid!"
From: Vladimir Zolotykh
Subject: Re: cmucl mp: where is simultaneousness?
Date: 
Message-ID: <20060528120121.1eca7cae.gsmith@eurocom.od.ua>
OK, I get the point at last (kindly forgive my stupidity). The key is

  (sleep 1)

However, I'd say it is a little inconvenient to revise all the code
and insert calls to SLEEP at proper places. It is also so because I
don't know beforehand what time each code fragmet takes. Wrongly put
call to SLEEP may again cause one thread (process) occupies more
processor time than it should. Isn't it possible to automate that?
But to do that we need a thread (= process) which gets processed
periodically disregarding if other threads do SLEEP or not. Kind of a
vicious circle :(


-- 
Vladimir Zolotykh
From: Aurélien (nospam, please) Campéas
Subject: Re: cmucl mp: where is simultaneousness?
Date: 
Message-ID: <447979a2$0$19180$626a54ce@news.free.fr>
Vladimir Zolotykh wrote:
> OK, I get the point at last (kindly forgive my stupidity). The key is
> 
>   (sleep 1)
> 
> However, I'd say it is a little inconvenient to revise all the code
> and insert calls to SLEEP at proper places. It is also so because I
> don't know beforehand what time each code fragmet takes. Wrongly put
> call to SLEEP may again cause one thread (process) occupies more
> processor time than it should. Isn't it possible to automate that?
> But to do that we need a thread (= process) which gets processed
> periodically disregarding if other threads do SLEEP or not. Kind of a
> vicious circle :(
> 
> 

no ! you really want to call MP:PROCESS-YIELD

cmucl has non-preemptive green-threads (probably...), which means you 
have to give back control explicitly

'automating that', that is having a fair scheduler preempt thread 
execution, is provided currently only by these Lisp compilers supporting 
native/os threads (I'm not aware of any one that would work the 
Erlang/Oz way), where fairness is provided by the os kernel scheduler
From: Ivan Boldyrev
Subject: Re: cmucl mp: where is simultaneousness?
Date: 
Message-ID: <qs9ok3-cei.ln1@ibhome.cgitftp.uiggm.nsc.ru>
On 9487 day of my life Vladimir Zolotykh wrote:
> Aren't threads is meant to intervene? I thought they were. However, if
> they do in ACL and SBCL (the ones being tested) they don't in CMUCL.

CMUCL features 'green threads'.  They are not real OS threads (and
don't need OS support), but scheduled by CMUCL itself.  That means
that thread will run until it waits for something (lock/IO), exits or
calls MP:PROCESS-YIELD.  Such threads always run on single processor
even if you have many ones.

One of early versions of Sun JVM (1.x?) used green threads too.

-- 
Ivan Boldyrev

                       Perl is a language where 2 x 2 is not equal to 4.
From: Steven E. Harris
Subject: Re: cmucl mp: where is simultaneousness?
Date: 
Message-ID: <83fyiuginr.fsf@torus.sehlabs.com>
Ivan Boldyrev <···············@cgitftp.uiggm.nsc.ru> writes:

> That means that thread will run until it waits for something
> (lock/IO), exits or calls MP:PROCESS-YIELD.

That makes it difficult to use threads that call on code written
without threading in mind. Say there's some difficult computation,
already written and proven to work correctly, and one wants to run
that computation "simultaneously" against several distinct inputs in
different threads. It sounds like that computation code would need to
be modified to be made thread-aware, just to sprinkle in yield points
to assist the scheduler. How is that acceptable?

-- 
Steven E. Harris
From: Peter Seibel
Subject: Re: cmucl mp: where is simultaneousness?
Date: 
Message-ID: <m2slmuouan.fsf@gigamonkeys.com>
"Steven E. Harris" <···@panix.com> writes:

> Ivan Boldyrev <···············@cgitftp.uiggm.nsc.ru> writes:
>
>> That means that thread will run until it waits for something
>> (lock/IO), exits or calls MP:PROCESS-YIELD.
>
> That makes it difficult to use threads that call on code written
> without threading in mind. Say there's some difficult computation,
> already written and proven to work correctly, and one wants to run
> that computation "simultaneously" against several distinct inputs in
> different threads. It sounds like that computation code would need to
> be modified to be made thread-aware, just to sprinkle in yield points
> to assist the scheduler. How is that acceptable?

Well, if the code ever does I/O then you don't need to because you'll
get the implicit yields when the threads do I/O. And if your code
never does I/O you're not really going to be running them
"simultaneously" unless you actually have multiple CPUs. I.e. if the
work is completely compute bound, the CPU is busy all the time and
switching between tasks in order to provide an illusion of
simultaneity is mostly just going to burn cycles on task-switching
overhead. Which is not to say that there aren't situations here an
illusion of simultaneity would be worth the overhead such as if you
want to run several computations each of which might produce an answer
and as soon as one of them does you want to abandon the others. But
one common use of threads is basically to keep the CPU busy by
switching to another task while one task is stalled waiting for I/O to
complete. For that purpose, and assuming you only have one CPU, green
threads, such as are provided by CMU are completely acceptable. Of
course if you actually have multiple CPUs they're less so since you
can't (in one process) take advantage of multiple CPUs.

-Peter

-- 
Peter Seibel           * ·····@gigamonkeys.com
Gigamonkeys Consulting * http://www.gigamonkeys.com/
Practical Common Lisp  * http://www.gigamonkeys.com/book/
From: Thibault Langlois
Subject: Re: cmucl mp: where is simultaneousness?
Date: 
Message-ID: <1148847998.321193.250670@y43g2000cwc.googlegroups.com>
I've been using cmucl's green threads and i'm quite happy with them but
as multi-core processor become more common, os-based threads may
take the advantage. I was wondering: can two os-based thread comunicate
by changing some special variables values or is it necessary to use a
different mechanism ?  

Thibault
From: Marcin 'Qrczak' Kowalczyk
Subject: Re: cmucl mp: where is simultaneousness?
Date: 
Message-ID: <877j45dhhj.fsf@qrnik.zagroda>
Peter Seibel <·····@gigamonkeys.com> writes:

> Well, if the code ever does I/O then you don't need to because you'll
> get the implicit yields when the threads do I/O. And if your code
> never does I/O you're not really going to be running them
> "simultaneously" unless you actually have multiple CPUs.

Here is an example where preemptive concurrency from the programmer's
point of view is important, even though it doesn't matter whether it's
truly parallel and preemptive from the OS point of view:

Computer plays a game like chess. During computer's turn, the user can
interrupt it (and e.g. undo his last move). If there is a time limit
for computer's move, expiration of the limit interrupts the analysis
and performs the currently best move. And during human's turn, the
computer doesn't waste time but performs analysis for replies for all
possible user moves, so it doesn't have to start from zero when its
turn comes.

This could still be archieved with cooperative threading by inserting
enough yield points in the code performing the analysis; this is ugly
and unmodular.

While supporting green threads at all does constrain implementation
choices and is non-trivial to add to an existing implementation,
making such threads preemptive is quite easy. Especially if the
runtime already supports interruption by Unix signals or ^C. If a Lisp
implementation requires explicit yields, it seems that it should be
easy to be improved.

-- 
   __("<         Marcin Kowalczyk
   \__/       ······@knm.org.pl
    ^^     http://qrnik.knm.org.pl/~qrczak/
From: Rob Warnock
Subject: Re: cmucl mp: where is simultaneousness?
Date: 
Message-ID: <ZdCdnTWor_2YqufZnZ2dnUVZ_s-dnZ2d@speakeasy.net>
Marcin 'Qrczak' Kowalczyk  <······@knm.org.pl> wrote:
+---------------
| While supporting green threads at all does constrain implementation
| choices and is non-trivial to add to an existing implementation,
| making such threads preemptive is quite easy. Especially if the
| runtime already supports interruption by Unix signals or ^C. If a Lisp
| implementation requires explicit yields, it seems that it should be
| easy to be improved.
+---------------

My understanding [and I invite anyone who knows better to update me!]
is that CMUCL's signal handling is not *quite* 100% reliable yet,
and that there are rare, tiny windows wherein a signal interrupt
might not be continuable. While that is (barely) tolerable when the
only signals being used are manual ^C to check on progress or kill
runaway code, even a very small probability of failure is unacceptable
when using frequent SIGALRM (timer) interrupts for pre-emptive
scheduling. So even though there *is* already code in CMUCL to
provide that, it is not enabled by default.

Aha! Here it is! Look starting at line 1488 in "src/code/multi-proc.lisp":

  ;;; Start-Sigalrm-Yield  --  Internal
  ;;;
  ;;; Start a regular interrupt to switch processes. This may not be a
  ;;; good idea yet as the CMUCL code is not too interrupt safe.
  ;;;
  (defun start-sigalrm-yield (&optional (sec 0) (usec 500000))
    "Start a regular SIGALRM interrupt which calls process-yield. An optional
    time in seconds and micro seconds may be provided. Note that CMUCL code
    base is not too interrupt safe so this may cause problems."
    ... )

So you could always call (START-SIGALRM-YIELD 0 100000) [for 0.1s slices]
and take your chances on hitting a bad window...

A very brief test suggests that it does work as expected [when it works]:

    cmu> (mp::startup-idle-and-top-level-loops)	; not necessary, but helps

    cmu> (mp::start-sigalrm-yield 0 100000)	; use pre-emptive scheduling

    cmu> (defun busywait () (dotimes (i 1000000))) ; note: *NOT* compiled!
						; takes ~4s on my laptop
    BUSYWAIT
    cmu> (defun start (name count)
	   (mp:make-process
	     (lambda ()
	       (loop for i below count do
		 (busywait)
		 (format t "~&~s: step ~d~%" name i)))))

    START
    cmu> (start :first 10)

    #<Process Anonymous {4838A01D}>
    cmu> (start :second 10)

    #<Process Anonymous {48781735}>
    cmu> (start :third 10)
    :FIRST: step 0
    #<Process Anonymous {482D2E25}>
    cmu> 
    :SECOND: step 0
    :FIRST: step 1
    :THIRD: step 0
    :SECOND: step 1
    :FIRST: step 2
    :THIRD: step 1
    :SECOND: step 2
    (mp:all-processes)    ; <== hand-typed
    (#<Process Anonymous {482D2E25}> #<Process Anonymous {48781735}>
     #<Process Anonymous {4838A01D}> #<Process Top Level Loop {4814E9F5}>
     #<Process Idle Loop {48007C1D}>)
    cmu> 
    :FIRST: step 3
    :THIRD: step 2
    :SECOND: step 3
    ...[and so on]...
    :SECOND: step 8
    :THIRD: step 8
    :FIRST: step 9
    :SECOND: step 9
    :THIRD: step 9
    (mp:all-processes)    ; <== hand-typed
    (#<Process Top Level Loop {4814E9F5}> #<Process Idle Loop {48007C1D}>)
    cmu> 


-Rob

-----
Rob Warnock			<····@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607
From: David Golden
Subject: Re: cmucl mp: where is simultaneousness?
Date: 
Message-ID: <z_meg.9666$j7.306537@news.indigo.ie>
Steven E. Harris wrote:

> Ivan Boldyrev <···············@cgitftp.uiggm.nsc.ru> writes:
> 
>> That means that thread will run until it waits for something
>> (lock/IO), exits or calls MP:PROCESS-YIELD.
> 
> That makes it difficult to use threads that call on code written
> without threading in mind. Say there's some difficult computation,
> already written and proven to work correctly, and one wants to run
> that computation "simultaneously" against several distinct inputs in
> different threads. It sounds like that computation code would need to
> be modified to be made thread-aware, just to sprinkle in yield points
> to assist the scheduler. How is that acceptable?
> 

"cooperative" threading still allows you to write stuff that's most
clearly expressed as (perhaps naively) multithreaded code (clarity
being in the eyes of the beholder to some extent, I don't buy into the
notion that multithreaded code is automatically unclear), while also
avoiding a class of threading bugs that your (perhaps naive) code would
otherwise have, since at any given time, only one "thread" is really
executing.  Relying on that may be judged "bad practice"  in the modern
era, because someone will go and assume you were multithreading to
allow simultaneous multiprocessing on a real multiprocessor computer
rather than for clarity and ease of implementation, but hey.

If you've got code that was never written to be thread-safe, most of the
time you can just run it in another OS process, cmucl is a
lisp-on-unix/linux primarily, and processes are pretty cheap on linux,
even cmucl ones (by modern bloaty standards... Though they'd *look*
cheaper if cmucl didn't do that big static VM grab thing. No, I'm not
volunteering to rewrite cmucl or sbcl memory allocation, that's a Big
Job).  That's not to say sbcl's native threading efforts aren't
worthwhile, it's still very nice to be able to have a full spectrum of
options. However, people do sometimes mistake platform-specific
weaknesses for general rules."windows processes are expensive"
becomes "processes are expensive", "windows command line sucks"
becomes "command  lines suck", etc.
From: Julian Stecklina
Subject: Re: cmucl mp: where is simultaneousness?
Date: 
Message-ID: <86lkslgb4n.fsf@dellbeast.localhost>
"Steven E. Harris" <···@panix.com> writes:

> That makes it difficult to use threads that call on code written
> without threading in mind. 

You could automatically instrument all functions in, say, a given
package or by name to call PROCESS-YIELD, if they are called or
return. Or you also could shadow DEFUN to instrument single functions.

Regards,
-- 
Julian Stecklina

Being really good at C++ is like being really good at using rocks to
sharpen sticks. - Thant Tessman
From: Steven E. Harris
Subject: Re: cmucl mp: where is simultaneousness?
Date: 
Message-ID: <834pz8gogi.fsf@torus.sehlabs.com>
Julian Stecklina <··········@web.de> writes:

> You could automatically instrument all functions in, say, a given
> package or by name to call PROCESS-YIELD, if they are called or
> return.

That's a nice idea.

Aside from shadowing DEFUN as you had suggested, is there another
mechanism by which one can do this instrumenting? It sounds like a
before/after/around method mechanism, but you didn't limit your
suggestion to methods. You must have had normal functions in mind as
well.

-- 
Steven E. Harris
From: Thomas A. Russ
Subject: Re: cmucl mp: where is simultaneousness?
Date: 
Message-ID: <ymiodxfgywh.fsf@sevak.isi.edu>
"Steven E. Harris" <···@panix.com> writes:

> Julian Stecklina <··········@web.de> writes:
> 
> > You could automatically instrument all functions in, say, a given
> > package or by name to call PROCESS-YIELD, if they are called or
> > return.
> 
> Aside from shadowing DEFUN as you had suggested, is there another
> mechanism by which one can do this instrumenting?

Some lisp systems support the non-standard ADVISE functionality, which
lets you wrap code around existing functions.  But I don't think CMUCL
is one of the lisps that support this.



-- 
Thomas A. Russ,  USC/Information Sciences Institute
From: Rob Warnock
Subject: Re: cmucl mp: where is simultaneousness?
Date: 
Message-ID: <YMidncfgFN1b-ePZnZ2dnUVZ_t6dnZ2d@speakeasy.net>
Thomas A. Russ <···@sevak.isi.edu> wrote:
+---------------
| "Steven E. Harris" <···@panix.com> writes:
| > Julian Stecklina <··········@web.de> writes:
| > > You could automatically instrument all functions in, say, a given
| > > package or by name to call PROCESS-YIELD, if they are called or
| > > return.
| > 
| > Aside from shadowing DEFUN as you had suggested, is there another
| > mechanism by which one can do this instrumenting?
| 
| Some lisp systems support the non-standard ADVISE functionality, which
| lets you wrap code around existing functions.  But I don't think CMUCL
| is one of the lisps that support this.
+---------------

CMUCL has "Function Wrappers":

    http://common-lisp.net/project/cmucl/doc/cmu-user/extensions.html#toc83
    ...
    Function wrappers, fwrappers for short, are a facility for
    efficiently encapsulating functions.
    ...
    A function wrapper replaces the primary function in the fdefn
    object with a function of its own, and records the original
    function in an fwrapper object, a funcallable instance. Thus,
    when the function is called, the fwrapper gets called, which in
    turn might call the primary function, or a previously installed
    fwrapper that was found in the fdefn object when the second
    fwrapper was installed.
    ...

Not sure it can do what you want, but there it is...  ;-}


-Rob

-----
Rob Warnock			<····@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607
From: a
Subject: Re: cmucl mp: where is simultaneousness?
Date: 
Message-ID: <tsNfg.1695$5J6.100@newsread1.mlpsca01.us.to.verio.net>
"Rob Warnock" <····@rpw3.org> wrote in message
<snip>
>
> CMUCL has "Function Wrappers":
>
>    http://common-lisp.net/project/cmucl/doc/cmu-user/extensions.html#toc83
>    ...
>    Function wrappers, fwrappers for short, are a facility for
>    efficiently encapsulating functions.
<snip>

Advice and function wrappers are easy to implement from scratch.

(defun foo (a b)
     (* a (+ b 10)))
FOO

(foo 2 3)
26

 (defun wrap (sym-fun wrapper-fun)
        (let ((orig-fun (symbol-function sym-fun)))
             (setf (symbol-function sym-fun)
                      (lambda (&rest args)
                        (funcall wrapper-fun sym-fun orig-fun args)))))
WRAP

(wrap 'foo #'(lambda (sym fun args)
                            (format t "~&Before ~A" sym)
                            (let ((result (apply fun args)))
                               (format t"~&After ~A" sym)
                               result)))
#<interpreted closure (LAMBDA (&REST ARGS) (FUNCALL WRAPPER-FUN SYM-FUN 
ORIG-FUN ARGS))>

(foo 2 3)
Before FOO
After FOO
26

Beware attempting that with symbols in the COMMON-LISP package. Unwrapping, 
or deleting the advice, is accomplished by setf'ing the symbol-function to 
its orginal value. Retrieving that value must be planned for before the 
original wrap.
From: Ivan Boldyrev
Subject: Re: cmucl mp: where is simultaneousness?
Date: 
Message-ID: <oae8l3-aiu.ln1@ibhome.cgitftp.uiggm.nsc.ru>
On 9493 day of my life ·@bc.def wrote:
> Advice and function wrappers are easy to implement from scratch.
...
>  (defun wrap (sym-fun wrapper-fun)
>         (let ((orig-fun (symbol-function sym-fun)))
>              (setf (symbol-function sym-fun)
>                       (lambda (&rest args)
>                         (funcall wrapper-fun sym-fun orig-fun args)))))

Unfotunately, it _can_ cause unspecified behaviour: 

,----[ 3.2.2.3 Semantic Constraints ]
| All conforming programs must obey the following constraints, which are
| designed to minimize the observable differences between compiled and
| interpreted programs:
| ...
| * A call within a file to a named function that is defined in the same
|   file refers to that function, unless that function has been declared
|   notinline. The consequences are unspecified if functions are
|   redefined individually at run time or multiply defined in the same
|   file.
`----

NOTINLINE declaration is necessary for the adviced function.

However, OP has problem with the only one implementation, thus he is
safe.

-- 
Ivan Boldyrev

                       Perl is a language where 2 x 2 is not equal to 4.