-
-
Notifications
You must be signed in to change notification settings - Fork 752
Possibility of multithreading? #626
Description
With 2 of the big 3 Ruby implementations supporting a GIL-free threading model, I've been playing around with the idea of adding an option to separate spec examples across different threads for improved spec runtime.
Not everything about this will be perfect, which is why it should be an option and not enforced.
Won't reduce initial load time
AFAIK, requiring dependencies for the code under test should be sequential. I don't think requires are thread-safe.
Creating threads isn't free
Paying the cost of creating threads in very small apps/gems with short sequential spec runtimes may increase runtimes slightly. Additionally, Rubies with a GIL would pay this penalty without the ability to run specs concurrently at all.
Specs that depend on state outside of its scope get wrecked
A lot of Rails specs use something like config.before(:each) { DatabaseCleaner.clean }
. This will wreck any specs that hit the DB. The good news is that spreading out specs across threads encourages developers to write specs to be independent of outside state.
However, this could lead to false passes, as well, which can be even more problematic. The best thing here might just be to encourage developers who aren't confident that their specs are 100% independent of each other to refrain from using this feature.
But there is good here
The pro, obviously, is greatly reduced spec runtime when using JRuby or Rubinius on multi-core machines. This works out much better for larger apps with hundreds of specs. This may also encourage development on these particular Rubies.
Proposal
One route I thought about was providing an option to describe
and context
blocks, but that would get repetitive in the spec code and would be specific to the block, disregarding the Ruby implementation on which it's being run. For CI running against multiple Rubies, this could extend MRI runtimes.
If we provide a CLI option (such as -j
used by make
), developers can choose whether or not to use the feature. I'm not sure how this could be configured to be specific to the Ruby implementation for CI, but it seems like it'd be easier.
The idea was to use a thread pool and a queue — either queue up the worker threads, pop them off from the main thread and pass spec examples to them or queue up the examples and let the threads pop them off and execute them. I'll be honest, I haven't had a chance to look at the code in depth yet so I don't know whether either of these would be the best solution (or even a viable solution) or whether another might be better. I'd definitely like to work on this a bit once I've had a chance to familiarize myself with the code, but I wanted to see what y'all thought about it first.