This is the third article of a series about BeagleBoneBlack by Šikula Robotik association. In last post, we set up network connections. In this article we will discuss how to compile for BeagleBoneBlack by three different ways: compilation on board, cross-compilation and distributed compilation.
The easiest way to compile a program is certainly to launch the compilation on the same machine as the result will be deployed. The BBB has enough power to compile small programs. In fact, with a lot of patience, it can also compile big ones (days may be requiered).
To begin, we install “gcc” and “make” programs suites (“pacman -S gcc make”). The first one is the GNU Compiler Collection in its ARMv7 declination. Complete name (in arch-vendor-os-abi-gcc-version convention) of this compiler is actuallyarmv7l-unknown-linux-gnueabihf-gcc-4.7.2. In fact, with its dependancies, it provides several compilers (for c, c99, c++, etc...), a linker, some programs to manipulate binary objects, and a standard library. The second packet is for the GNU Make tool. It helps to control the generation of executables.
We create a very simple project with only one C++ file nammed hello.cpp:
#include <iostream> int main() { std::cout << “Hello world” << std::endl; return 0; }
Because make has implicit rules, we can simply type “make hello”. Make will try to build hello, but since no rule is explicitely set in Makefile, it will find in its implicit rules that “a target with only one .cpp file which have the same name as the target” can be built directly. Hello program is built and can be executed (“./hello”).
As you can see, all behaves exactly like on a traditionnal workstation. However, one major difference is that the compilation is pretty slow, due to the low computing power of the target. Here comes cross-compilation
Because the target has quite low computing power, it would be nice to be able to compile on a another computer that have much more resources. This is called cross-compilation, because we compile on a computer of a different achitecture than the target.
The compilation tools are a bundle of compilers, binaries utilities and libc. In the case of ArchLinuxARM, this suite uses gcc 4.7.2, binutils 2.23.1 and glic 2.17 which itself is built to interact with kernels 3.6.3 and newer. A configuration file is available for crosstool-ng if you want to build the cross-compiler yourself.
For x86_64 Linux users, precompiled archive is available and we will use this one in this post. Download the archive labelled ARMv7l with hard float on ArchLinuxARM website, and simply unpack it (“tar xf x-tools7h.tar.xz”). All we need is now contained in the created directory.
Let's try to compile the same hello.cpp as before, but this time we cross-compile it simply by specifying the cross-compiler path.
CXX=/opt/x-tools7h/arm-unknown-linux-gnueabi/bin/armv7l-unknown-linux-gnueabihf-g++ make hello
All works well. We can check the output executable (“file hello”).
hello: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.27, not stripped
It is an executable for ARM Linux ! By copying it to the target (“scp hello user@192.168.2.1:hello”) and executing it we see that all is right.
On bigger projects, you have to replace each call to g++, gcc, etc... by the cross-compiler equivalents. Check the documentation of your tools to see if there is an easy way to do it. When you write your own Makefile, remember to add something like the following to handle both native and cross compilations. And of course use those variables in your rules.
AR=$(CROSS_COMPILE)ar AS=$(CROSS_COMPILE)as CC=$(CROSS_COMPILE)cc CXX=$(CROSS_COMPILE)g++
With a such Makefile, you can cross-compile with “CROSS_COMPILE=/opt/x-tools7h/arm-unknown-linux-gnueabi/bin/armv7l-unknown-linux-gnueabihf- make”.
Distributed compilation is a mix of the two above solutions. The idea is to compile with the help of other computers. For the current post, I will use the BBB as the computer that wants help and my 8-cores laptop as the helper. You can imagine more sophisticated setups with several workstations as helpers or even a full cross-compilation grid if you need.
Notice that having 242 cores will not reduce magically the compilation time to nothing. There is actually an overhead introduced by the distribution mechanism, some tasks are non-compressible and finally some tasks have to be executed in sequence.
The distribution is managed by distcc, so we have to install it (“pacman -S distcc”). The same operation has to be done on the helper machines (actually we want distccd on helpers, which is probably in the same package... see your distribution's documentation).
As the method to launch the server is probably different from distribution to distribution, I present here the “manual with only one line in console” launching way. Again, RTFM read your distribution's documentation if you want something more easy to use.
DISTCCD_PATH="/opt/x-tools7h/arm-unknown-linux-gnueabi/bin/" distccd -j 9 -a 192.168.2.1 --no-detach –daemon
In the command line, I chose to allow to launch up to 9 threads (I use the rule of thumb N cores + 1) and gave the list of allowed IPs (here ony the BBB). Any kind of accessible IP is acceptable, either with the network set up in last post, as done here, or with a local network through Ethernet or WiFi. Of course I also gave the compiler path.
On the BBB, we can set the list of possible helpers in distcc configuration file or pass it via the environment variable DISTCC_HOSTS. Because the IPs are susceptible to change quite often, the second version is presented here. A mix can be made, defining default hosts in configuration file and changing it by environment when needed. If there are several helpers, separate IPs by spaces. Notice you can use localhost too.
DISTCC_HOSTS="192.168.2.10" CXX="distcc armv7l-unknown-linux-gnueabihf-g++" make -j3
Notice that the compiler is now “distcc armv7l-unknown-linux-gnueabihf-g++” (with distcc prefix command, do not forget the space), and that I chose to compile 3 files at the time.
It is interesting to compare the different methods of compilation. For this purpose, I chose a real small project of 9 C++ files.
This project was compiled on board with one and two threads. It was also cross-compiled with one, two and nine threads, and compiled by distributed compilation with one, two and nine threads.
Compilation time with different methods. Lower is better.
Name | Type | Threads | Time | Improvement |
---|---|---|---|---|
Make | Compilation on board | 1 | 20.634s | Reference |
Make -j2 | Compilation on board | 2 | 22.394s (109%) | ÷1.08 |
Distcc make | Distributed compilation | 1 | 6.593s (32%) | ×3.13 |
Distcc make -j2 | Distributed compilation | 2 | 4.794s (23%) | ×4.30 |
Distcc make -j9 | Distributed compilation | 9 | 5.528s (27%) | ×3.73 |
Cross make | Cross compilation | 1 | 2.493s (12%) | ×8.28 |
Cross make -j2 | Cross compilation | 2 | 1.459s (7%) | ×13.8 |
Cross make -j9 | Cross compilation | 9 | 0.737s (4%) | ×28 |
Of course, cross-compilation is the quickest process. With no overhead and 8 cores clocked at 2.2Ghz the project compiles 28 times faster than on BBB. Notice that the files were stored in the laptop RAM which is way faster than the SD card.
When the project to compile uses external libraries, the cross-compilation is a little harder. We have to keep a copy of the target's filesystem up to date on all the hosts susceptible to cross compile so that all the needed files are available and so on. In this case, distributed compilation can be an acceptable compromise.
In the distributed solution, the project is compiled 4 times faster. With too much threads, the BBB is not able to treate all the quick answers and a little reduction is observed.
At the end, when no helper computer is available, compiling on board can be a fallback solution. After all, when the project is well designed, it is rare to have to recompile all when a modification is done.
This is the end of the third post. Next time we will talk about the related topic of debugging.