Speed Up Data-Parallel APL with Dyalog Isolates
Run independent APL workers in parallel with Dyalog isolates, avoiding shared-state bugs at the cost of copying data.
19 Sept 2025, 03:49 UTC

The problem: one core, one APL thread
APL is powerful for array work, but the classic interpreter executes most user code on a single thread. When your program spends seconds inside a loop over a large matrix, the other cores on your laptop sit idle. Rewriting that loop as parallel code is risky in many languages because threads share memory and can corrupt each other's state.
Dyalog isolates offer a different model: each isolate is an independent APL worker with its own workspace. Workers do not share variables. Instead, the main thread copies data into an isolate, the isolate computes, and copies the result back. This message-passing style rules out entire classes of shared-state bugs, at the cost of copying data.
Worked example: parallel sum of squares
Suppose you have a large numeric vector and want the sum of its squares. A sequential version is short:
seqResult ← +/ bigVec × bigVec
To parallelize, split the vector into chunks, send each chunk to a separate isolate, collect the partial sums, and add them. Run this in a Dyalog 18.2 or later session. No special operating-system permissions are required.
⍝ Define the work each isolate performs.
chunkSumSquares ← {+/,⍵×⍵}
⍝ Split bigVec into chunks. Placeholder: adjust chunkSize for your data.
chunkSize ← 1000000
chunks ← ... ⍝ partition bigVec into vectors of length chunkSize
⍝ Create one isolate per chunk. Exact syntax varies by version; check the
⍝ Dyalog Language Reference for ⎕NEW ⎕ISO, ⎕EXPORT, and ⎕IMPORT.
isolates ← ⎕NEW ⎕ISO (⍴chunks)
:For i :In ⍳⍴isolates
isolates[i] ⎕EXPORT 'chunkSumSquares'
isolates[i] ⎕EXPORT ('chunk', chunks[i])
:EndFor
⍝ Collect partial results. The import call is isolate-specific.
partials ← ⎕IMPORT ¨ isolates ⍝ conceptual placeholder
parResult ← +/ partials
Where to run: the Dyalog session. Required permissions: none beyond normal Dyalog execution. Meaningful placeholders: bigVec is your input vector; chunkSize controls how much data each isolate receives. Expected check: time the sequential and parallel versions with ⎕TK or ⎕AI on a multi-core machine. Risk: each isolate receives a copy of its chunk, so memory use rises roughly with the number of isolates.
Trade-offs and limitations
- Copy overhead. Arguments and results are copied, not shared. Sending many tiny chunks can be slower than the sequential loop because copying dominates computation.
- No shared globals. Isolates cannot see
⎕NLor⎕CSvariables from the parent unless you explicitly export them. Existing code that relies on shared state must be refactored to pass everything as arguments. - Local error handling. An unhandled error inside an isolate terminates that isolate and surfaces as a nested error object in the caller. Wrap isolate work in local
:Try/:Catchwhere appropriate. - Memory. Each isolate runs in its own workspace. On a machine with limited RAM, too many isolates can force swapping.
How to verify and choose chunk size
Start by measuring the sequential baseline:
start ← ⎕TK
seqResult ← +/ bigVec × bigVec
seqTime ← ⎕TK - start
Then run the isolate version with several chunk sizes and compare. If the parallel version is slower, the chunks are probably too small and copy cost is dominating. Increase chunk size until computation time exceeds copy time. Confirm memory behavior with ⎕WAIT or your operating system's task manager; each isolate should appear as a separate workspace.
There is no rollback step because isolates are ephemeral workers, not persistent system changes. When you are done, let the isolates go out of scope or close the Dyalog session.
Closing
Isolates are a good fit for data-parallel APL when the work per chunk is large enough to amortize copying. Profile first, pick chunk sizes that keep each isolate busy, and wrap the pattern in a reusable operator so everyday code stays readable.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.