Scientific Computing

Using Intel oneAPI and MKL with CMake

There can be substantial speed boosts from Intel compilers with Intel CPUs. Intel oneAPI gives advanced debuggers and performance measurements. Intel oneMKL can give a significant speed boost to ABI-compatible compilers for certain math operations.

For Windows, use the oneAPI Command Prompt. Otherwise, specify environment variables CC, CXX, FC to indicate desired compilers via script:

Build with CMake:

cmake -B build

cmake --build build

Example CMakeLists.txt

To see the compiler commands CMake is issuing, use

cmake --build build -v

Refer to Intel Link Advisor for supported compiler / operating system / MKL combinations.


Get runtime confirmation that MKL is being used via MKL_VERBOSE.

  • Linux:

    MKL_VERBOSE=1 ./mytest
  • Windows

    set MKL_VERBOSE=1
    mytest.exe

That gives verbose text output upon use of MKL functions. That runtime option does slow down MKL performance, so normally we don’t use it.

Apple Silicon virtual machines

Native virtualization has a “guest” OS with the same CPU architecture as the “host” physical CPU. Non-native emulation generally runs slower than native virtualization. Non-native virtualization means a host computer (such as Apple Silicon) can emulate any supported CPU architecture. Apple Silicon is ARM64, but with virtualization such as UTM / QEMU the Apple Silicon CPU can emulate ARM32, x86_64, MIPS, RISC-V, PowerPC, and more within the container.

QEMU emulator is available on Homebrew for Apple Silicon and can emulate a different CPU architecture or run native architecture at nearly full performance. UTM is a containerized emulation based off of QEMU for iOS and macOS–like QEMU, the same CPU architecture is virtualized at near full performance, while non-native virtualization is emulated with slower performance. When creating a new virtual machine in UTM, the first questions include whether the VM will be virtualized (native) or emulated (non-native) and the CPU architecture. UTM works with native virtualized Windows 11 for ARM, Linux, and emulates many architectures, even old PowerPC macOS guest images.

VirtualBox is an open-source native virtualization application that generally targets x86_64 CPUs. VirtualBox “Developer Preview” for Apple Silicon is available from the Nightly Builds as “macOS/ARM64 BETA”. The Oracle developer notes that the VirtualBox Apple Silicon beta is not yet ready for production use.


Commercial paid Apple Silicon virtualization: these native virtualization applications are not open-source. They run native virtual machines on Apple Silicon including Windows 11 ARM.

  • Parallels is paid-only software
  • VMWare Fusion is paid software, but has a no-cost personal-use license for home users.

GUI viewers for HDF5 / NetCDF4 data

HDF5 is a popular data container format, a filesystem within a file. Many programs supporting HDF5 like Matlab can read and plot data. It is useful to have a standalone simple data browser like HDFview.

HDFview from the HDF Group can read HDF5, NetCDF4, and FITS. HDFview enables editing (writing) as well as reading HDF5. One can simply download the HDFview binaries, or use package managers:

  • Linux: apt install hdfview
  • macOS: brew install hdfview

ViTables is a Python-based HDF5 GUI.


The Java-based PanoplyJ is available for macOS, Linux and Windows.

CMake compiler flag precedence

To ensure the user set compile flags take precedence by appearing later in the command line vs. CMake’s automatic flags, use add_compile_options to set language-specific or project-wide compiler options. target_compile_options is similar, but applies to only one target.

The generator expression $<$<COMPILE_LANGUAGE:C>:...> is used to only add the flag for the C language. This is important as many compiler options are language-specific, particularly for C / C++ and Fortran projects.

If one truly needs to wipe out all CMake automatic flags, try settingCMAKE_FLAGS variable to an empty string.

Matlab arguments validation

Matlab function arguments validation syntax is generally recommended over legacy validateattributes() and inputParser(). Function arguments validation specification coerces the input and/or output variables to the class declaration given if possible, and errors otherwise.

  • Default values are easily specified, which required such verbose syntax before.
  • only a single class can be specified
  • recall the .empty method of most Matlab classes e.g. datetime.empty() that allows initializing an empty array.

Matlab argument validation syntax coerces class at runtime.

An example of Matlab function argument validation on input and output arguments is in matlab-stdlib.

Git default text editor

Git uses the EDITOR environment variable to determine which text editor to use for commit messages. Since the commit message editing is typically small and simple, it may be desired to set a distinct text editor just for Git. This is done via Git global config:

git config --global core.editor "nano"

CMake environment variable names with special characters

Most environment variable have alphanumeric names and don’t need any special consideration to access. On Windows, some important programs still use the “Program Files (x86)” directory, denoted by environment variable “ProgramFiles(x86)”.

cmake_minimum_required(VERSION 3.10)

set(px $ENV{ProgramFiles\(x86\)})

message(STATUS "${px}")

Most other code languages don’t have any particular issues using environment variables named with special characters.

All of the following print like:

C:\Program Files (x86)

Matlab:

getenv("ProgramFiles(x86)")

Python:

python -c "import os; print(os.environ['ProgramFiles(x86)'])"

C++:

#include <cstdlib>
#include <iostream>

int main()
{
    std::cout << std::getenv("ProgramFiles(x86)") << "\n";
    return EXIT_SUCCESS;
}

Fortran:

program env

implicit none

integer :: i
character(100) :: path

call get_environment_variable('ProgramFiles(x86)', path, status=i)
if (i/=0) error stop "env var ProgramFiles(x86) not found"

print '(a)', path

end program env

Matplotlib datetime tick labels

Matplotlib plots with datetime axes can benefit from rotating axes tick labels or concise tick labels to avoid overlapping text.

Example code used in this post with synthetic data:

from matplotlib.pyplot import Figure
import matplotlib.dates as mdates
from datetime import datetime, timedelta

def datetime_range(start: datetime, end: datetime, step: timedelta) -> list[datetime]:
    """like range() for datetime"""
    return [start + i * step for i in range((end - start) // step)]

t = datetime_range(datetime(2021, 1, 1), datetime(2021, 1, 2), timedelta(minutes=30))
y = range(len(t))

Rotate datetime tick labels

If rotating tick labels, the overall axes typically need to be positioned to allow for the rotated labels, otherwise the tick labels can be cut off the figure edges. The axes position is updated automatically with constrained_layout option of figure().

fg = Figure(constrained_layout=True)
ax = fg.gca()

ax.plot(t, y)
ax.set_xlabel('time')
ax.set_ylabel('y')
ax.tick_params(axis="x", labelrotation=30)  # arbitrary rotation amount

fg.savefig("example.png")

Matplotlib date formatting

Matplotlib datetime axes have numerous formatting options. Here we show the ConciseFormatter, which may avoid the need to rotate tick labels.

fg = Figure(constrained_layout=True)
ax = fg.gca()

ax.plot(t, y)
ax.set_xlabel('time')
ax.set_ylabel('y')

ax.xaxis.set_major_formatter(
    mdates.ConciseDateFormatter(ax.xaxis.get_major_locator()))

fg.savefig("example.png")

Diagnose silent quit program DLL conflict Windows

If a DLL conflicts with a programs needed DLLs, the program may quit with a specific message, or it may silently exit. The return code may correspond to segfault or other error. To help see if a DLL conflict is occurring, use gdb to run the program. This works even for general programs that weren’t built on the system. We suggest obtaining GDB via MSYS2. If there is a problem with a DLL, GDB will often print the name of the DLL. If the DLL is in an unexpected location, this may indicate a directory that should not be in environment variable Path.

Start GDB Fortran debugger: assuming executable myprog

gdb myprog.exe

Run program from (gdb) prompt:

r

AppleClang + GFortran no compact unwind warnings

On macOS when using the default “AppleClang” compiler in a Fortran project where GFortran objects are linked with C/C++ objects, the ld linker may emit warnings like:

ld: warning: could not create compact unwind for ...: register .. saved somewhere other than in frame
ld: warning: could not create compact unwind for ...: registers .. and .. not saved contiguously in frame

This is an actual issue because C++ exception handling will not completely work when this warning is emitted from C++ code coupled with Fortran code. In general, using C++ exception handling within C++ code that is linked with Fortran code will work just fine, except when this warning is issued. The solution is to use GNU GCC C++ compiler with GFortran instead of mixing AppleClang with GFortran.

Specifying environment variable:

LDFLAGS="$LDFLAGS -Wl,-no_compact_unwind"

removes the warning, but this also disables C++ exception handling so is not recommended.

It is possible to programmatically detect this link conflict from CMake using try_compile.

try_compile(abi_compile
PROJECT abi_check
SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/abi_check
OUTPUT_VARIABLE abi_output
)

if(abi_output MATCHES "ld: warning: could not create compact unwind for")
  message(WARNING "C++ exception handling will not work reliably due to incompatible compilers:
  C++ compiler ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}
  Fortran compiler ${CMAKE_Fortran_COMPILER_ID} ${CMAKE_Fortran_COMPILER_VERSION}"
  )
endif()

where directory “abi_check” contains a small C program that links with a Fortran program.