Indeed. I remember being shocked when I first read "Thousands of Threads and Blocking I/O" [0] by Paul Tyma.
After reading the C10k problem [1], I just believed that a big pile of threads would never work and never updated my beliefs. While such an approach is still quite memory limited compared to asynchronous approaches, it scales quite far and can have a very high work rate.
on other hand we should talk about C100k or C1m problems.
This is why actor models are good IMO. They are based on the actual resource model, rather than the developer's mental model. That is, until the developer learns to change his/her mental model.
Async is generally associated with lower latency.
Can you expound?
In the threaded code, you need to get a window of time and memory. In my Java, I have a max heap size of 8gb and I can maybe create up to 8k threads. When making 20000 DB queries only 8k is being executed where most of these are waiting for the query to complete or CPU time.
I know that you can have millions of M:N/green threads but these are different. In any case, you need to compete for memory and CPU time to execute.
Since not many people are programming in Haskell and BEAM languages my simplifications still hold.
public class Test {
volatile static boolean stop = false;
static AtomicInteger counter = new AtomicInteger();
public static void main(String[] args) throws InterruptedException {
Thread[] threads = new Thread[100000];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(() -> {
counter.incrementAndGet();
while (!stop) {
try {
Thread.sleep(1000);
} catch (InterruptedException ignored) {
}
}
});
}
for (Thread thread : threads) {
thread.start();
}
while (counter.get() < threads.length) {
Thread.sleep(1000);
}
System.out.println("Threads are running");
Thread.sleep(10000);
stop = true;
for (Thread thread : threads) {
thread.join();
}
}
}Isn't that vmem? Unless it also writes to the entirety of that 1MB before starting the thread, it's not actually committed by default on most unices.
To get a bit pedantic, synchronization introduces overhead. If you've got an 8-core processor running things async/parallel may complete faster since you aren't pinned to one core.
That said, it's much harder to get right since you have to deal not only with thread sync overhead but also cache/pipeline sharing and a bunch of other gnarly things.
Your asynchronous system can be descheduled by the OS on top of the stuff it does on its own if it runs on a preemptive multitasking OS or hypervisor, which more or less all of them do.
> And it cannot ever be faster than asynchronous, because asynchronous doesn't yield.
The asynchronous system yields doubly, it yields to its own userland scheduler/event loop and it's de-scheduled by the preemptive multitasking OS it runs on top of.
Yielding is a thing from the multithreaded world with synchronous APIs. Asynchronous systems don't yield, they are programmed asynchronously. They have no yielding overhead and can never be slower than yielding threads.
If you are arguing for 1:1 kernel threading against M:N threading (coroutines, goroutines, green threads) that's fine, but multithreading implementations have absolutely nothing to do with asynchronous systems.
https://en.wikipedia.org/wiki/Asynchrony_(computer_programmi...
> If it’s possible to build more reliable systems with programming languages with stricter constraints, what about languages with the strongest constraints? I’ve skewed all the way to the far end of the spectrum and have been building a web service in Rust
Bit overselling it, that introduction would work better for something like ATS or possibly Idris. Rust has its strictness, but it's not exactly "you must prove this recursion terminates"-strict
> Synchronous operations aren’t as fast as a purely asynchronous approach
This is misleading. Synchronous sequences are generally faster than asynchronous ones because yielding and getting scheduled back has a non-zero overhead (this is regularly re-discovered, most recently by the developers of "better-sqlite": https://news.ycombinator.com/item?id=16616374).
Async operations are a way to increase concurrency when your synchronous sequence would just be waiting with its thumb up its ass (e.g. on a network buffer filling or on a file read completing), so you can provide more service over the same wallclock time by (hopefully) always using CPU time.