It should be no surprise that arrays are faster than lists for parallel mapping. The open-coded versions of pmap and pmap-into, which are triggered when a single array is mapped to an array, are particularly fast in SBCL when the array types are declared or inferred. For the extreme case of a trivial inline function applied to a large array, the speed increase can be 20X or more relative to the non-open-coded counterparts.

Condition handling under the hood

To the user, a task is a function designator together with arguments to the function. However the internal representation of a task is like a generalization of a closure. A closure is a function which captures the lexical variables referenced inside it. Implementation-wise, a task is a closure which captures the task handlers present when the task is created. A closure bundles a lexical environment; a task additionally bundles a dynamic environment. This is the basic theory behind parallelized condition handling in lparallel.

Communicating via conditions

Because task handlers are called immediately when a condition is signaled inside a task, condition handling offers a way to communicate between tasks and the thread which created them. Here is a task which transfers data by signaling:

(defpackage :example (:use :cl :lparallel :lparallel.queue))
(in-package :example)

(define-condition more-data ()
  ((item :reader item :initarg :item)))

(let ((channel (make-channel))
      (data (make-queue)))
  (task-handler-bind ((more-data (lambda (c)
                                   (push-queue (item c) data))))
    (submit-task channel (lambda ()
                           (signal 'more-data :item 99))))
  (receive-result channel)
  (pop-queue data))

; => 99

receive-result has been placed outside of task-handler-bind to emphasize that handlers are bundled at the point of submit-task. (It doesn’t matter where receive-result is called.)

Multiple kernels

It may be advantageous to have a kernel devoted to particular kinds of tasks. For example one could have specialized channels and futures dedicated to IO.

(defvar *io-kernel* (make-kernel 16))

(defun make-io-channel ()
  (let ((*kernel* *io-kernel*))

(defmacro io-future (&body body)
  `(let ((*kernel* *io-kernel*))
     (future ,@body)))

Since a channel remembers its associated kernel, submit-task and receive-result do not depend upon the value of *kernel*. In the promise API, only future and speculate need *kernel*.

Posted in Uncategorized | Leave a comment

lparallel-1.5.0 released

  • pmap and pmap-into are now open-coded in the case of one vector being mapped to a vector — allows a large performance boost in some CL implementations (like SBCL) when array types are known
  • SBCL is now able to terminate when live kernels exist — previously, end-kernel needed to be called on all kernels before exiting (which is good practice but is no longer required)
  • added try-receive-result — non-blocking version of receive-result
Posted in Uncategorized | Leave a comment

lparallel-1.4.0 released

  • added function task-categories-running
  • new special variable *debug-tasks-p* — setting it to false will transfer errors instead of invoking the debugger inside tasks; default is true
  • added convenience function invoke-transfer-error for local control over debugging tasks:
    (task-handler-bind ((error #'invoke-transfer-error)) ...)
    (task-handler-bind ((error #'invoke-debugger)) ...)
Posted in Uncategorized | Leave a comment

lparallel-1.3.0 released

  • new support for fine-grained parallelism with `defpun’
  • new work-stealing model with lockless queues and optional spinning; enabled by default on SBCL, others default to central queue
  • added pfind, pcount, plet-if, pfuncall
  • fixed redundant restart in `chain’
  • `fulfill’ now accepts non-promises (never succeeds)
  • removed high optimizations exposed in some API functions
  • added shell script for unthrottling CPUs in Linux
  • renamed *kernel-task-category* -> *task-category*, *kernel-task-priority* -> *task-priority*, kernel-handler-bind -> task-handler-bind, preduce/partial -> preduce-partial; old names are still available
Posted in Uncategorized | Leave a comment

lparallel-1.2.0 released

  • added function cancel-timeout; submit-timeout now returns a timeout object
  • renamed emergency-kill-tasks to kill-tasks; old name is still available
  • minor optimization to ptrees
  • added type checks to psort arguments
  • switched test framework to eos
Posted in Uncategorized | Leave a comment

lparallel-1.1.0 released

  • added :wait option to end-kernel — blocks until the kernel has shut down
    (please read the documentation for end-kernel before using)
  • bound *print-circle* to t when printing a kernel — avoids SBCL + SLIME
    crash when evaluating the single form (setf *kernel* (make-kernel …))
Posted in Uncategorized | Leave a comment

lparallel is now part of Quicklisp

(ql:quickload :lparallel)
Posted in Uncategorized | Leave a comment