I tried out a few options for compiling on low memory systems and collected data on build time and memory usage.
The takeaways first (but please review the data and provide comment):
Issue #6658 from September identified the memory requirements of the build as a problem. The concern was around 1 GB, but this data shows that this can be comfortably mitigated with gcc options or by using clang. This also holds for systems with 512 MB memory plus 512 MB swap. Given this, issue 6658 can be closed.
Building with 512 MB and no swap (think Raspberry Pi 1 and Zero) is possible, but there isn't a lot of headroom. If the build becomes more memory hungry, it will push this category of users to a different option for getting binaries. Since this is future-looking, this may not warrant this issue remaining open, but it is worth discussing and monitoring going forward.
The '--disable-tests' option doesn't reduce memory use.
Trending Importance (Sept. 2015 to Feb. 2016)
The recent new features for pruning, mempool limiting and libsecp256k1 have made bitcoind on low-end hardware more practical. Also very inexpensive single-board computers with 512 MB RAM are becoming widely available, so we can anticipate the number of node-running systems in this category to be growing.
It is desirable to preserve compile-from-source as an option running node with a decent security/convenience trade-off.
Test Configurations
512 MB to 1 GB is assumed as the window of interest. The three system configurations tested are:
A Raspberry Pi 1 Model B - 512MB RAM, no disk swap, single-core 700Mhz ARM, Class 10 SD card for the filesystem, Raspbian Jessie Lite, running headless. gcc v4.9.2, clang 3.5.0-10+rpi1
A Raspberry Pi 2 Model B - 1GB RAM, no disk swap, quad-core 900Mhz ARM, Class 10 SD card for the filesystem, Raspbian Jessie, running an idle desktop environment. gcc version 4.9.2, clang version 3.5.0-10+rpi1
A VirtualBox VM, 512MB RAM, 575MB swap, assigned one core of a AMD 1650Mhz E-450 CPU, virtual drive backed by 5400 RPM 3.5" desktop SATA drive for swap and filesystem, Debian Jessie amd64, running headless. gcc version 4.9.2, clang version 3.5.0-10
Test Method
- All builds are on the branch v0.12.0rc2
- All building is done with a single process to use a single core.
- The GUI wallet is not part of the build
- The build is with the stock distro libdb-dev and libdb++-dev, which requires --with-incompatible-bdb flag for './configure'
- The elapsed time for 'make' is measured with the console 'time' utility, for real time, user CPU time and system CPU time.
- Fields marked FAIL are for when the build was not successful under the parameters of the trial.
The measurements for memory used is obtained by polling the /proc file for the complier pid and recording the VmPeak value like so:
$ for((i=0;;++i)) { echo $i ` grep VmPeak /proc/\`pidof cc1plus\`/status
| grep -o '[0-9]*'`; sleep 1 || break; } > VmPeak.txt
For clang, 'clang' is substituted for 'cc1plus':
$ for((i=0;;++i)) { echo $i ` grep VmPeak /proc/\`pidof clang\`/status
| grep -o '[0-9]*'`; sleep 1 || break; } > VmPeak.txt
The highest value is then extracted like so:
$ cat VmPeak.txt |awk '{print $2}' |sort -g |uniq -c |tail -n 1
Trial 1.A - GCC without tuning
$ ./configure --with-incompatible-bdb
| Time | Pi 1 | Pi 2 | VM |
|---|---|---|---|
| real | FAIL | FAIL | FAIL |
| user | FAIL | FAIL | FAIL |
| sys | FAIL | FAIL | FAIL |
| VmPeak | 503932 kB | 734708 kB | 1037012 kB |
Pi 1 died with error:
virtual memory exhausted: Cannot allocate memory
Pi 2 and the VM died with error:
g++: internal compiler error: Killed (program cc1plus)
This is Matt's original 6658 error and it happened as memory usage approached 100%.
Trial 1.B - GCC without tuning, tests disabled
$ ./configure --with-incompatible-bdb --disable-tests
| Time | Pi 1 | Pi 2 | VM |
|---|---|---|---|
| real | FAIL | FAIL | FAIL |
| user | FAIL | FAIL | FAIL |
| sys | FAIL | FAIL | FAIL |
| VmPeak | 503924 kB | 725432 kB | 1031176 kB |
Same result as 1.A
Pi 1 died with error:
virtual memory exhausted: Cannot allocate memory
Pi 2 and the VM died with error
g++: internal compiler error: Killed (program cc1plus)
Trial 2.A - Clang
$ ./configure --with-incompatible-bdb CC=clang CXX=clang++
| Time | Pi 1 | Pi 2 | VM |
|---|---|---|---|
| real | FAIL | 87m40.539s | 62m41.597s |
| user | FAIL | 81m21.470s | 47m20.512s |
| sys | FAIL | 2m4.330s | 2m59.824s |
| VmPeak | 529644 kB | 638112 kB | 990080 kB |
Pi 1 died with error:
clang: error: unable to execute command: Segmentation fault
This was when memory use was at 100%.
Trial 2.B - Clang, tests disabled
$ ./configure --with-incompatible-bdb --disable-tests CC=clang CXX=clang++
| Time | Pi 1 | Pi 2 | VM |
|---|---|---|---|
| real | FAIL | 53m34.138s | 43m26.447s |
| user | FAIL | 49m36.690s | 30m13.548s |
| sys | FAIL | 1m14.260s | 2m9.580s |
| VmPeak | 529644 kB | 638352 kB | 990092 kB |
Same result as 2.A
Pi 1 died with error:
clang: error: unable to execute command: Segmentation fault
Trial 3.A - GCC tuned with recommended options
$ ./configure --with-incompatible-bdb CXXFLAGS=" --param ggc-min-expand=1 --param ggc-min-heapsize=32768"
| Time | Pi 1 | Pi 2 | VM |
|---|---|---|---|
| real | 591m47.976s | 183m6.240s | 103m9.526s |
| user | 500m14.560s | 177m33.270s | 98m38.500s |
| sys | 16m19.190s | 3m11.130s | 2m34.136s |
| VmPeak | 426956 kB | 426960 kB | 627860 kB |
Trial 3.B - GCC tuned with recommended options, tests disabled
$ ./configure --with-incompatible-bdb --disable-tests CXXFLAGS=" --param ggc-min-expand=1 --param ggc-min-heapsize=32768"
| Time | Pi 1 | Pi 2 | VM |
|---|---|---|---|
| real | 334m52.635s | 104m2.129s | 70m11.030s |
| user | 281m42.100s | 99m40.200s | 63m27.220s |
| sys | 9m14.980s | 1m52.270s | 1m59.780s |
| VmPeak | 426960 kB | 426956 kB | 627872 kB |
Trial 4 - GCC tuned with aggressive ggc-min-heapsize
$ ./configure --with-incompatible-bdb CXXFLAGS=" --param ggc-min-expand=1 --param ggc-min-heapsize=4096"
| Time | Pi 1 | Pi 2 | VM |
|---|---|---|---|
| real | 626m17.587s | 196m14.033s | 112m59.351s |
| user | 529m31.800s | 190m44.230s | 104m0.372s |
| sys | 16m42.750s | 3m10.960s | 2m45.296s |
| VmPeak | 426956 kB | 426960 kB | 627868 kB |
Trial 5 - GCC tuned with aggressive ggc-min-expand
$ ./configure --with-incompatible-bdb CXXFLAGS=" --param ggc-min-expand=0 --param ggc-min-heapsize=32768"
| Time | Pi 1 | Pi 2 | VM |
|---|---|---|---|
| real | FAIL | FAIL | FAIL |
| user | FAIL | FAIL | FAIL |
| sys | FAIL | FAIL | FAIL |
| VmPeak | FAIL | FAIL | FAIL |
This also makes the compiler EXTREMELY slow. I let it run for a day and it barely got anywhere - probably less than 10% of the way through. It will probably complete eventually, this is not a practical option.
Trial 6 - GCC tuned with aggressive ggc-min-expand and ggc-min-heapsize
$ ./configure --with-incompatible-bdb CXXFLAGS=" --param ggc-min-expand=0 --param ggc-min-heapsize=4096"
| Time | Pi 1 | Pi 2 | VM |
|---|---|---|---|
| real | FAIL | FAIL | FAIL |
| user | FAIL | FAIL | FAIL |
| sys | FAIL | FAIL | FAIL |
| VmPeak | FAIL | FAIL | FAIL |
This also runs at a milli-crawl. It also ran for a day before I killed it and it made less progress than above.
Your thoughts?