Eric Schneider wrote:
>
> Out of curiousity,why wouldnt one use the faster function? More overhead?
> More complex code?
> --
> Eric S. eschneider@jensenprecast.com
You can in some cases, make a case for using a slower
version of a function, for the sake of more readable
and maintainable code, but I don't think that in this
case it would be valid, and I am not suggestiong that
you should not use the faster one. By all means, use
Jadranko's version. However, in any context where its
use is what I consider valid, there is little-to-no
performance benefit.
Neiether version of the function posted is appropriate
for use in a scenario where the relative effeciency of
either would have any perceptible difference, because
neither the slow or the faster version of this function
is an effecient approach in a scenario where it is being
used iteratively (the only scenario where effeciency is
meaningful).
For example:
(setq list-of-lines
'(( )...) ;; A list of many lines
)
;; Process each line against a single distance,
;; and get a list of points (one for each line)
;; back:
(mapcar
'(lambda (line)
(point-on-line (car line) (cadr line) dist)
)
lines
)
That was an example of using (point-on-line)
iteratively, where the distance is a constant.
Here is another example of using (point-on-line)
iteratively, where the line is a constant, and
there are many distances:
(setq distances '( ...))
(setq p1 (Getpoint....)
(setq p2 (getpoint...)
(mapcar
'(lambda (distance)
(point-on-line p1 p2 distance)
)
list-of-distances
)
Both examples are ineffecient ways to go about using
the code in (point-on-line) iteratively, because of
the overhead of having to make a seperate function
call (to point-on-line) for each iteration.
Reason: Making calls to a user-defined function has
a fixed amount of overhead associated with it, and
in this case, it can be completely avoided by not
making numerous calls to a version of (point-on-line)
that's designed to handle only one point on one line.
Instead, you should encapsulate the iteration into
another version of (point-on-line) that takes either
many lines, or many distances, and does not have to
call another user-defined function once for each item
it processes, in order to get the job done.
The following should demonstrate my point, and I hope,
and put an end to this.
;=== POLTEST.LSP =====================================
;; Original point-on-line function (Tony):
(defun point-on-line (p1 p2 dist)
(setq sc (/ dist (distance p1 p2)))
(mapcar '+ p1
(mapcar '*
(mapcar '- p2 p1)
(list sc sc sc)
)
)
)
;; Faster point-on-line function (Jadranko):
(defun point-on-line2 (p1 p2 dist / sc)
(setq sc (/ dist (distance p1 p2)))
(mapcar '(lambda (p1 p2) (+ p1 (* sc (- p2 p1)))) p1 p2)
)
;; A third version of (point-on-line) that
;; is specifically designed and optimized
;; for iterative use (multiple points on a
;; single line):
(defun points-on-line (p1 p2 offsets / delta x y z dx dy dz)
(setq delta (mapcar '- p2 p1))
(setq len (distance p1 p2))
(setq dx (car delta) dy (cadr delta) dz (caddr delta))
(setq x (car p1) y (cadr p1) z (caddr p1))
(mapcar
'(lambda (dist / sc)
(setq sc (/ dist len))
(list (+ x (* dx sc))
(+ y (* dy sc))
(+ z (* dz sc))
)
)
offsets
)
)
;; =================================================
;;
;; The following test code builds a list of
;; 200 doubles, and then uses each of the above
;; three functions to obtain a list of 200 points
;; that lie on a single line, at the 200 specified
;; distances from the start of the line.
;;
;; The first two test wrappers call each of
;; the two original (point-on-line) functions
;; 200 times using (mapcar).
;;
;; The third wrapper calls (points-on-line) once,
;; since it need be called only once to return the
;; same result as the 200 iterative calls to each
;; of the two versions of (point-on-line).
;;
;; All of the test wrappers repeat the operation
;; 100 times, to scale up the relative difference
;; of the results.
;;
;; The purpose of this is to demonstrate that
;; a function designed to perform a given process
;; on a single item, is not necessarily the most
;; effecient approach when that same process must
;; be iteratively applied to many items.
;; Test wrappers for the above 3 functions:
(defun points-on-line-1 ( / rslt)
(setq rslt
(time
'(repeat 100
(mapcar
'(lambda (dist)
(point-on-line p1 p2 dist)
)
offsets
)
)
)
)
(princ
(strcat
"\nPoints-on-line-1 (Tony): "
(rtos rslt 2 4)))
)
(defun points-on-line-2 ( / rslt)
(setq rslt
(time
'(repeat 100
(mapcar
'(lambda (dist)
(point-on-line2 p1 p2 dist)
)
offsets
)
)
)
)
(princ
(strcat
"\nPoints-on-line-2 (Jadranko): "
(rtos rslt 2 4)))
)
(defun points-on-line-3 ( / rslt)
(setq rslt
(time
'(repeat 100
(points-on-line p1 p2 offsets)
)
)
)
(princ
(strcat
"\nPoints-on-line-3 (optimized): "
(rtos rslt 2 4)))
)
(defun time (expr)
(/ (abs (- (getvar "millisecs")
(progn (eval expr) (getvar "millisecs"))
)
)
1000.0
)
)
(defun C:TEST ( / offsets i)
(setq i 1.0)
(repeat 200
(setq offsets (cons (setq i (1+ i)) offsets))
)
(setq p1 '(5.0 10.0 12.0) p2 '(500.0 400.0 200.0))
(points-on-line-1)
(points-on-line-2)
(points-on-line-3)
(princ)
)
;; --------------------------------------------------------
My results are as follows:
Command: TEST
Points-on-line-1 (Tony): 2.3530
Points-on-line-2 (Jadranko): 1.3420
Points-on-line-3 (optimized): 0.4110
If there is a lesson here, it is that taking a 'library'
of 'reusable' functions, that attempt to shrink-wrap or
encapsulate various discrete tasks, and simply using them
blindly as-is, can ultimately lead to inferior code, in
terms of effeciency and performance.
Checkout the AcadX(tm) ActiveX Extension Library at:
http://www.caddzone.com/acadx/acadx.htm
/*********************************************************/
/* Tony Tanzillo Design Automation Consulting */
/* Programming & Customization for AutoCAD & Compatibles */
/* ----------------------------------------------------- */
/* tony.tanzillo@worldnet.att.net */
/* http://www.caddzone.com */
/*********************************************************/