clang: warning: argument unused during compilation: '-fopenmp'
fatal error: 'omp.h' file not found
Editor’s Note: This post was updated on 06/18/2017 to address the changes in R 3.4.0, which allow for more native support of OpenMP in R.
Intro
Lately, I’ve spent the past year or so working with parallelization techniques. In particular, I’ve grown accustom to using OpenMP, which follows a shared memory paradigm that enables parallel computing on single computer by taking advantage of the multiple cores shipped on modern CPUs (more info in the HPC Parallel Talk). However, a lot of the work that I do is done on macOS instead of a Windows or a Linux development computer. This is particularly problematic as macOS does not currently support OpenMP under the default compiler (clang
). Thus, when the omp header, #include <omp.h>
, is included, the clang
compiler screams out the following:
R and OpenMP Sittin’ in a Tree, First comes love…
Lots of what I do is related to the statistical software R. Thus, imagine my surprise when I looked into R’s support for OpenMP and saw the official R project’s comment on this matter being rather bleak:
There is nothing to say what version of OpenMP is supported: version 3.0 (May 2008) is supported by recent versions of the Linux, Windows and Solaris platforms, but portable packages cannot assume that end users have recent versions. OS X currently uses Apple builds of clang with no OpenMP support.
On a more positive note though, official OpenMP with clang should be arriving sometime in June after WWDC as the Intel funded clang-omp feature has been merged into the main clang branch. Thus, there is hope that in XCode 9 OS X users may once again join the flock of OpenMP disciples. (A year later… XCode 8 goes to XCode 9…)
In the interim, there is a workaround that will enable the use of OpenMP in R with OS X. However, this workaround is only really for personal use and does not excuse a developer from protecting against a compiler’s lack of OpenMP support, which I will say is abnormal in this day and age but still…
Then comes a marriage of OpenMP and macOS…
Depending on your R installation, there are a few different ways to go about installing OpenMP on macOS. Please consult the appropriate section below to obtain the correct installation instructions.
Note: You will need to visit the Common Software section to install the shared components between approaches.
Common Software
One of the common tools we will need to install is the Xcode command-line tools that allow for access to a developer rich environment.
First off, let’s install Xcode’s command-line tools
- Open the
Terminal
from/Applications/Utilities/
- Type the following into
Terminal
xcode-select --install
- This will pop up a window that looks like so:
- Press “Install”
- Watch it install…
- Verify installation by typing into terminal:
git --version
3.0.0 - 3.3.*
Next up, installing homebrew, the missing package manager for OS X
- Open the
Terminal
from/Applications/Utilities/
- Type the following into
Terminal
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
Now, let’s use the package manager to grab gcc
and in turn gfortran
.
There are many different versions of gcc
the homebrew offers. To find one that suits you view:
brew search gcc
Obtaining OpenMP requires having a gcc
install that is greater than 4.2. Ideally, one should grab GCC 4.9.3, which offers OpenMP v4.0.
brew install homebrew/versions/gcc49 --without-multilib
Note: the use of --without-multilib
is because OpenMP may not work with it being active.
Disabling this feature means the lose of support for multiple architectures and, thus, the inability to compile binaries for different architectures. For our purposes, this is aokay.
Now, we need to take care of writing the correct symbolic links via:
# Handles symlink permission issues
sudo chown -R $(whoami):admin /usr/local
# Writes gcc symlink
brew link --overwrite --force gcc49
# You may need to do:
brew unlink gcc49 && brew link gcc49
Update - 06/18/2017 to the 09/23/2016 Update: On April 2nd, 2017, the homebrew team deprecated the boneyard in PR 314. Previously, only the clang-omp
formula was deprecated from the main homebrew repository. Therefore, the previously recommend approach to use clang-omp
is no longer possible. Instead, the recommendation is to now download and install llvm
, which provides OpenMP directly as the latest version of llvm
offered by homebrew is now 4.0.0. Therefore, we are going to install llvm
with:
brew install llvm
All done with these house keeping instructions for R < 3.4.0. Wasn’t that easy-peazy?
>= 3.4.0
With the release of R 3.4.0, the R binary for macOS is compiled using a compiler and gfortran
binaries that are not included with Xcode. However, unlike previous versions of R, these tools do provide support for OpenMP.
Official gfortran
binary download
Before we get too involved in the clang
compiler specifics, you will need to download the official gfortran 6.1
build. Downloading and installing gfortran 6.1
allows for Fortran routines to benefit from OpenMP code. The official website where the installer is located is here: https://gcc.gnu.org/wiki/GFortranBinaries#MacOS-11
Note: You will need to download the OS X El Capitan gfortran 6.1
binaries regardless of whether or not you are on macOS Sierra, which presently only offers gfortran 6.3
.
Obtaining clang
The R team responsible for the maintaining the macOS R binary has made available a pre-built version of the compiler. There are two options presently to installing the binary: 1. use a .pkg
installer that I created or 2. use a bash script.
For those use to using a graphical user interface (GUI) installer, you may wish to stick with it. The installer provides a more secure path manipulation and a smarter handling of a pre-existing ~/.R/Makevars
. Instructions for obtaining the installer can be found at the GUI Installer section.
On the otherhand, for those who prefer to know exactly what is going on feel free to use the shell script. Users of this approach should be warned in advance that you will overwrite your ~/.R/Makevars
file. Instructions for this are at the bash clang4
Install Script section.
GUI Installer
- Download the clang4-r.pkg (263.6 mb) from https://uofi.box.com/v/r-macos-clang-pkg
- MD5 Hash: f49df42ccc84ec529c489e8e3f02248c
- Install it!
You can verify that the downloaded installer has the above MD5 hash by opening Terminal
and typing:
md5 ~/Downloads/clang4-r.pkg
- This assumes that the installer was downloaded into the
~/Downloads
folder.
As an added benefit to open source, the code used to create the installer is available! You can view how I built the .pkg
here https://github.com/coatless/r-macos-clang. I may or may not release a tutorial later on about how to create a .pkg
.
Bash clang4
Install Script
# Download binary
curl -O http://r.research.att.com/libs/clang-4.0.0-darwin15.6-Release.tar.gz
# Extract binary onto root directory
tar fvxz clang-4.0.0-darwin15.6-Release.tar.gz -C /
# Overwrite the ~/.R/Makevars
cat <<- EOF > ~/.R/Makevars
# The following statements are required to use the clang4 binary
CC=/usr/local/clang4/bin/clang
CXX=/usr/local/clang4/bin/clang++
CXX11=$CXX
CXX14=$CXX
CXX17=$CXX
CXX1X=$CXX
LDFLAGS=-L/usr/local/clang4/lib
# End clang4 inclusion statements
EOF
Note: Specific CXX11
, CXX14
, and CXX17
implicit variables were added in R 3.4.0. Previously, using a different C++ compiler with C++ standard different from C++98 was handled with CXX1X
.
All done with these house keeping instructions for R >= 3.4.0. Wasn’t that easy-peazy?
Enabling R to Compile Code with OpenMP on macOS
Now, you can either use clang
or gcc
to compile code while having access to the <omp.h>
header. However, we need to let R know about these new options. To do this, we’ll need to use the knowledge found in Section 6.3.2: macOS packages of the R Installation and Administration to modify the ~/.R/Makevars/
file.
Open the ~/.R/Makevars
file on macOS via terminal with:
vim ~/.R/Makevars
Then choose your adventure!
3.0.0 - 3.3.*
OpenMP for R via homebrew clang
In the ~/.R/Makevars
, write the following:
CC=/usr/local/opt/llvm/bin/clang
CXX=/usr/local/opt/llvm/bin/clang++
CXX1X=/usr/local/opt/llvm/bin/clang++
LDFLAGS=-L/usr/local/opt/llvm/lib -Wl,-rpath,/usr/local/opt/llvm/lib
CXXFLAGS=-I/usr/local/opt/llvm/include
FLIBS=-L/usr/local/Cellar/gcc/4.9.3/lib/gcc/4.9
What we have just done is set global compile options for R. Specifically the lines break down as follows:
- Lines 1-3: set the default compiler for C, C++, and C++11 respectively.
- Line 4: supplies necessary libc++ library location.
- Line 5: supplies include statement.
- Line 6: provides a path to using
gfortran
.
OpenMP for R via homebrew gcc
In the ~/.R/Makevars
, write the following:
=-4.9
VER=gcc$(VER)
CC=g++$(VER)
CXX=g++$(VER)
CXX1X=-mtune=native -g -O2 -Wall -pedantic -Wconversion
CFLAGS=-mtune=native -g -O2 -Wall -pedantic -Wconversion
CXXFLAGS=-L/usr/local/Cellar/gcc/4.9.3/lib/gcc/4.9 FLIBS
The above follows slightly from the previous. In this case, I define a VER
variable to store the present version of gcc
binary. In this case, the addition of the version enables the right bin file to be located in /usr/local/Cellar/gcc/4.9.3/bin
. Otherwise, the Xcode version of gcc
that maps to clang
will be used.
>= 3.4.0
Verify the following statements are in the ~/.R/Makevars
file:
CC=/usr/local/clang4/bin/clang
CXX=/usr/local/clang4/bin/clang++
CXX11=$CXX
CXX14=$CXX
CXX17=$CXX
CXX1X=$CXX
LDFLAGS=-L/usr/local/clang4/lib
If they are not present, please add them by pressing I
to enter insert mode.
Close the file with ESC
+ :q
Protect your Users!
Now that you have OpenMP on macOS, you may think…
“Well, everyone now has OpenMP!”
This is not true at all since the entirety of this post exist because most user do not have OpenMP! It is your responsibility to use this new power you gained appropriately. Thus, when writing code using OpenMP please make sure to protect any reference to OpenMP.
The most common instance of this will be the protection of the omp
header inclusion. To do so, use the following:
#ifdef _OPENMP
#include <omp.h>
#endif
Here we use a preprocessor statement #ifdef
that checks to see if a macro variable _OPENMP
has been defined via #define _OPENMP
. If it is defined, then it allows the header file to be read like a traditional if-else. Otherwise, nothing happens.
Note:
Within R 3.4.0, the SUPPORT_OPENMP
variable has been removed from Rconfig.h
in favor of _OPENMP
. Within R 3.2.4, SUPPORT_OPENMP
variable was deprecated with the recommended usage being _OPENMP
.
The second area that may be problematic is when one goes to parallelize portions of code. In those cases, we need to add an #else
statement to the #ifdef
to provide serial instructions. Thus, we have:
#ifdef _OPENMP
// multithreaded OpenMP version of code
#else
// single-threaded version of code
#endif
Again, the above is to protect primarily users on macOS from not being able to run parallelization code. However, it also protects users that lack an OpenMP compliant compiler. Whenever Apple gets around to enabling OpenMP, this will not be as imperative as it was once before. But, until then, it’s sorta like we’re stuck supporting IE6 even though Microsoft Edge is out.
Misc
This post sprouted out of discussion on the Rcpp-devel listserv and due to questions arising on StackOverflow