Limits on Parallel Tasks in Dyalog APL via .NET TPL Integration
0 reputation · 01 Mar 2024, 01:20 UTC
0 reputation · 01 Mar 2024, 01:20 UTC
Dyalog APL 19.0 and later expose a .NET interop layer that submits work to the .NET Task Parallel Library (TPL) for parallel execution.
While the engine relies on the .NET thread pool, the documentation does not state whether it caps the number of concurrent APL tasks to the number of logical processors or allows unlimited parallelism. Users have observed contention and memory spikes when launching many parallel functions, suggesting that the engine does not automatically throttle beyond the .NET pool limits. No Dyalog‑specific configuration option currently exists to adjust these limits.
35275 reputation · 01 Mar 2024, 08:49 UTC
Dyalog APL (19.0 and later) does not impose its own cap on parallel tasks. When you submit parallel work through the .NET interop layer, scheduling and concurrency limits come entirely from the .NET Task Parallel Library and its thread pool. There is no Dyalog-specific configuration option — documented or otherwise — to throttle task counts. That is the likely explanation for the contention and memory spikes you observed: the engine queues everything you give it and lets the CLR decide what runs when.
Two separate limits matter here, and conflating them causes confusion:
So the answer to "does it cap to logical processors" is: not exactly. Throughput is initially near the core count, but the pool grows beyond it under sustained load. The memory spikes you saw are consistent with many queued tasks each holding APL workspace state, not with an engine-level throttle being hit.
When queued work exceeds available threads, excess tasks just wait — there is no rejection, exception, or back-pressure signal back into APL. The practical failure modes are indirect: slow ramp-up (the pool's deliberate injection delay), memory growth from queued task state, and GC pressure. If tasks block on each other while holding pool threads, you can also hit a form of starvation where the pool grows to its maximum and throughput collapses.
Since Dyalog exposes no setting, adjust the pool from the .NET side before launching the workload. A small helper assembly works:
// C# helper, loaded via Dyalog .NET interop
public static class PoolConfig {
public static bool SetLimits(int maxWorker, int maxIo) {
return System.Threading.ThreadPool.SetMaxThreads(maxWorker, maxIo);
}
public static void GetLimits(out int maxWorker, out int maxIo) {
System.Threading.ThreadPool.GetMaxThreads(out maxWorker, out maxIo);
}
}Call SetLimits once at session start, before any parallel work is queued. Note that SetMaxThreads cannot go below the processor count, and it returns false on failure — check the return value.
Often the better fix is application-level throttling: use a SemaphoreSlim or ParallelOptions.MaxDegreeOfParallelism in the helper so only N APL callbacks run concurrently, regardless of pool size. This bounds memory per-task deterministically, which pool tuning alone does not.
ThreadPool.GetMaxThreads through interop and record them.Process.GetCurrentProcess().Threads.Count.SetMaxThreads (or a semaphore-based limiter), re-run, and confirm the thread ceiling and peak memory both change.This behavior depends on which CLR your Dyalog installation hosts (.NET Framework vs .NET Core/.NET 5+); pool defaults and injection rates differ between them. The exact interop mechanics of Dyalog 19.0's TPL submission are not fully documented publicly, so treat the helper-assembly approach as the reliable lever and verify on your specific version. If you can share which runtime Dyalog is loading (System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription), the pool-tuning advice can be made more precise.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.