3 ms·
This is a GPU compiler that made different design choices to LLVM and GCC. Autovectorisation of numerical code has a long and not totally successful history. I
by JonChesterfield 3y ago
This is a GPU compiler that made different design choices to LLVM and GCC.
Autovectorisation of numerical code has a long and not totally successful history. It turns out to be really difficult to determine which instructions are independent enough to pack their arguments into vector registers.
Where Cuda differs from autovectorisation is by giving that data independence problem to the programmer. The semantics of the kernel are that the instructions are independent from one another, thus the compiler doesn't need to prove it, and if you've written code where that has unwanted behaviour it's your problem.
Where ispc differs from llvm is by immediately mapping that implicitly vectorised code onto vector operations in the compiler front end and then optimising the vector form. llvm keeps the instructions in the scalar style until very near codegen.
I think ispc made the better choice here. It makes control flow easier to optimise and basic blocks harder to optimise than llvm's approach and that looks like the right tradeoff. However there's an element of the grass is always greener here and it's an expensive experiment to change llvm over to predicated vector IR to find out whether it is better in practice.
Interesting blog series, recommended.
- adrian_b 3y agoThe semantics of the CUDA (or OpenCL) kernels are identical to the OpenMP Fortran "parallel do" and OpenMP C/C++ "parallel for" (which derive from similar syntax proposed by C.A.R. Hoare in 1978 and first implemented in the language Occam). As you say, the difference between autovectorization and OpenMP or CUDA is that for the latter the programmer specifies the independent sequences of operations, so that the compiler knows with certainty when they can be safely scheduled to be executed concurrently, either on different processor cores or on different SIMD lanes of the same core, therefore the performance of the compiled code is predictable and it does not vary wildly after minor changes in the source code. Moreover, the compiler can report errors when the programmer introduces by mistake dependencies between sequences of operations that are intended to be independent.