Skip to content

Commit ea8b315

Browse files
authored
a few hacks that improve the build time (BinaryAnalysisPlatform#1028)
* a few hacks that improve the build time Building the whole BAP repository from scratch is now more than twice as fast and takes about 8 minutes or even less with faster CPUs and SSD drives. What is more important, rebuilding BAP now takes less than a minute even if something was changed deep in the BAP's core, like disassembler or even regular or bap-types. Below is the list of tricks that made this possible. 1) Disable -short-paths. This option was responsible for nearly x2 slowdown. Since nowadays we mostly do typechecking with merlin having short paths doesn't really matter anymore. So screw them. Short paths are also disable in bapbuild. 2) Disable cross-module optimizations. When cross-module optimizations are enabled (by default) every time a module changes all dependent modules are recompiled. This behavior sort of defeats the purpose of separate compilation and leads to 20 minutes of recompilation when some module in disassembler is changed. Now it won't happen and when any module is changed no other modules will be recompiled. If the interface is changed only the modules that import this interface will be recompiled. Theoretically, it may lead to less optimal builds. We didn't notice any slowdown yet, but we may consider putting this particular trick under a compilation flag and disable during releases. 3) Hotfix ocamlbuild linking rule. There is a bug in ocamlbuild that prevents caching from working properly. The rule for linking library mentions .so as its product, but it never builds it (and can't (and shouldn't)). As a result the caching system notices a missing object and triggers relinking of all libraries. Ideally, it should be fixed on the upstream (I will PR to them) but we don't want to wait for upsteam, especially since the fix is simple - we just override the rule by our own, that didn't mention any .so files. 4) Disables compression when plugins are packed. Plugins are zip files underneath the hood and when we pack and unpack them it takes time to compress them. It goes much faster (especially for custom llvm builds with debugging symbols) when the compression is disabled. This trick is also a candidate to be made optional. 5) Packs a plugin in one step. Instead of doing 4 packings/repackings we now do it only once. 6) Unlimits the number of jobs tried by ocamlbuild. This may break on Travis, but let's try. We all know that ocamlbuild is not very good in parallelizing tasks, but it tries its best. I didn't see more than 12 jobs at once, so it should be allright. * fixes an invalid depenency in the cmxa -> cmxs rule. It should depend on '.a' also, as `cmxa` is a collection of `cmx` so it doesn't change when cross-module optimizations are disabled, or when the set of imported modules is unchanged. * implements build caching for plugins Now all built plugins are cached inside the _build folder, instead of being rebuilt every time in a temporary folder. And md5sum of the transitive closure of plugin dependencies is used as the cache validator. If any dependency is changed, then the plugin is relinked and repacked. Additionally, the script builds only those plugins that were migrated into the build tree, so when we're installing via opam it doesn't try to rebuild already installed plugins. This is an extperimental patch, I'm concerned with its portability. We use md5sum utility, which is not present on MacOS X. However, there is md5 there, with a slightly different interface, which we're trying to use instead. Probably, we should rewrite this in OCaml instead, for better portability and performance. Finally, it introduces new system dependency, md5[sum], so our docker builds may fail. * adds the development mode to configure Adds a new variable called `development` to the configure script that is disabled by default. When enabled (with the `--enable-development` option) it will enable certain options and optimizations that are in general not suitable for the released version. Right now this option, when enabled, disables cross-module optimizations. We might later attach more build time optimizations and tweaks to that option. The cross-module optimizations, when disabled will significantly improve the rebuilding time (at no to neglible preformance degradation). Turning them off won't affect a lot, the initial build time so there is no need to disable them during the initial installation.
1 parent 7be422e commit ea8b315

File tree

8 files changed

+97
-54
lines changed

8 files changed

+97
-54
lines changed

Makefile

+11-5
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,23 @@ doc:
1515
all:
1616
$(SETUP) -all $(BAPALLFLAGS)
1717

18-
install:
19-
$(SETUP) -install $(BAPINSTALLFLAGS)
18+
.PHONY: plugins
19+
install-plugins:
2020
sh tools/build_plugins.sh
2121
if [ -f ./postinstall.native ]; then ./postinstall.native; fi
22+
if [ -f ./postinstall.byte ]; then ./postinstall.byte; fi
23+
24+
install-libs:
25+
$(SETUP) -install $(BAPINSTALLFLAGS)
26+
27+
reinstall-libs:
28+
$(SETUP) -reinstall $(BAPINSTALLFLAGS)
2229

2330
uninstall:
2431
$(SETUP) -uninstall $(BAPUNINSTALLFLAGS)
2532

26-
reinstall:
27-
make uninstall
28-
make install
33+
install: install-libs install-plugins
34+
reinstall: reinstall-libs install-plugins
2935

3036
clean:
3137
$(SETUP) -clean $(BAPCLEANFLAGS)

lib/bap_build/bap_build.ml

-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ module Plugin_rules = struct
1919
let default_tags = [
2020
"thread";
2121
"debug";
22-
"short_paths";
2322
"custom";
2423
"pp(ppx-jane -dump-ast -inline-test-drop)"
2524
] @ List.map default_predicates ~f:(sprintf "predicate(%s)")

lib/bap_bundle/bap_bundle.ml

+4-4
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ module Std = struct
126126
| Some man -> man
127127
| None -> Manifest.create "noname" in
128128
let mdata = Manifest.to_string man in
129-
Zip.add_entry mdata zip Nameof.manifest;
129+
Zip.add_entry ~level:0 mdata zip Nameof.manifest;
130130
Zip.close_out zip
131131
end
132132

@@ -194,9 +194,9 @@ module Std = struct
194194
let zout = Zip.open_out bundle.path in
195195
Hashtbl.iteri files ~f:(fun ~key:name ~data ->
196196
match data with
197-
| `Data s -> Zip.add_entry s zout name
198-
| `Copy f -> Zip.copy_file_to_entry f zout name
199-
| `Move f -> Zip.copy_file_to_entry f zout name;
197+
| `Data s -> Zip.add_entry ~level:0 s zout name
198+
| `Copy f -> Zip.copy_file_to_entry ~level:0 f zout name
199+
| `Move f -> Zip.copy_file_to_entry ~level:0 f zout name;
200200
Sys.remove f);
201201
Zip.close_out zout
202202

myocamlbuild.ml.in

+30
Original file line numberDiff line numberDiff line change
@@ -71,14 +71,44 @@ let predicate_used_libs = function
7171
(S [A "-predicates"; A ("used_"^lib)]))
7272
| _ -> ()
7373

74+
let disable_crossmodule_optimization = function
75+
| After_rules ->
76+
if bool_of_string @@ expand "$development"
77+
then flag ["ocaml"; "compile"] (A "-opaque")
78+
| _ -> ()
79+
7480

81+
let link_shared_native ~cmxa ~dst =
82+
Cmd (S [
83+
!Options.ocamlopt;
84+
A "-shared";
85+
A "-linkall";
86+
A "-I";
87+
A (Filename.dirname cmxa);
88+
P cmxa; A "-o"; Px dst])
89+
90+
let register_cmxs_of_cmxa_rule () =
91+
rule "bap: cmxa -> cmxs"
92+
~prods:["%.cmxs"]
93+
~deps:["%.cmxa"; "%.a"]
94+
(fun env _ ->
95+
link_shared_native
96+
~cmxa:(env "%.cmxa")
97+
~dst:(env "%.cmxs"))
98+
99+
let override_broken_cmxs_rule = function
100+
| Before_rules ->
101+
register_cmxs_of_cmxa_rule ()
102+
| _ -> ()
75103

76104
let () =
77105
mark_tags ();
78106
Ocamlbuild_plugin.dispatch (fun stage ->
79107
Rules.dispatch stage;
80108
dispatch_default stage;
109+
override_broken_cmxs_rule stage;
81110
pr_6184_hack stage;
82111
pass_pp_to_link stage;
83112
predicate_used_libs stage;
113+
disable_crossmodule_optimization stage;
84114
predicate_used_packages stage)

oasis/common

+5-3
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,7 @@ Plugins: META (0.4)
1111
AlphaFeatures: ocamlbuild_more_args, compiled_setup_ml
1212
BuildTools: ocamlbuild
1313
XOCamlbuildExtraArgs:
14-
-j 4
15-
-use-ocamlfind
16-
-classic-display
14+
-j 0
1715
-plugin-tags "'package(findlib)'"
1816

1917
PreConfCommand: $rm setup.data
@@ -22,3 +20,7 @@ PostDistcleanCommand: $rm _tags myocamlbuild.ml setup.ml setup.data
2220
Flag everything
2321
Description: Build every feature by default
2422
Default: false
23+
24+
Flag development
25+
Description: Enable development mode
26+
Default: false

oasis/common.tags.in

-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
# OASIS_START
22
# OASIS_STOP
3-
true: short_paths
4-
true: bin_annot
53
true: debug
64
true: warn(+a-4-6-7-9-27-29-32..42-44-45-48-50-60)

tools/bap_config.ab

+1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
PLUGINS_DIR="$plugindir"
22
DATA_DIR="$datadir/bap"
3+
OS="$system"

tools/build_plugins.sh

+46-39
Original file line numberDiff line numberDiff line change
@@ -4,54 +4,61 @@ set -ue
44

55
. $(dirname $0)/bap_config
66

7+
md5sum=md5sum
8+
9+
if [ $OS = "macosx" ]; then
10+
md5sum=md5
11+
fi
12+
13+
built_plugins=_build/built_plugins
14+
[ -d $built_plugins ] || mkdir $built_plugins
15+
16+
compute_digest() {
17+
plugin_lib=$1
18+
output=$2
19+
$md5sum `ocamlfind query -predicates byte -a-format -r $plugin_lib` > $output
20+
}
721

822
build_plugin() {
923
plugin=bap_plugin_$1
10-
TMPDIR=`mktemp -d`
11-
cd $TMPDIR
12-
touch $plugin.ml
13-
bapbuild -package bap-plugin-$1 $plugin.plugin
14-
DESC=`ocamlfind query -format "%D" bap-plugin-$1`
15-
CONS=`ocamlfind query -format "%(constraints)" bap-plugin-$1`
16-
TAGS=`ocamlfind query -format "%(tags)" bap-plugin-$1`
17-
if [ ! -z "$CONS" ]; then
18-
bapbundle update -cons "$CONS" $plugin.plugin
19-
fi
20-
if [ ! -z "$TAGS" ]; then
21-
bapbundle update -tags "$TAGS" $plugin.plugin
22-
fi
23-
bapbundle update -desc "$DESC" $plugin.plugin
24-
bapbundle update -name $1 $plugin.plugin
24+
plugin_lib=bap-plugin-$1
25+
BUILDIR=$built_plugins/$1
2526

26-
mv $plugin.plugin $1.plugin
27-
bapbundle install $1.plugin
28-
cd -
29-
rm -rf $TMPDIR
30-
}
27+
[ -d $BUILDIR ] || mkdir $BUILDIR
28+
cd $BUILDIR
29+
30+
[ -f digest ] || touch digest
3131

32-
cd plugins
32+
compute_digest $plugin_lib new_digest
3333

34-
for plugin in `ls`; do
35-
if ocamlfind query bap-plugin-$plugin 2>/dev/null
34+
if cmp -s digest new_digest
3635
then
37-
plugin_lib=`ocamlfind query bap-plugin-$plugin`
38-
if [ -f $PLUGINS_DIR/$plugin.plugin ]
39-
then
40-
existing_plugin=$PLUGINS_DIR/$plugin.plugin
41-
if [ $plugin_lib -nt $existing_plugin ]
42-
then
43-
build_plugin $plugin &
44-
else
45-
echo "Not rebuilding $plugin"
46-
fi
36+
echo "$1: cache hit"
37+
else
38+
echo "$1: cache miss"
39+
touch $plugin.ml
40+
bapbuild -clean
41+
bapbuild -package bap-plugin-$1 $plugin.plugin
42+
DESC=`ocamlfind query -format "%D" bap-plugin-$1`
43+
CONS=`ocamlfind query -format "%(constraints)" bap-plugin-$1`
44+
TAGS=`ocamlfind query -format "%(tags)" bap-plugin-$1`
45+
if [ -z $CONS ]; then
46+
bapbundle update -name $1 -desc "$DESC" -tags "core,$TAGS" $plugin.plugin
4747
else
48-
echo "Building $plugin as it wasn't built yet"
49-
build_plugin $plugin &
48+
bapbundle update -name $1 -desc "$DESC" -cons "$CONS" -tags "core,$TAGS" $plugin.plugin
5049
fi
51-
else
52-
echo "Not building $plugin as it wasn't selected"
50+
mv $plugin.plugin $1.plugin
5351
fi
54-
done
52+
bapbundle install $1.plugin
53+
mv new_digest digest
54+
}
5555

56-
cd ..
56+
57+
for plugin in `ls _build/plugins`; do
58+
(build_plugin $plugin)&
59+
echo `pwd`
60+
done
5761
wait
62+
63+
64+
echo "Finished updating plugins"

0 commit comments

Comments
 (0)