So you’ve put in the time and effort to refactor your code and data for parallel execution and are eager to see some parallel action. Unfortunately, you might be disappointed; in some cases your tasks might get serialized, performed sequentially in a single thread. With the overhead you’ve just introduced, your code probably performs a little bit worse than before.
If you thought TParallel.Join was a nasty bug, things can apparently get even worse. Reading further in Primož Gabrijelčič‘s book Delphi High Performance, Chapter 7, “Exploring Parallel Practices”:
There’s a nasty bug in the System.Threading code that was introduced in Delphi 10.2 Tokyo. I certainly hope that it will be fixed in the next release, as it makes the Parallel Programming Library hard to use. It sometimes causes new threads not to be created when you start a task. That forces your tasks to execute one by one, not in parallel.
(Interestingly, I’m able to reproduce it reliably in XE7, too.)
The ParallelTasks sample project demonstrates the issue.
If you run the “Check primes 2” code with four tasks first, then two tasks, and finally one task, you’ll get the expected result:
However, running the code with one task first, then two tasks, and finally four tasks will give you this:
The author offers two different workarounds:
– insert a little delay after each call to TTask.Run
(in the sample code, uncomment the Sleep(1)
call in btnCheckPrimes2Click method), or
– create your own thread pool and limit the minimum number of running threads in it (in the sample code, see btnCustomThreadPoolClick method).