summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMaxim Fedorov <dane@whatsapp.com>2019-03-28 20:08:13 -0700
committerMaxim Fedorov <dane@whatsapp.com>2019-03-28 20:08:13 -0700
commitae0af35e8c8707ea8e60467a422d815bd549b55b (patch)
tree2f05fb5f1d89935e85256ede5034f4dd6b057ca8
parentcc788f1ff3c2c845cda19f27291309effb94511a (diff)
Enable parallel build
Support for parallel compilation of *.erl file was dropped before 3.0 release. However, our tests for a project containing ~500 source files show substantial gain, lowering compilation time from 58 seconds to 18 on a MacBook Pro 15" (4 cores, 8 threads), and to just 10 seconds on Xeon-D machine.
-rw-r--r--src/rebar_compiler.erl56
1 files changed, 55 insertions, 1 deletions
diff --git a/src/rebar_compiler.erl b/src/rebar_compiler.erl
index 02c74db..b04c2c4 100644
--- a/src/rebar_compiler.erl
+++ b/src/rebar_compiler.erl
@@ -77,7 +77,7 @@ run(CompilerMod, AppInfo, Label) ->
true = digraph:delete(G),
compile_each(FirstFiles, FirstFileOpts, BaseOpts, Mappings, CompilerMod),
- compile_each(RestFiles, Opts, BaseOpts, Mappings, CompilerMod).
+ compile_parallel(RestFiles, Opts, BaseOpts, Mappings, CompilerMod).
compile_each([], _Opts, _Config, _Outs, _CompilerMod) ->
ok;
@@ -99,6 +99,60 @@ compile_each([Source | Rest], Opts, Config, Outs, CompilerMod) ->
end,
compile_each(Rest, Opts, Config, Outs, CompilerMod).
+compile_worker(QueuePid, Opts, Config, Outs, CompilerMod) ->
+ QueuePid ! self(),
+ receive
+ {compile, Source} ->
+ Result = CompilerMod:compile(Source, Outs, Config, Opts),
+ QueuePid ! {Result, Source},
+ compile_worker(QueuePid, Opts, Config, Outs, CompilerMod);
+ empty ->
+ ok
+ end.
+
+compile_parallel([], _Opts, _BaseOpts, _Mappings, _CompilerMod) ->
+ ok;
+compile_parallel(Targets, Opts, BaseOpts, Mappings, CompilerMod) ->
+ Self = self(),
+ F = fun() -> compile_worker(Self, Opts, BaseOpts, Mappings, CompilerMod) end,
+ Jobs = min(length(Targets), erlang:system_info(schedulers)),
+ ?DEBUG("Starting ~B compile worker(s)", [Jobs]),
+ Pids = [spawn_monitor(F) || _I <- lists:seq(1, Jobs)],
+ compile_queue(Targets, Pids, Opts, BaseOpts, Mappings, CompilerMod).
+
+compile_queue([], [], _Opts, _Config, _Outs, _CompilerMod) ->
+ ok;
+compile_queue(Targets, Pids, Opts, Config, Outs, CompilerMod) ->
+ receive
+ Worker when is_pid(Worker), Targets =:= [] ->
+ Worker ! empty,
+ compile_queue(Targets, Pids, Opts, Config, Outs, CompilerMod);
+ Worker when is_pid(Worker) ->
+ Worker ! {compile, hd(Targets)},
+ compile_queue(tl(Targets), Pids, Opts, Config, Outs, CompilerMod);
+ {ok, Source} ->
+ ?DEBUG("~sCompiled ~s", [rebar_utils:indent(1), filename:basename(Source)]),
+ compile_queue(Targets, Pids, Opts, Config, Outs, CompilerMod);
+ {{ok, Warnings}, Source} ->
+ report(Warnings),
+ ?DEBUG("~sCompiled ~s", [rebar_utils:indent(1), filename:basename(Source)]),
+ compile_queue(Targets, Pids, Opts, Config, Outs, CompilerMod);
+ {skipped, Source} ->
+ ?DEBUG("~sSkipped ~s", [rebar_utils:indent(1), filename:basename(Source)]),
+ compile_queue(Targets, Pids, Opts, Config, Outs, CompilerMod);
+ {Error, Source} ->
+ NewSource = format_error_source(Source, Config),
+ ?ERROR("Compiling ~ts failed", [NewSource]),
+ maybe_report(Error),
+ ?FAIL;
+ {'DOWN', Mref, _, Pid, normal} ->
+ Pids2 = lists:delete({Pid, Mref}, Pids),
+ compile_queue(Targets, Pids2, Opts, Config, Outs, CompilerMod);
+ {'DOWN', _Mref, _, _Pid, Info} ->
+ ?ERROR("Compilation failed: ~p", [Info]),
+ ?FAIL
+ end.
+
%% @doc remove compiled artifacts from an AppDir.
-spec clean([module()], rebar_app_info:t()) -> 'ok'.
clean(Compilers, AppInfo) ->