ISPC (Implicit SPMD Program Compiler) is a compiler made by Intel that can be used to achieve a high degree of parallelism in CPU computations for multiple architectures (not just Intel). It generates parallel programs utilizing SIMD (Single Input Multiple Data) instructions and does it architecture agnostically. SIMD as its name implies are parallel instructions that operate on multiple data. This is useful for example doing fast vector computations such as adding two vectors or arrays. The standard way of adding two arrays using C++ is looping through the elements and adding them and then compiling the program using compiler optimization flags and hoping that the compiler optimizes the loop to use SIMD instructions. For something simple as vector addition a compiler such as GCC can vectorize the loop but SIMD optimizations are not typically done for some other common operations such as the dot product.
Integrating the ISPC compiler into a project is useful for example a game programmer, AI developer or anyone with heavy parallelizable CPU tasks who wants to write efficient calculations with support for multiple platforms and targets without having to write code for multiple architectures or having to make portability compromises.
ISPC actually uses a SPMD (Single Program Multiple Data) computing model as the ISPC compiled programs are executed as multiple programs in parallel and achieves a SIMD model.
Comparing ISPC programs to programs using intrinsics. Would you prefer to write code such as this
I’m sure the code above is efficient but it was probably not simple to write and only targets SSE.
That’s enough introduction. You can follow the steps I go through to create a minimum working project that utilizes ISPC to write efficient parallel functions. You can then integrate ISPC programs into your own project.
The ISPC compiler
My recommendation is downloading the precompiled binaries of the ISPC compiler. You can find the precompiled binaries here. Extract them somewhere accessible on your filesystem. For example
As of writing this article (March 2021) GCC has an ABI bug which means the Clang C++ compiler is needed for Linux to link with the ISPC object files. If you’d like to use the GCC compiler you’d need to compile ISPC from source using GCC. ISPC source code is available here.
ISPC Hello World
We’ll write a C++ main function that will call a ISPC program to perform some parallel computations. We’ll begin by writing the ISPC program and use a simple example from the ISPC website. ISPC uses a C-like language that should feel familiar. You can read more about the language details in the ISPC user’s guide. We’ll call this program simple.ispc.
The program accepts an array of floats that it does some basic computation with, squaring the first 3 values and evaluating the square root of any other value. When this is compiled with the ISPC compiler we’ll instruct it to also output a header we can use to access the function symbol from our main program. The header basically consists of this
We’ll use this main C++ program to execute the ISPC program.
We’re simply initializing an array of floats from 0 to 15 and calling the ISPC program with it and printing the results.
This is done using the following custom command. The example is for a Linux system. Change the path to your ISPC compiler path. The custom command for Microsoft using MSVC is almost identical except for the command path. The compiler path and flags should actually be passed to CMake as an environment variable but I’ve kept it hardcoded here for simplicity.
The command outputs the ISPC object file into the project binary directory and the header file an include directory under the project binary directory. The command specifies a dependence to the source file so that it’s rebuilt if it changes and it also specifies the outputs so CMake can figure out the dependency graph. You could use some nicer directories than the binary output directory root.
CMake version 3.19 and above has some nice support for ISPC but it still doesn’t properly target MSVC which is why I’m using this custom command.
To link against the ISPC object files I’m sure there exists multiple methods using CMake. Here I’ll use the CMake add_library function to create a link target. We’ll define a library called ispcLibrary consisting of the ISPC object file and tell the linker how it should be linked against (C-style linking).
What’s left is defining the main executable and instructing CMake how it should be compiled and linked, telling CMake ispcLibrary should be generated before the main executable and including the header directory we specified that ISPC should output to.
I’ll show a compilation under Linux using Clang since I’ve used the precompiled libraries. I’ll also be using the more modern Ninja build system instead of make.
Now when I run the main binary the following output is produced
Further if I disassemble the simple.o object file compiled against the AVX2 instruction set I can see AVX2 instructions being generated such as vbroadcastss.
You can access all relevant source files at my GitHub.