now that we are running executors inside executorsthere are four combinations of threads and process pools that we can be using to compress images they each have quite different timing profilesprocess pool per row process pool per image seconds thread pool per image seconds thread pool per row seconds seconds as we might expectusing threads for each image and again using threads for each row is the slowestsince the gil prevents us from doing any work in parallel given that we were slightly faster when using separate processes for each row when we were using single imageyou may be surprised to see that it is faster to use threadpool feature for rows if we are processing each image in separate process take some time to understand why this might be my machine contains only four processor cores each row in each image is being processed in separate poolwhich means that all those rows are competing for processing power when there is only one imagewe get (very modestspeedup by running each row in parallel howeverwhen we increase the number of images being processed at oncethe cost of passing all that row data into and out of subprocess is actively stealing processing time from each of the other images soif we can process each image on separate processorwhere the only thing that has to get pickled into the subprocess pipe is couple filenameswe get solid speedup thuswe see that different workloads require different concurrency paradigms even if we are just using futures we have to make informed decisions about what kind of executor to use also note that for typically-sized imagesthe program runs quickly enough that it really doesn' matter which concurrency structures we use in facteven if we didn' use any concurrency at allwe' probably end up with about the same user experience this problem could also have been solved using the threading and/or multiprocessing modules directlythough there would have been quite bit more boilerplate code to write you may be wondering whether or not asyncio would be useful here the answer is"probably notmost operating systems don' have good way to do non-blocking reads from the filesystemso the library ends up wrapping all the calls in futures anyway