Concurrency/Speed of Supervisor spawning processes vs starting GenServers

I have a mathematical solving program and have tried implementing it in a few different ways. Both of my solutions have the exact same Supervisor parent that starts up children using worker() into the children array, then runs supervise() on them. However, my first solution’s children are implemented as simple process modules that have a start_link() method that calls spawn_link(), while my second solution’s children are implemented as GenServer modules that have their start_link() method call GenServer.start_link().

The main application code and Supervisor are identical between both versions - the only different is the child implementation. However, run times show that the process implementation is making use of my dual core machine (CPU time is 2x the real time) while the GenServer implementation is not (CPU time is just over 1x the real time). What gives, am I using GenServer wrong?

I can post my exact code if needed

Thanks

Yes please.

That function is deprecated for years now. Elixir 1.6 deprecated it as far as I remember. You should use child specs instead.

2 Likes

You’re doing way too much work in the init function - this function is blocking when the module is launched as a process via start_link, so you want init to be as fast and light as possible. Take a look at the handle_continue callback to achieve a dramatic improvement while still keeping the code easy to reason about.

6 Likes

GenServer.start_link can’t return before Child.init completes, because it needs its return value. See the doc here.

For one-off tasks the Task module would be a better fit than GenServer, but just for fun, you can make the computation happen in parallel with the :continue / handle_continue feature from the doc above.

4 Likes

Thank you both, handle_continue gave me similar concurrency to when I used processes! I agree that Task would likely fit this use case better, but I’m glad to have now learned about handle_continue