Lisp HUG Maillist Archive

(Lisp-HUG) Identifying Calling Function...

Greetings to the List -

Question:  Is there any way in standard Common Lisp to identify a 
calling function?

E.g., (cons (f1 x) (f2 x))

....is there any rational way to figure out inside f1 the fact that 
the next function up the food chain is cons???

As a fallback, is there any way to do that in Lisp Works as a 
non-standard call???

Regards from the Rocky Mountains,

Jack Harper
Secure Outcomes Inc.
Evergreen, Colorado USA


_______________________________________________
Lisp Hug - the mailing list for LispWorks users
lisp-hug@lispworks.com
http://www.lispworks.com/support/lisp-hug.html


Re: (Lisp-HUG) Identifying Calling Function...

Unable to parse email body. Email id is 11680

RE: (Lisp-HUG) Identifying Calling Function...

In LW you should be able to do it with defadvice on each function. Or you
could perhaps use trivial-backtrace
(http://common-lisp.net/project/trivial-backtrace/), though as Paul points
out, the functions may not be there after the optimizer has done its job.


Eivind

> -----Original Message-----
> From: owner-lisp-hug@lispworks.com [mailto:owner-lisp-hug@lispworks.com]
> On Behalf Of Jack Harper
> Sent: 3. august 2012 17:26
> To: lisp-hug@lispworks.com
> Subject: (Lisp-HUG) Identifying Calling Function...
> 
> 
> Greetings to the List -
> 
> Question:  Is there any way in standard Common Lisp to identify a calling
> function?
> 
> E.g., (cons (f1 x) (f2 x))
> 
> ...is there any rational way to figure out inside f1 the fact that the
next function
> up the food chain is cons???
> 
> As a fallback, is there any way to do that in Lisp Works as a non-standard
call???
> 
> Regards from the Rocky Mountains,
> 
> Jack Harper
> Secure Outcomes Inc.
> Evergreen, Colorado USA
> 
> 
> _______________________________________________
> Lisp Hug - the mailing list for LispWorks users lisp-hug@lispworks.com
> http://www.lispworks.com/support/lisp-hug.html

_______________________________________________
Lisp Hug - the mailing list for LispWorks users
lisp-hug@lispworks.com
http://www.lispworks.com/support/lisp-hug.html


RE: (Lisp-HUG) Identifying Calling Function...


 > Question:  Is there any way in standard Common Lisp to identify a calling
 > function?
 >
 > E.g., (cons (f1 x) (f2 x))
 >
 > ...is there any rational way to figure out 
inside f1 the fact that the next function up the food chain is cons???


At 12:12 PM 8/3/2012, Eivind Midtgård wrote:
>In LW you should be able to do it with defadvice on each function. Or you
>could perhaps use trivial-backtrace
>(http://common-lisp.net/project/trivial-backtrace/), though as Paul points
>out, the functions may not be there after the optimizer has done its job.


Greetings to the List -

I asked the question a few days ago because I am 
trying to see what sort of processing 
efficiencies might be realized by moving our 
commercial application --- forensic fingerprint 
scanning and analysis (103,000 lines of Common 
Lisp) --- from a single-core embedded Pentium 
processor to multi-core hardware with LW6.x.

My interest, for years now, is to try to build a 
"clean" multiprocessing software programming 
"paradigm" (hate that word!), that I can 
understand, rather than just manually hacking in 
the LW6.x threads/processes etc.

If you assume that hardware core counts will 
double about every year, then 1K-core platforms 
should be the norm within ten years or so, which 
is the assumed product life of this commercial 
software (with enhancements, of course). Thus, 
ideally, the software moved to the parallel 
hardware multi-core environment should 
automatically speed up with no modification or 
thought as the core count increases over time (at 
least no worse than according to Amdahl's Law - 
http://en.wikipedia.org/wiki/Amdahl%27s_law).

I first worked with multiprocessing forty years 
ago on a UNIVAC mainframe and learned that it is 
not easy. Fifteen years later, I worked on a 
larger scale parallel machine with up to 128 
processors (http://frobenius.com/spock.gif), 
where I learned that building large scale general 
parallel software is a nightmare and exceedingly difficult to do.

I believe that there are two primary problems: 
(a) Inadequate parallelism syntax that leads to 
application code being sprinkled with confusing, 
ugly, and irritating control/synchronization 
primitives; and (b) It is easy to spawn hordes of 
processes, but not so easy to gracefully kill all 
of them off once an answer (e.g., a search in a Search Space) has been found.

Transactional Memory (TM) appears promising, but 
is really not my primary cup of tea: (a) I think 
that it is more oriented toward protecting 
processes that mostly modify small sets of 
existing objects in atomic 'transactions' rather 
than creating new objects or munging large 
subsets; and (b) I think, especially, that it 
will require special hardware to work effectively 
(I/O is also problematic; how do you undo I/O 
when a transaction has to be rolled back?).

I very much believe that Good Code is inherently 
like Good Physics - The controlling primitives in 
software code have to be "pretty" and a joy to 
read to have any chance to be good/trusted to 
work. If you can't easily read and understand the 
code (like reading/understanding Maxwell's 
Equation primitives in physics), then there is 
almost no way that the code can be determined to 
be "right" --- and control/synchronization 
primitives sprinkled everywhere in code is 
certainly not "pretty", much less understandable. 
I believe that the key to parallel programming is 
absolute brevity in the control syntax.


After some thought over the past several years 
(in my not so abundant spare time), perhaps the 
following syntax to deal with parallel processing 
on multi-core hardware might be useful:

e.g.,

(defun build-tree(x n)
   (cond ((zerop n) x)
            (t (cons {build-tree x (1- 
n)}              ; Note braces... ...{} denotes a 'future'...
                        {build-tree x (1- 
n)}))))          ; Note braces... ...{} denotes a 'future'...

where the forms in braces (i.e., the {}s) are 
"futures" 
(http://en.wikipedia.org/wiki/Future_%28programming%29 
- stolen for this from Halstead's MultiLisp circa 1980).

A future transparently spawns a process to 
evaluate its form in parallel, but immediately 
returns a placeholder (pointer to a "future" 
object) so the original calculation can proceed 
until it needs an actual value at which time it 
hangs until that calculated value exists. Once 
the future has been resolved with an answer, then 
the pointer to the future is automatically 
replaced with a pointer to the actual value and 
the delayed process happily proceeds.

This on-the-fly pointer replacement is not so 
easy to do in standard Common Lisp but can, I 
think, be implemented with closures, threads, 
messaging, and other messy things setup by the {} Read Macro.

The above example Build-Tree constructs a tree of 
depth n with each leaf being x.  Thus, 
(build-tree 3.14   20) will construct a 20-depth tree with about a million PIs.

The above Build-Tree, with the {} forms, 
automatically spawns new processes as needed that build and return sub-trees.

The primary thing that I like about the {} read 
macro above is that the general form of the 
parallel version of the code (above) is identical to the serial form.

Implementation of the {} read macro, at first 
glance, seems simple (well, simplish) and 
obvious:  A Read Macro for the {} constructs a 
correct form with all of the LW 6.x parallel 
processing control and other mechanisms injected.

However, there is a problem (actually 
several):  In the above Build-Tree, pointers to 
sub-trees are actually pointers to futures - and 
those pointers need to automatically and 
asynchronously replace themselves with pointers 
to final resolved sub-trees of cons cells, once they are generated.


Another situation:

(+  {f1 x}  {f2 y}  {f3 z})

In Build-Tree above, cons will happily cons up 
futures that will eventually be replaced with 
pointed to realized values, but the + function 
doesn't know about futures and absolutely 
requires numeric arguments. Thus, the {} read 
macro has to deal with hanging the process that 
is running the function + until all of the future arguments have been resolved.


The only reasonable way that I see a solution to 
all this would be for the {} read macro to know 
what the calling function is (e.g., is it 
something like cons that will accept literally 
anything in which case let the new process run 
etc - or is it something like + that requires a 
particular and specific type such as a number in 
which case hang the + process until the futures all resolve to numbers).


These are, of course (I think), really binding issues:

(let ((v {f x}))....)

As usual, the {f x} is a future. The binding of v 
(ignoring, for the moment, the compiler) is, 
perhaps, a pointer to a future, a pointer to a 
resolved primitive Lisp object (e.g., 3), or a 
pointer, for example, to a cons cell with two 
futures embedded.  At some point, asynchronously, 
the binding of v will change from a future to a 
realized value.  So, at one moment, v -> {future} 
and at another later moment, v -> 3.

I find it interesting to note (again, ignoring 
the compiler) that *all* user defined functions 
will immediately accept future values - it is 
only Lisp primitives (e.g., +) that require 
actual resolved values (is this really true???).


Refinements on the {} read macro:

(a) {} would know the core count and its 
generated code would only spawn a new process at 
runtime if a core was available, otherwise it 
would treat at runtime the {}s as ()s and execute 
serially, which would help to minimize choking 
the system with spawned processes.

(b) A Throw somewhere deep inside a spawned 
process that goes outside the top-level would 
automatically terminate all other generated 
processes. This would be a clean way to terminate 
all those hordes of processes that are searching 
a Search Space etc (neat idea stolen from Gabriel 
and McCarthy's QLAMBDA circa 1984).

(c) Once {} has been implemented, then something 
else ('atomic'?) would be added to the system to 
provide some sort of higher-level object-level synchronization/protection.

(d) etc


All of the above could be implemented with access 
(and knowledge!) of the Lisp compiler, etc. 
However, I am trying to construct something that 
will work in standard Common Lisp (I don't see how) - or with LW6.x primitives.

One messy implementation technique could be for 
the {} read macro to figure out (at compile time; 
run time?) the calling function (e.g., cons in 
the above Build-Tree) and generate code 
accordingly (thus, my original question) based on 
the function: Cons would be treated in the macro 
expansion differently than +. Gee, there are over 
800 of those things in CL - each to be analyzed...

Of course, any Common Lisp system that supports 
parallelism is living in a State Of Sin anyway 
(e.g., arguments to a function are evaluated 
"strictly left to right" --- and I am not sure what others).


So, question:  Anyone have any thoughts as to how 
to possibly implement the {} parallel processing 
read macro in LW6.x Lisp without, hopefully, 
messily thinking about the calling function (cons, + above)?????

Just gotta' be an un-ugly way --- something not too horribly offensive...


Regards from a Slow and Lovely Summer Day in the Rocky Mountains,

Jack Harper
Secure Outcomes Inc
Evergreen, Colorado USA





_______________________________________________
Lisp Hug - the mailing list for LispWorks users
lisp-hug@lispworks.com
http://www.lispworks.com/support/lisp-hug.html


Re: (Lisp-HUG) Identifying Calling Function...

On Sun, Aug 5, 2012 at 7:14 PM, Paul Tarvydas <paul.tarvydas@rogers.com> wrote:
> [disclaimer reprise: multi-core utilization will not not benefit from
> "automatic" conversion of low-level programming operations. It requires a
> change in architectural thinking and restructuring of designs (e.g. along
> the lines of TTL hardware designs).]

Generally true, but not entirely true. Many algorithms get automatic
speedup from parallel evaluation in conjunction with Cilk-like
techniques (code transformations + back end). See for example my
implementation (lparallel.org/defpun).

Syntactic sugar aside, the OP is basically talking about
lparallel:pfuncall, which was subject to Cilk-like transformations in
the previous version of lparallel. This was disabled in the last
version, however spurred by this thread I am considering enabling it
again.

Sugaring parallel evaluation is a cute idea, though in practice I
would probably use a regular macro.

(ql:quickload :lparallel)

(defun curly-reader (stream char)
  (declare (ignore char))
  (append `(lparallel:pfuncall (function ,(read stream)))
          (read-delimited-list #\} stream t)))

(set-macro-character #\{ #'curly-reader)
(set-macro-character #\} (get-macro-character #\)))

CL-USER> (time (+ (progn (sleep 0.1) 3) (progn (sleep 0.1) 4)))
Timing the evaluation of (+ (PROGN (SLEEP 0.1) 3) (PROGN (SLEEP 0.1) 4))

User time    =        0.000
System time  =        0.000
Elapsed time =        0.200
Allocation   = 14680 bytes
0 Page faults
7

CL-USER> (time {+ (progn (sleep 0.1) 3) (progn (sleep 0.1) 4)})
Timing the evaluation of (LPARALLEL.COGNATE:PFUNCALL (FUNCTION +)
(PROGN (SLEEP 0.1) 3) (PROGN (SLEEP 0.1) 4))

User time    =        0.000
System time  =        0.000
Elapsed time =        0.100
Allocation   = 12068 bytes
0 Page faults
7

_______________________________________________
Lisp Hug - the mailing list for LispWorks users
lisp-hug@lispworks.com
http://www.lispworks.com/support/lisp-hug.html


Re: (Lisp-HUG) Identifying Calling Function...

Greetings to the List -

I appreciate the replies (Paul, Eivind, Mikel, Tim, James) to my 
question regarding parallel processing with LW6.x.

e.g., from Paul:

>
>(+  {f1 x}  {f2 y}  {f3 z})
>
>My first thought is - change your syntax, to
>
>{+ (f1 x) (f2 x) (f3 x)}


I am trying to avoid the  {+ (f1 x) (f2 x) (f3 x)}  form because I 
lose control of exactly what is spawned as parallel processes - in 
this case, suddenly all the arguments (f1 x), (f2 x), and (f3 x) are 
spawned out as separate processes.  Building the {} macro for that 
is, in fact, straightforward.

But, the  {+ (f1 x) 2 3 4 (f2 x)}  form does not seem like a good 
idea to me because *everything* is spawned out.  However, the {} 
macro could certainly watch for simple arguments like integers and 
the like and not spawn those, but still it bothers me that all 
non-simple arguments would be spawned out.

The  {+ (f1 x) (f2 x) (f3 x)}  form has been used since ancient times 
- Halstead's MultiLisp (in a syntactically different form as (future 
x) etc), Gabriel/McCarthy's Qlambda (also in a syntactically 
different form - qlet & friends) etc - but, they are more or less equivalent.

In that case, {} really means, "evaluate the arguments of the 
enclosed form in parallel and hang until all that is done".  What I 
am after is for the {} to mean, "spawn the form inside the braces as 
a single separate process and immediately return a pointer to a 
future, which will eventually turn into something else once it has 
been calculated."

I believe that the (+  {f1 x}  {f2 y}  {f3 z}) form is better so that 
I control who gets spawned out and who does not - e.g., I may prefer 
(+  (f1 x)  {f2 y}  (f3 z)) for some reason - clear to me but not to 
the {} macro or to the compiler....

However, I certainly slowly begin to understand why people that 
actually implemented such things over the past four decades have 
favored the {+ (f1 x) (f2 x) (f3 x)}  form.

With everything of importance *inside* the braces, the macro can 
easily see everything and deal with it.

I just don't see how to reasonably implement the {} macro (without 
access to the compiler) that I wish for when what it needs to do 
depends on things that are outside its scope. That is the fundamental problem.

Thoughts??


Regards,

Jack Harper
Secure Outcomes Inc
Evergreen, Colorado USA





_______________________________________________
Lisp Hug - the mailing list for LispWorks users
lisp-hug@lispworks.com
http://www.lispworks.com/support/lisp-hug.html


Re: (Lisp-HUG) Identifying Calling Function...

On Tue, Aug 7, 2012 at 6:47 PM, Jack Harper <jharper@secureoutcomes.net> wrote:
> I am trying to avoid the  {+ (f1 x) (f2 x) (f3 x)}  form because I lose
> control of exactly what is spawned as parallel processes - in this case,
> suddenly all the arguments (f1 x), (f2 x), and (f3 x) are spawned out as
> separate processes.  Building the {} macro for that is, in fact,
> straightforward.
>
> But, the  {+ (f1 x) 2 3 4 (f2 x)}  form does not seem like a good idea to me
> because *everything* is spawned out.  However, the {} macro could certainly
> watch for simple arguments like integers and the like and not spawn those,
> but still it bothers me that all non-simple arguments would be spawned out.
>
> The  {+ (f1 x) (f2 x) (f3 x)}  form has been used since ancient times -
> Halstead's MultiLisp (in a syntactically different form as (future x) etc),
> Gabriel/McCarthy's Qlambda (also in a syntactically different form - qlet &
> friends) etc - but, they are more or less equivalent.
>
> In that case, {} really means, "evaluate the arguments of the enclosed form
> in parallel and hang until all that is done".  What I am after is for the {}
> to mean, "spawn the form inside the braces as a single separate process and
> immediately return a pointer to a future, which will eventually turn into
> something else once it has been calculated."
>
> I believe that the (+  {f1 x}  {f2 y}  {f3 z}) form is better so that I
> control who gets spawned out and who does not - e.g., I may prefer (+  (f1
> x)  {f2 y}  (f3 z)) for some reason - clear to me but not to the {} macro or
> to the compiler....
>
> However, I certainly slowly begin to understand why people that actually
> implemented such things over the past four decades have favored the {+ (f1
> x) (f2 x) (f3 x)}  form.
>
> With everything of importance *inside* the braces, the macro can easily see
> everything and deal with it.
>
> I just don't see how to reasonably implement the {} macro (without access to
> the compiler) that I wish for when what it needs to do depends on things
> that are outside its scope. That is the fundamental problem.
>
> Thoughts??

Why not change what you wish for? In particular, change it into a wish
that has already been granted :) The lparallel:plet macro addresses
this need,

(plet ((a (f1 x))
       (b (f2 x)))
  (+ a 2 3 4 b))

PLET does not hang before executing its body. A and B are symbol
macrolets; their respective computations are forced only when needed.
Thus you have the option to insert code before (+ a 2 3 4 b), that is,
to do some computation while A and B are being computed.

Also, PLET forms which appear inside DEFPUN undergo Cilk
transformations, where tasks are chunkified and waiting is replaced
with work-stealing. This is a huge optimization, typically turning a
net slowdown into a net speedup.

As a general rule you should not trust multi-threaded libraries
because they are so hard to get right. I can only say that the
lparallel tests are brutal, and no CL implementation has survived
without confessing a bug in itself. The possible exception is SBCL,
which merely had a performance bug in MAP-INTO (now fixed).

Please excuse the self-promotion -- when I notice someone trying to
solve a problem that I've already solved, I can't help but speak up
:). I recognize some well-worn paths here.

Best,
lmj

_______________________________________________
Lisp Hug - the mailing list for LispWorks users
lisp-hug@lispworks.com
http://www.lispworks.com/support/lisp-hug.html


Re: (Lisp-HUG) Identifying Calling Function...


* Jack Harper <201208072248.q77Mmc78040953@lwfs1-cam.cam.lispworks.com> :
Wrote on Tue, 07 Aug 2012 16:47:38 -0600:

| However, I certainly slowly begin to understand why people that
| actually implemented such things over the past four decades have
| favored the {+ (f1 x) (f2 x) (f3 x)}  form.
|
| With everything of importance *inside* the braces, the macro can
| easily see everything and deal with it.
|
| I just don't see how to reasonably implement the {} macro (without
| access to the compiler) that I wish for when what it needs to do
| depends on things that are outside its scope. That is the fundamental
| problem.

Maybe I'm misunderstanding, but this appears to fairly simple with
symbol macros, PROVIDED you are willing to make F1 F2 F3 macros which
delegate to actual functions:

(define-symbol-macro -harper-calling-op- nil)

(defmacro lbrace (op &rest forms)
  `(symbol-macrolet ((-harper-calling-op- ,op))
     (,op ,@forms)))

#||
;; TEMPLATE/SKETCH

(defun f1-delegate (x) (+ x 10))

(defmacro f1 (x &environment env)
  `(progn
     (format t "~S CALLS ~S.~&" (macroexpand-1 '-harper-calling-op- ,env ) 'f1)
     (f1-delegate ,x)))

(lbrace + (f1 10)) ; => 20
;;=> + CALLS F1.
||#

;; This can be wrapped up in some crude macros, so the calling function
;; can be made available to the callee via a special.

(defvar *harper-calling-op* nil)

(eval-when (load eval compile)
  (defun delegate-name (symbol)
    (intern (concatenate 'string (symbol-name symbol) "-HARPER"))))

(defmacro defharperfun (name (&rest list) &body body)
  (let ((delegate (delegate-name name))
        (args `(list ,@list)))
    `(progn
       (defmacro ,name (,@list &environment env)
         `(let ((*harper-calling-op* (macroexpand-1 '-harper-calling-op- ,env)))
            (apply ',',delegate ',,args)))
       (defun ,delegate (,@list) ,@body))))


;; Example:

(defharperfun f1 (x) (+ x 1))
(defharperfun f2 (x) (+ x 2))
(defharperfun f3 (x) (warn "F3 called by ~S." *harper-calling-op*) (+ x 3))

(lbrace + (f1 0) (f1 10) (f2 20)))

... though I'm not sure this will help solve your actual problem ..

--- Madhu

_______________________________________________
Lisp Hug - the mailing list for LispWorks users
lisp-hug@lispworks.com
http://www.lispworks.com/support/lisp-hug.html


Re: (Lisp-HUG) Identifying Calling Function...

On Tue, Aug 7, 2012 at 6:47 PM, Jack Harper <jharper@secureoutcomes.net> wrote:
> I believe that the (+  {f1 x}  {f2 y}  {f3 z}) form is better so that I
> control who gets spawned out and who does not - e.g., I may prefer (+  (f1
> x)  {f2 y}  (f3 z)) for some reason - clear to me but not to the {} macro or
> to the compiler....

By the way you can actually do this with enough effort -- you just
need a codewalker to lift out the {} forms, transforming

(+  (f1 x)  {f2 y}  (f3 z))

into

(plet ((#:G413 (f2 y)))
  (+  (f1 x) #:G413 (f3 z)))

_______________________________________________
Lisp Hug - the mailing list for LispWorks users
lisp-hug@lispworks.com
http://www.lispworks.com/support/lisp-hug.html


Re: (Lisp-HUG) Identifying Calling Function...

On Wed, Aug 8, 2012 at 1:30 AM, James M. Lawrence <llmjjmll@gmail.com> wrote:
> On Tue, Aug 7, 2012 at 6:47 PM, Jack Harper <jharper@secureoutcomes.net> wrote:
>> I believe that the (+  {f1 x}  {f2 y}  {f3 z}) form is better so that I
>> control who gets spawned out and who does not - e.g., I may prefer (+  (f1
>> x)  {f2 y}  (f3 z)) for some reason - clear to me but not to the {} macro or
>> to the compiler....
>
> By the way you can actually do this with enough effort -- you just
> need a codewalker to lift out the {} forms, transforming
>
> (+  (f1 x)  {f2 y}  (f3 z))
>
> into
>
> (plet ((#:G413 (f2 y)))
>   (+  (f1 x) #:G413 (f3 z)))

I wrote that too soon -- of course the other forms should be lifted as
well, so the expansion would look like

(plet ((#:PARALLEL413 (f2 y)))
  (let ((#:SERIAL414 (f1 x))
        (#:SERIAL415 (f3 z)))
    (+ #:SERIAL414 #:PARALLEL413 #:SERIAL415)))

_______________________________________________
Lisp Hug - the mailing list for LispWorks users
lisp-hug@lispworks.com
http://www.lispworks.com/support/lisp-hug.html


Updated at: 2020-12-10 08:36 UTC