Source-linked AI summary
OCCA: A unified approach to multi-threading languages
David S Medina, Amik St-Cyr, T. Warburton
TL;DR
OCCA addresses the difficulty of maintaining parallel software across changing architectures and programming languages. It uses runtime compilation and macro expansion to express kernels once across OpenMP, OpenCL, and CUDA, with evaluations reporting portable high performance across several numerical methods and platforms. The approach nevertheless includes architecture-specific constraints, including special handling for returns across OpenMP and GPU execution and CUDA’s NVIDIA dependence.
Problem
Changing architectures and platform-specific languages make it difficult to maintain portable parallel implementations across hardware.
Method
OCCA uses a host API, runtime compilation, and macro-based kernel expansion to target OpenMP, OpenCL, and CUDA from a unified kernel language.
Results
Evaluations using finite difference, spectral element, and discontinuous Galerkin methods report platform portability without sacrificing performance.
Takeaways & Limitations
OCCA provides a unified interface for multi-platform numerical computing while retaining runtime platform-choice flexibility.
Takeaways & Limitations
OpenMP and GPU kernels differ in return and barrier behavior, while CUDA implementations remain tied to NVIDIA GPUs.
Abstract
from arXiv · showhide
The inability to predict lasting languages and architectures led us to develop OCCA, a C++ library focused on host-device interaction. Using run-time compilation and macro expansions, the result is a novel single kernel language that expands to multiple threading languages. Currently, OCCA supports device kernel expansions for the OpenMP, OpenCL, and CUDA platforms. Computational results using finite difference, spectral element and discontinuous Galerkin methods show OCCA delivers portable high performance in different architectures and platforms.
1. Introduction
OCCA addresses uncertainty across changing parallel architectures by unifying platform APIs and kernel languages while preserving runtime platform choice. Its macro-based approach expands one kernel implementation to OpenMP, OpenCL, or CUDA and targets portable performance.
- 1. Introduction: Changing architectures and accompanying programming standards make refactoring legacy parallel codes for new hardware a recurring challenge.The paper cites successive architectures and standards, including OpenMP, CUDA, and OpenCL, as evidence of this setting.
- 1.1. GPU Programming: CUDA provides GPU programming but vendor-locks implementations to NVIDIA hardware, motivating platform-portable approaches.OpenCL instead targets heterogeneous platforms including CPUs, GPUs, Xeon Phi, and FPGAs.
- 1.2. Compiler Constructs: Prior systems translate kernels, abstract runtime APIs, transform intermediate languages, or provide fixed numerical routines rather than a generic multi-platform kernel API.Examples include Swan, CU2CL, GPU Ocelot, ViennaCL, and PyFR.
- 1.3. OCCA Overview: OCCA abstracts OpenMP, OpenCL, and CUDA back-ends and expands kernels at runtime through dynamic pragmas or device-specific kernels.The approach is intended to simplify incorporation of additional languages.
- 1.3. OCCA Overview: OCCA’s host API and kernel language were designed to combine portability, performance, and platform-choice flexibility through runtime kernel expansion.The paper presents the host API before explaining expansion to OpenMP, OpenCL, and CUDA.
2. OCCA Host API
OCCA’s host API abstracts devices, memory, and kernels while allowing runtime selection of supported platforms and integration with external libraries. It unifies distinct execution and memory interfaces, but leaves performance-sensitive host-device transfers to programmers.
- 2. OCCA Host API: OCCA provides a stand-alone host API that can be combined with external libraries without conflict.Figure 1 presents this wrapping of different language APIs across platforms.
- 2.1. OCCA device: An occa device abstracts a selected processor or accelerator, creates its context and command queue, and allocates memory and compiles kernels.The platform target can be selected at runtime through just-in-time code generation.
- 2.2. OCCA memory: The occa memory class abstracts device memory handles and reports information such as device-array sizes for host-device communication.Memory objects also help kernels distinguish and communicate between distinct memory types.
- 2.2. OCCA memory: For performance reasons, programmers remain responsible for managing reads and writes between host and device.The memory abstraction facilitates communication but does not automate transfer management.
- 2.3. OCCA kernels: The occa kernel class presents one interface for OpenMP function pointers, OpenCL kernels, and CUDA functions despite differences in argument handling and implicit work-item information.The supplied passage introduces these discrepancies but does not provide their complete resolution.
3. OCCA Device API
OCCA uses macros and runtime expansion to present a unified kernel interface across OpenMP, OpenCL, and CUDA. Its abstractions model GPU-style work-groups and work-items while handling platform-specific barriers, registers, and loop execution.
- 3. OCCA Device API: OCCA macros translate one kernel interface into OpenMP, OpenCL, or CUDA implementations through platform-specific expansions.The preprocessor approach avoids source-to-source translation and leaves room for supporting additional languages.
- 3.2. Handling multi-threading architectures: The programming model separates outer work-group loops from inner work-item loops, mirroring GPU blocks and threads across supported backends.OpenCL and CUDA obtain group and thread dimensions through language-specific calls, while OCCA generalizes the loop structure for OpenMP.
- 3.2. Handling multi-threading architectures: OpenMP expands the analogous inner-most work-group loop with a parallel-for pragma, whereas GPU modes optimize away the corresponding macro scopes.This preserves a common kernel structure despite different work-item generation strategies.
- 3.2. Handling multi-threading architectures: Returning from the middle of a kernel is problematic because OpenMP uses continue while GPU work-items return, especially when barriers split loop groups.The occaInnerReturn keyword provides a cross-backend mechanism for this behavior.
- 3.3. Barriers: OCCA barriers split serial OpenMP inner loops, requiring discontinuous scopes that complicate register initialization and private-variable handling.Variables defined before one split loop may not carry into the next, and regular private variables can be overwritten across iterations.
- 3.4. Registers and register arrays: occaPrivate and occaPrivateArray provide work-item-indexed registers, expanding to ordinary GPU registers or per-work-item OpenMP storage.Operator overloading hides the backend-specific work-item ID and array indexing.
4. Numerical Examples
The numerical examples evaluate OCCA on finite difference, spectral element, and discontinuous Galerkin methods. Performance is compared separately across CPU and GPU architectures using GFLOP count and bandwidth.
- 4. Numerical Examples: OCCA is applied to finite difference, spectral element, and discontinuous Galerkin numerical methods across different architectures.The finite difference example introduces the project structure, while spectral element and discontinuous Galerkin implementations provide performance studies.
- 4. Numerical Examples: Kernel performance is measured with GFLOP count and bandwidth, comparing CPU and GPU architectures separately.The comparisons include OpenMP versus OpenCL on CPUs and OpenCL versus CUDA on GPUs.
4.1. Finite Difference
The finite difference example implements a discretized acoustic wave equation with OCCA kernel and host code. Its performance is reported in MNodes/s across CPU and GPU platforms, with platform-dependent differences in vectorization and GPU performance.
- 4.1. Finite Difference: The finite difference code is presented as a construction example rather than an optimized implementation, and the paper refers elsewhere for optimized GPU code.The example includes both OCCA device and host code.
- 4.1.1. Governing Equations: The finite difference example uses a 2D acoustic wave equation on a square structured grid with a first-order second-derivative stencil.The spatial discretization uses a stencil of size 2r + 1 in each dimension.
- 4.1.2. Discretization: The pseudocode advances each grid node by iterating over a one-dimensional stencil in each dimension and storing the next time-step solution.The update combines current and previous solutions with the weighted discrete Laplacian.
- 4.1. Finite Difference: The OCCA finite difference kernel maps work-group and work-item indices to grid coordinates, checks bounds, computes the stencil Laplacian, and writes the updated field.The host code selects OpenCL, CUDA, or OpenMP, allocates device buffers, builds the kernel, and configures global and local sizes.
- 4.1.3. Finite Difference Performance: OpenMP and Intel’s OpenCL platform show similar finite difference performance, while AMD’s OpenCL platform does not vectorize well.The metric is millions of nodes processed per second, reported as MNodes/s.
4.2. Spectral Element Methods
The spectral element example applies an occa SEM kernel to a screened Coulomb potential discretized with tensor-product hexahedral elements, then evaluates performance across CPU and GPU platforms.
- 4.2.1. Discretization: The discretization uses tensor-product Lagrange basis functions on Gauss-Lobatto-Legendre nodes and simplifies mass and stiffness operators for computation.The mass operator is lumped into diagonal quadrature weights, while the stiffness operator uses tensor-product structure and precomputed geometric factors.
- 4.2.2. Spectral Element Performance: The SEM performance figures report GFLOP performance and bandwidth usage across OpenMP and OpenCL CPU platforms.Figure 3 uses an Intel i7-3930K 6-core processor with Intel and AMD OpenCL platforms.
- 4.2.2. Spectral Element Performance: The GPU comparison evaluates CUDA and OpenCL on an NVIDIA Titan alongside OpenCL on an AMD Radeon 7970.Figure 4 reports GFLOP performance and bandwidth usage for these device-platform combinations.
- 4.2.2. Spectral Element Performance: Intel’s OpenCL and OpenMP perform similarly on the i7-3930K, while AMD’s OpenCL shows a large performance gap.The comparison is reported for the SEM operator, with an outlier associated with Intel’s 8-way vectorization and explicit loops at N = 7.
4.3. Discontinuous Galerkin for Shallow Water Equations
The discontinuous Galerkin example solves shallow water equations with a high-order element method and profiles the computationally intensive volume kernel across CPU and GPU platforms.
- 4.3.2. Discontinuous Galerkin Discretization: The shallow water equations are discretized on non-overlapping conforming triangular elements using discontinuous piecewise polynomials of degree N.The weak formulation separates surface and volume kernel contributions.
- 4.3.2. Discontinuous Galerkin Discretization: The performance figures measure GFLOP performance and bandwidth usage for the DG volume integration kernel.CPU results use an Intel i7-3930K with OpenMP and Intel and AMD OpenCL; GPU results use an NVIDIA Titan and AMD Radeon 7970.
- 4.3.2. Discontinuous Galerkin Discretization: The profiled volume kernel is the most computationally intensive routine in floating-point operations and uses optimal kernel tuning parameters.The reported mesh contains 212,800 elements.
- 4.3.3. Discontinuous Galerkin Performance: Intel’s OpenCL and OpenMP outperform AMD’s OpenCL at high polynomial orders, while CUDA outperforms OpenCL on the Titan GPU.At low orders, Intel’s OpenCL and OpenMP operate similarly; OpenCL remains comparable to CUDA at some polynomial orders.
5. Concluding Remarks
OCCA combines multi-threading and many-core processing through a unified macro-based language with runtime compilation. Performance profiles across finite difference, spectral element, and discontinuous Galerkin applications report platform portability without sacrificing performance.
- 5. Concluding Remarks: OCCA introduces a unified multi-threading language that combines elements of multi-threading and many-core parallel processing.Its macro-based approach allows code to differ across architecture and platform execution while supporting additional languages.
- 5. Concluding Remarks: Runtime compilation and macro expansion support platform portability across finite difference, spectral element, and discontinuous Galerkin applications.The reported performance profiles use real-file applications and retain performance across platforms.
7. Appendix: OCCA Kernel Keywords
The appendix organizes OCCA keywords by purpose and presents corresponding macro expansions for OpenMP, OpenCL, and CUDA. Its tables cover kernel indexing, sizes, loop scopes, attributes, setup, synchronization, memory, loop control, and platform-specific optimization.
- The appendix presents corresponding OpenMP, OpenCL, and CUDA macro expansions alongside each OCCA keyword.The table format places the OCCA keyword first, followed by expansions for the three supported platforms.
- Table 1 covers keywords for obtaining work-group and work-item IDs, while Table 2 covers their sizes.
- Table 3 documents keywords for explicitly displaying work-group and work-item loop scopes.
- Tables 4 and 5 cover OCCA variable attributes, kernel prototypes, and kernel setup.
- Tables 6 and 7 cover synchronization barriers, platform-dependent private memory, and OpenMP loop-break handling around barriers.
- Table 8 lists keywords that specify platforms for platform-dependent kernel optimization.