👋 I have a question on the more recent versions of core async (1.8.741), and the removal of the clojure.core.async.pool-size property. From looking at how impl/dispatch routes various workloads, it seems that go blocks run on the :io thread-pool which is an unbounded cached thread-pool. This is different from earlier versions of core async that ran on a fixed size thread pool configured with the aforementioned property. However, the doc string for go still refers to the risk of "depleting the fixed pool of go block threads, causing all go block processing to stop." Is the docstring out of date, or is my understanding of thread pool allocation incorrect? From earlier messages in this channel it seems that all thread-pools are indeed cached thread pools but I wanted to double check given the doc https://clojurians.slack.com/archives/C05423W6H/p1744001224550609?thread_ts=1743996641.717599&cid=C05423W6H
yes to most of that. because the pool can be changed by the new system property to custom executor, which could be limited, you could still deplete it (but that now won't happen by default)
Sweet! That makes sense 🙂
I think the idea now is to choose based on your "workflow type", so that the pools can be optimized based on them if needed. io-thread <-- For IO go <-- For async coordination thread <-- When you need a dedicated OS thread guaranteed There's nothing really dedicated for compute. Small computations can be done in go, longer computations could use thread or io-thread.
I guess my point is more that, I think core.async is missing a heavy/long running compute task. Something that could run on a bounded pool of real threads with an unbounded queue. There's no really appropriate choice when that's your workload.
What's wrong with clojure.core.async/pipeline ?
From the docs:
> Note this should be used for computational parallelism.
Since all of the channel ops can also work on real threads, it's also fairly straightforward to use one of the ExecutorServices offered by Executors.
longer computations should use thread, not io-thread
go and io-thread may map to the virtual thread pool when available, where extended computation is again an anti-pattern
@richhickey But cached thread pool is also an anti-pattern for longer compute. I don't know that it's much better than virtual threads, in fact I suspect it could be worse at scale, since if you don't queue up jobs you'd start having too many thread competing for compute and taking memory.
The issue with pipeline, is that it does not have a scheduler optimized for heavy compute, it uses the same thread pool as pipeline-blocking. You can control the parallelism level on the pipeline itself, but if you have many pipeline concurrently throughout your code, it becomes very confusing what's going on between them. Also, it's kind of higher level. Say I just want to fire of a long background compute job. I don't always need pipeline, it be nice to have a compute-thread, similar to io-thread.
We have ideas for things like future-io and future-compute in core. But I don't see the role for compute-thread in core.async, it's an IO library. Now c.a.flow is also a computation library and has a :compute workload and corresponding pool already.
I don't see the role for compute-thread in core.async, it's an IO libraryI guess if you see it as an IO library, but compute is often awaited by IO or waiting for IO. You could use future but the incompatibility between future and channels means you need to wrap them back and forth. Also sometimes you want a stream. Say you have a big inference job, like a CPU driven LLM, and you want to stream return the computation results. A channel would be nice for that.
Even nowadays, IO can be a bit of a misnomer, if you kick off a GPU workload, it's doing compute, but it's also async to the CPU, so you would not want to block your CPU.
That said I think for me, what I've observed is more that using core.async causes a confusion. I feel saying this is for async orchestration, this is for compute, and this is for blocking ops like IO, it gives a guidance in a way that makes using it then more straightforward.