git » just-lisp-things.git » commit aa13983

wip

author Alan Dipert
2019-12-12 20:34:26 UTC
committer Alan Dipert
2019-12-12 20:34:26 UTC
parent b42243321df949c15f249705987e2f5d76026669

wip

posts/collecting-macro-edition.md +55 -5

diff --git a/posts/collecting-macro-edition.md b/posts/collecting-macro-edition.md
index 313822e..bc3eb28 100644
--- a/posts/collecting-macro-edition.md
+++ b/posts/collecting-macro-edition.md
@@ -1,10 +1,10 @@
 ---
 date: "2019-12-12"
 keywords: ["LOOP", "collect", "lists", "macros"]
-title: "Collecting without LOOP: Funcall Begone"
+title: "Collecting without LOOP: Macro Edition"
 id: "urn:uuid:a2d4ab9a-1c99-11ea-a6e0-843a4b7c6000"
 abstract: |
-  Implementing COLLECTING with a macro interface instead of a functional interface.
+  Implementing COLLECTING with a macro interface instead of a function.
 ---
 
 In a [previous post](collecting.html), I professed my fondness for the
@@ -15,18 +15,68 @@ delivered as a function called `COLLECTOR`.
 `COLLECTOR` compared to `collect`: function call overhead.
 
 In this post, we'll survey a few tools Common Lisp provides that can
-be used to eliminate function call overhead, and end up with a new,
-[macro][macro]-based collecting contraption: `WITH-COLLECT`.
+be used to eliminate function call overhead, and use them to concoct a
+new, [macro][macro]-based collecting contraption: `WITH-COLLECT`.
 
 ## Wait, function calls are slow?
 
-Well, no. But there is some overhead associated with a function
+Well, no, but there is some overhead associated with a function
 call. Usually, at a minimum:
 
 1. The function reference must be resolved
 1. A stack frame must be allocated
 1. Arguments must be copied to the stack
 
+One way that a compiler can optimize away the overhead of a function
+call is by _inlining_ the function. That's the process of replacing
+the place where a function is called, the [call site][call-site], with
+the implementation of the function.
+
+Of course, functions are such a powerful tool that in most cases,
+their overhead is well worth it. We're interested in inlining here
+only because as library authors we want the collecting operation to be
+as fast as possible, and because the use-case we have in mind —
+collecting values in a single lexical block of code — doesn't
+strictly require an actual function to exist.
+
+Honestly, I'm not really used to thinking too much about function call
+overhead.[^r] Much of my professional programming experience has been
+with the JVM and JavaScript, two platforms with optimizing [tracing
+JIT][jit] compilers that do all kinds of optimization for you,
+including automatically inlining. As programs on these platforms run,
+they are continually analyzed and recompiled in order to run faster.
+
+
+## Inlining and CL
+
+Common Lisp was specified before tracing JITs were a thing, but it
+does have pervasive facilities for all kinds of _static_ optimization
+strategies, including inlining. Mechanisms supporting inlining
+directly or indirectly that I'm aware of include:
+
+* Macros ([`DEFMACRO`][defmacro], [`MACROLET`][macrolet])
+* Compiler macros ([`DEFINE-COMPILER-MACRO`][cmacro])
+* The [`INLINE`][inline] [declaration][declare]
+
+Here's an example showing the functional way, `COLLECTOR`, compared to
+what I have in mind with `WITH-COLLECT`. Both expressions evaluate to
+`(2 4 6 8 10)`:
+
+~~~{.lisp}
+;; with COLLECTOR from previous post
+(let ((collect (collector)))
+  (dotimes (i 10 (funcall collect))
+    (let ((i (+1 i)))
+      (when (evenp i)
+        (funcall collect i)))))
+      
+;; with WITH-COLLECT, which we haven't implemented yet
+(with-collect collect
+  (dotimes (i 10)
+    (let ((i (1+ i)))
+      (when (evenp i)
+        (collect i)))))
+~~~
 
 In this post, we'll address the function call overhead question with a
 new, [macro][macro]-based collecting contraption, `WITH-COLLECT`. But