GPU Programming Models¶
This episode introduces the major GPU programming models and explains the different approaches available for developing GPU-accelerated applications. Rather than focusing on a specific programming language, it presents the main categories of programming models, highlighting their advantages, limitations, portability, and typical application domains.
These topics include programming with standard C++ and Fortran, directive-based programming models (such as OpenMP and OpenACC), vendor-specific kernel programming models (such as CUDA and HIP), portable kernel programming models (such as SYCL, Kokkos, alpaka, and OpenCL), and high-level programming models (such as Python and Julia) and libraries. Together, these approaches provide a broad overview of the software ecosystem available for GPU programming and help developers choose the most appropriate model for their applications.
Questions
What are the main categories of GPU programming models, and how do they differ?
What are the key differences between directive-based, kernel-based, and standard language approaches to GPU programming?
How should I choose the most appropriate GPU programming framework for my application?
What are the trade-offs between portability, performance, and programming effort across different GPU programming models?
When should I use vendor-specific frameworks (such as CUDA or HIP) instead of portable programming models (such as SYCL, Kokkos, or OpenMP)?
Objectives
Understand the main categories of GPU programming models and their underlying concepts.
Compare different GPU programming frameworks in terms of portability, performance, and ease of development.
Evaluate the advantages and limitations of directive-based, kernel-based, and high-level programming approaches.
Select an appropriate GPU programming framework based on the requirements of a particular application or project.
Instructor note
30 min teaching
10 min exercises/discussion
There are several ways to use GPUs for computation. In the best case, when an application already supports GPU acceleration, it is sufficient to configure a few parameters to take advantage of the available hardware. In other cases, the computationally intensive parts of an application can be offloaded to well-established third-party libraries. This is increasingly common in areas such as machine learning, where Python frameworks transparently use GPU-accelerated libraries.
However, these scenarios remain relatively limited, and many applications still require explicit GPU programming to achieve high performance. A wide range of GPU programming environments and APIs are available, which can be broadly classified into directive-based programming models, non-portable kernel-based models, portable kernel-based models, and high-level frameworks and libraries, including approaches that integrate GPU support directly into programming languages.
Standard C++/Fortran¶
Programs written in standard C/C++ and Fortran can now take advantage of NVIDIA GPUs without relying on external GPU programming libraries or language extensions. This is made possible by the NVIDIA HPC SDK, whose compilers recognize standard language constructs, automatically generate GPU code, and optimize it for execution on NVIDIA devices. This approach allows developers to accelerate existing applications while preserving portability and readability of the source code.
A series of articles introducing acceleration with standard language parallelism is available here.
Guidelines for accelerating standard C++ with GPUs using
stdparcan be found here.Guidelines for writing Fortran code can be found here.
The performance achieved with these approaches is promising and, for many applications, can be comparable to more traditional GPU programming models. As demonstrated in the examples provided in the guidelines, significant speedups can often be obtained with relatively small modifications to existing C/C++ or Fortran code.
Directive-Based Programming Models¶
A fast and relatively inexpensive way to accelerate existing applications is to use directive-based programming models. In this approach, the original serial code is annotated with compiler directives (or pragmas) that indicate which loops or code regions should be executed on the GPU. These directives provide hints to the compiler, which automatically generates the corresponding GPU code. If the compiler does not support the directives, they are simply treated as comments, and the program executes as ordinary serial code.
Directive-based programming emphasizes programmer productivity and ease of use, often at the expense of achieving the highest possible performance. It enables existing applications to be accelerated with relatively small code changes, without requiring developers to write hardware-specific GPU kernels. The two most widely used directive-based programming models are OpenACC and OpenMP, both of which provide portable mechanisms for offloading computations to accelerators.
OpenACC¶
OpenACC is a directive-based programming standard developed by a consortium established in 2010 with the goal of creating a portable, scalable, and easy-to-use programming model for accelerators, including GPUs. The consortium includes GPU vendors such as NVIDIA and AMD, as well as leading supercomputing centers, universities, and software companies.
For many years, OpenACC implementations primarily targeted NVIDIA GPUs. More recently, however, there has been increasing effort to broaden support for additional GPU vendors and hardware architectures, improving the portability of OpenACC applications across a wider range of accelerator platforms.
OpenMP¶
OpenMP was originally developed as a portable, shared-memory programming API for parallelizing applications on multi-core CPUs. More recently, it has been extended to support GPU offloading, allowing selected regions of code to be executed on accelerators using the same directive-based programming model. Support for different GPU architectures depends on the capabilities of the underlying compiler.
Note
Directive-based programming models such as OpenMP and OpenACC are primarily intended for C/C++, and Fortran applications. Although third-party implementations and extensions exist for other programming languages, these remain the main languages supported by the standards.
Non-Portable Kernel-Based (Native) Programming Models¶
Direct GPU programming gives software developers a high degree of control by allowing them to write low-level code that interacts directly with the GPU hardware. This approach makes it possible to fully exploit the capabilities of the device and, in many cases, achieve significantly higher performance than CPU-only implementations.
The increased control comes at the cost of greater programming complexity. Effective use of direct GPU programming requires a solid understanding of the GPU execution model, memory hierarchy, and hardware architecture, as well as familiarity with the specific programming framework being used.
CUDA¶
CUDA Toolkit is a parallel computing platform and programming API developed by NVIDIA. It was the first widely adopted GPU programming framework and remains one of the most commonly used approaches for general-purpose GPU computing. CUDA allows developers to write C/C++-like code that is compiled and executed on NVIDIA GPUs, providing direct control over GPU resources and execution.
CUDA includes a comprehensive ecosystem of libraries, development tools, and optimization features that enable high performance for computationally intensive applications. However, despite its extensive adoption and mature software ecosystem, CUDA is limited to NVIDIA hardware and does not provide portability across different GPU vendors.
HIP¶
HIP (Heterogeneous Interface for Portability) is a GPU programming API developed by AMD that provides a low-level interface for GPU computing. HIP is designed to enable a single source code base to run on both AMD and NVIDIA GPUs, improving portability across different hardware platforms. It is based on the CUDA programming model and provides a programming interface that is very similar to CUDA, allowing many CUDA applications to be ported with relatively minor modifications.
Portable Kernel-Based Programming Models¶
Cross-platform portability ecosystems typically provide a higher-level abstraction layer that enables a convenient and portable programming model for GPU programming. By abstracting away hardware-specific details, these ecosystems reduce the time and effort required to develop, maintain, and deploy GPU-accelerated applications across diverse architectures. Their primary objective is to achieve performance portability, allowing a single-source application to deliver efficient execution across different GPU platforms and computing environments.
In the C++ ecosystem, the most prominent cross-platform portability frameworks include SYCL, OpenCL (with both C and C++ APIs), and Kokkos. Other notable frameworks include alpaka and RAJA, which provide additional approaches for writing portable, high-performance applications across heterogeneous computing systems.
In the Fortran ecosystem, portable kernel-based programming models are primarily provided through directive-based approaches and domain-specific frameworks. The most notable examples include OpenMP target offloading and OpenACC, which enable developers to express parallel kernels and data movement using compiler directives while maintaining a portable Fortran code base. Other approaches include frameworks such as PSyclone, which supports the generation of portable parallel kernels for domain-specific applications. These ecosystems aim to provide performance portability across heterogeneous architectures while preserving the productivity and maintainability of existing Fortran HPC applications.
Note
It should be noted that oneAPI is generally considered a portable kernel-based programming model oneAPI supports both C/C++ and Fortran, but with an important distinction: its portable accelerator programming model (SYCL) is C++-only, while its Fortran support comes through Intel’s HPC compiler ecosystem rather than through SYCL.
OpenCL¶
OpenCL (Open Computing Language) is a cross-platform, open-standard API for general-purpose parallel computing across CPUs, GPUs, FPGAs, and other accelerator devices. It supports a broad range of hardware architectures from multiple vendors, enabling developers to write applications that can be executed across diverse computing platforms. OpenCL provides a relatively low-level programming interface for GPU programming and follows a separate-source programming model, in contrast to single-source approaches used by programming models such as CUDA, HIP, Kokkos, and SYCL. Recent versions of the OpenCL standard introduced C++ support for both the host API and kernel programming; however, the traditional C-based interface remains the most widely adopted.
The OpenCL Working Group does not provide vendor-specific implementation frameworks. Instead, hardware vendors that produce OpenCL-compliant devices typically distribute OpenCL implementations as part of their software development kits (SDKs). Notable examples include the OpenCL SDKs provided by NVIDIA and AMD, which include the necessary libraries, compilers, and development tools for building and deploying OpenCL applications. These vendor-provided SDKs enable developers to target OpenCL-compatible devices while maintaining portability across supported platforms.
Figure: How OpenCL applications are organized. Source: Khronos group.¶
Kokkos¶
Kokkos is an open-source performance-portable programming model for heterogeneous parallel computing, primarily developed at Sandia National Laboratories. It is a C++-based ecosystem that enables developers to write efficient and scalable parallel applications targeting a wide range of many-core architectures, including CPUs, GPUs, and other accelerator devices. The Kokkos ecosystem consists of several components, including the Kokkos Core library, which provides abstractions for parallel execution and memory management; the Kokkos Kernels library, which provides optimized computational kernels for operations such as linear algebra and graph algorithms; and the Kokkos Tools library, which offers profiling, debugging, and performance analysis capabilities.
Kokkos is designed to integrate with existing HPC software ecosystems and supports interoperability with technologies such as MPI and OpenMP. Through its modular design and collaborations with other portability initiatives, Kokkos promotes interoperability, software sustainability, and standardization for portable high-performance C++ programming across diverse computing architectures.
Figure: The Kokkos ecosystem. Source: The Kokkos Lectures.¶
alpaka¶
alpaka (Abstraction Library for Parallel Kernel Acceleration) is an open-source, header-only C++ library that provides performance portability across heterogeneous computing architectures by abstracting hardware-specific parallelism. This library is platform-independent and it supports a wide range of devices, including host CPUs (x86, ARM, RISC-V) and GPUs from NVIDIA, AMD, and Intel, through multiple execution backends such as CUDA, HIP, SYCL, OpenMP, and serial execution.
A key feature of alpaka is its single-source programming model, which allows developers to write a kernel once and execute it across different hardware backends without modification. Its flexible backend architecture also enables a single application to target multiple accelerator technologies, making alpaka a lightweight and portable solution for heterogeneous high-performance computing.
Figure: The alpaka abstraction tree. Source: Alpaka workshop presentations.¶
SYCL¶
SYCL is a royalty-free, open-standard C++ programming model for heterogeneous and multi-device computing. It provides a high-level, single-source programming model for targeting CPUs, GPUs, and other accelerator devices. Although SYCL was originally developed on top of OpenCL, it is no longer tied to a specific backend and can be implemented over other low-level heterogeneous computing APIs, such as CUDA and HIP. This enables developers to write portable applications that can execute across a variety of hardware platforms. Despite its high-level abstractions, SYCL still requires developers to explicitly define GPU kernels.
Unlike alpaka, Kokkos, and RAJA, which are concrete software projects, SYCL is a programming standard with multiple independent implementations. The most widely used implementations for GPU programming are Intel oneAPI DPC++, which provides native support for Intel GPUs and supports NVIDIA and AMD GPUs through the Codeplay oneAPI plugins, and AdaptiveCpp (formerly hipSYCL and Open SYCL), which previously supports NVIDIA and AMD GPUs, with experimental Intel GPU support available in conjunction with Intel oneAPI DPC++. Other notable implementations include triSYCL and ComputeCPP.
Figure: Typical Use of SYCL for Single Source C++ Parallel Programming. Source: Khronos group.¶
RAJA¶
RAJA is an open-source C++ performance portability library developed by Lawrence Livermore National Laboratory (LLNL) for heterogeneous high-performance computing. It provides a high-level abstraction for expressing loop-based parallelism, enabling developers to write architecture-independent code that can be executed efficiently on CPUs and GPUs. By separating the specification of parallel execution from the application logic, RAJA promotes code portability and maintainability across diverse hardware architectures.
RAJA employs execution policies to map parallel operations to different hardware backends without requiring changes to the application code. It supports multiple execution backends, including CUDA, HIP, OpenMP, and sequential execution, allowing a single source code base to target a variety of heterogeneous computing platforms while maintaining high performance.
PSyclone¶
PSyclone is an open-source, domain-specific code generation and transformation framework for Fortran applications, primarily developed for high-performance scientific computing. Rather than serving as a general-purpose portability library, PSyclone automatically generates and optimizes parallel Fortran code from application source, enabling developers to target heterogeneous architectures while maintaining a single, portable code base.
PSyclone applies source-to-source transformations to introduce parallel programming models such as OpenMP, OpenACC, and MPI into Fortran applications. By separating scientific algorithms from parallel implementation details, it improves code maintainability, portability, and performance across a wide range of HPC platforms.
Comparison of portable GPU programming ecosystems¶
The following table summarizes the main characteristics of the major portability frameworks discussed. Since oneAPI is an ecosystem rather than a programming model, it is listed separately.
Framework |
Programming model |
Main features |
Main limitations |
Representative software/projects |
|---|---|---|---|---|
OpenCL |
Low-level API (C/C++) |
Open standard; supports CPUs, GPUs, FPGAs from multiple vendors; mature and widely available; fine-grained hardware control |
Verbose programming model; separate-source kernels; manual memory management; lower programmer productivity |
Blender (Cycles renderer), Darktable, GROMACS (historically), OpenCV, Hashcat, BOINC GPU applications |
Kokkos |
C++ library |
Performance portability via execution and memory abstractions; optimized kernels; strong HPC ecosystem; excellent scalability |
Template-heavy API; learning curve; primarily designed for HPC applications rather than general GPU computing |
LAMMPS, Trilinos, Cabana, SPARTA, ExaWind, ExaSky, many U.S. DOE Exascale Computing Project applications |
alpaka |
Header-only C++ library |
Single kernel implementation across multiple backends; lightweight; supports CUDA, HIP, SYCL, OpenMP, and serial execution; backend flexibility |
Lower-level than Kokkos; more implementation details exposed; smaller user community |
PIConGPU, PMacc, ISAAC, several Helmholtz and CERN research projects |
SYCL |
Single-source C++ |
High-level C++ abstraction; performance portability; modern C++ features; multiple implementations (oneAPI, AdaptiveCpp, etc.) |
Requires modern C++; implementations differ in maturity and feature support; kernels still written explicitly |
GROMACS (SYCL backend), AdaptiveCpp examples, Intel oneMKL, Intel oneDNN, Codeplay SDK examples, several exascale research applications |
RAJA |
C++ library |
Portable loop abstractions through execution policies; clean separation of algorithms and execution; integrates well with existing HPC codes |
Focused mainly on loop parallelism; fewer abstractions than Kokkos (e.g., memory management); smaller ecosystem |
MFEM, Axom, Umpire, CHAI, numerous LLNL simulation codes (e.g., hydrodynamics and radiation transport applications) |
PSyclone |
Source-to-source transformation (Fortran) |
Generates OpenMP/OpenACC/MPI code automatically; separates science code from parallel implementation; well suited for structured-grid scientific models |
Domain-oriented; not a general-purpose GPU programming framework; primarily adopted in climate and weather applications |
LFRic weather and climate model (UK Met Office), NEMO-related research projects, Dynamo 0.3 applications |
Intel oneAPI |
Software ecosystem |
Complete development ecosystem including SYCL (DPC++), compilers, MKL, MPI, debuggers, profilers; supports CPUs and GPUs |
SYCL is C++ only; some features remain Intel-centric; AMD/NVIDIA support depends on third-party plugins |
GROMACS, TensorFlow (Intel extension), PyTorch (Intel extension), OpenFOAM (Intel optimizations), Intel AI Analytics Toolkit examples |
High-Level Language Support for GPU Programming¶
High-level languages provide a more accessible approach to GPU programming by allowing developers to use familiar programming environments while leveraging GPU acceleration. Through language extensions, libraries, and compiler frameworks, these approaches abstract low-level GPU details and improve productivity, while balancing performance, portability, and control. Popular examples include Python- and Julia-based GPU programming ecosystems.
Python¶
Python supports GPU programming through multiple abstraction levels, ranging from low-level GPU interfaces to high-level libraries for scientific computing and machine learning. These approaches simplify GPU acceleration by hiding hardware details while providing different trade-offs between performance, flexibility, and ease of use. Representative Python GPU programming frameworks can be categorized by their abstraction level and usage scenarios.
Low-level GPU programming interfaces include PyCUDA, PyOpenCL, CUDA Python, and HIP Python, which provide direct access to GPU APIs.
GPU-accelerated numerical computing libraries include CuPy and Numba CUDA, which enable array-based computation and custom kernel development with Python syntax.
Higher-level domain-specific frameworks include PyTorch, TensorFlow, JAX, RAPIDS, and Taichi, which provide GPU acceleration for machine learning, data analytics, and scientific computing applications.
Low-level GPU programming interfaces
PyCUDA is a Python interface for NVIDIA CUDA that provides direct access to CUDA driver and runtime APIs. It enables developers to write custom CUDA kernels, manage GPU memory, and control execution from Python while retaining much of the flexibility of CUDA C/C++.
PyOpenCL is a Python wrapper for the OpenCL API that enables GPU programming across a wide range of hardware platforms. It allows developers to write and execute OpenCL kernels from Python, providing portability across devices from different vendors, including NVIDIA, AMD, and Intel.
CUDA Python is NVIDIA’s official Python interface for CUDA programming. It provides low-level access to CUDA driver and runtime APIs, allowing Python applications to interact directly with CUDA devices, manage memory, and launch GPU kernels without requiring C/C++ code.
HIP Python is a Python interface for AMD’s HIP programming model, enabling GPU programming on AMD devices using a Python-based interface. It provides access to HIP functionality and allows developers to write portable GPU applications targeting AMD GPUs while following a CUDA-like programming model.
GPU-accelerated numerical computing libraries
CuPy is a GPU-accelerated array computing library that provides a NumPy-compatible interface for NVIDIA GPUs. It allows users to accelerate existing NumPy-based applications with minimal code changes while supporting CUDA libraries and custom GPU kernels for high-performance numerical computing.
Numba CUDA is a just-in-time (JIT) compiler that enables GPU acceleration of Python functions through CUDA. It allows developers to write custom GPU kernels using a Python-based syntax and provides a balance between programming flexibility and ease of use for scientific and numerical applications.
Higher-level domain-specific frameworks
PyTorch is an open-source deep learning framework that provides high-level tensor computation and automatic differentiation capabilities with GPU acceleration. It offers a flexible programming interface for building and training machine learning models and supports GPU execution through CUDA, ROCm, and other backends.
TensorFlow is a widely used machine learning framework that provides high-level APIs for numerical computation and deep learning. It supports GPU acceleration through optimized backend libraries and enables scalable execution across CPUs, GPUs, and distributed computing platforms.
JAX is a high-performance numerical computing framework that combines NumPy-like programming with automatic differentiation, just-in-time compilation, and accelerator support through XLA. It enables efficient execution of scientific computing and machine learning workloads on GPUs, TPUs, and other accelerators.
RAPIDS is a suite of GPU-accelerated data science libraries developed by NVIDIA. It provides GPU-enabled alternatives to popular Python data analytics tools, such as pandas and scikit-learn, enabling accelerated data processing, machine learning, and graph analytics using CUDA.
Taichi is a high-level parallel programming framework embedded in Python that enables developers to write portable and efficient GPU-accelerated applications. It provides a domain-specific language and compiler infrastructure that automatically maps high-level code to different hardware backends, including CUDA, Vulkan, and CPU execution.
Julia¶
Julia provides first-class support for GPU programming through a collection of vendor-specific packages targeting GPUs from major hardware ecosystems. These include
CUDA.jl for NVIDIA GPUs,
AMDGPU.jl for AMD GPUs,
oneAPI.jl for Intel GPUs,
Metal.jl for Apple M-series GPUs.
These packages enable Julia applications to leverage GPU acceleration while maintaining a high-level, productive programming environment. Among these packages, CUDA.jl is the most mature and widely adopted, while AMDGPU.jl provides a stable solution for AMD GPUs. oneAPI.jl and Metal.jl are actively developed and functional, although they may still have limitations in feature coverage, stability, or performance compared with the more mature backends. Despite these differences, the APIs are designed to be highly consistent, making it relatively straightforward to port GPU applications between different backends.
All Julia GPU packages provide multiple levels of abstraction, ranging from high-level array-based programming that requires minimal GPU-specific knowledge to low-level kernel programming for developers who need fine-grained control over GPU execution and performance optimization.
Summary of GPU Programming Models¶
Directive-based programming models:
Existing serial code is augmented with compiler directives that identify regions to be executed on GPUs.
Common directive-based programming models include OpenACC and OpenMP target offloading.
These approaches prioritize programmer productivity and ease of adoption over fine-grained performance control.
They require minimal code modifications, making them suitable for incrementally introducing parallelism into existing applications.
Non-portable kernel-based programming models (native programming models):
Low-level GPU code is written explicitly to directly control GPU execution and resources.
CUDA is NVIDIA’s parallel computing platform and programming API for GPU acceleration.
HIP is an AMD-developed GPU programming API that provides a CUDA-like interface and supports both AMD and NVIDIA GPUs.
These approaches offer fine-grained control and high performance but require a deeper understanding of GPU architectures and parallel programming techniques.
Portable kernel-based programming models:
Provide higher-level abstractions for GPU programming while maintaining portability across different hardware platforms.
Representative examples include OpenCL, Kokkos, alpaka, RAJA, and SYCL.
Aim to achieve performance portability through a single-source programming model.
Enable applications to run across diverse GPU architectures, reducing the effort required for development, maintenance, and deployment of GPU-accelerated software.
High-level language support for GPU programming:
C++ and Fortran provide GPU programming support through language extensions, standard-based parallelism initiatives, and compiler frameworks.
Python GPU programming frameworks can be categorized by abstraction level into
low-level GPU interfaces (PyCUDA, PyOpenCL, CUDA Python, and HIP Python)
GPU-accelerated numerical computing libraries (CuPy and Numba CUDA)
higher-level domain-specific frameworks (PyTorch, TensorFlow, JAX, RAPIDS, and Taichi).
Julia provides dedicated GPU programming packages, including CUDA.jl, AMDGPU.jl, oneAPI.jl, and Metal.jl, targeting different hardware platforms.
These approaches offer high-level abstractions and language-specific interfaces that simplify GPU programming while balancing productivity, portability, and performance.
Guidelines for selecting appropriate GPU programming models
Each GPU programming environment has its own advantages and limitations, and the most suitable choice for a given application depends on several factors, including:
the target hardware platforms and accelerator architectures,
the characteristics of the computational workload,
the required level of performance and portability, and
the developer’s programming expertise and preferences.
In general, high-level and productivity-oriented APIs provide simpler programming models, greater abstraction, and improved code portability, making them easier to adopt and maintain. In contrast, low-level and performance-oriented APIs offer finer control over GPU execution, memory management, and hardware-specific optimizations, but require greater programming effort and a deeper understanding of GPU architectures. The choice of programming model therefore involves a trade-off between productivity, portability, performance, and development complexity.
Exercises¶
Choosing the right GPU programming model
What factors should influence the choice of a GPU programming model for a new application (e.g., hardware availability, performance requirements, portability, development effort)?
When would you choose a low-level model such as CUDA/HIP over a higher-level framework such as Kokkos, SYCL, or OpenMP offloading?
Is achieving maximum performance always the most important goal when selecting a GPU programming model?
Performance vs. portability trade-offs
Native GPU programming models such as CUDA provide fine-grained control over hardware. What advantages and disadvantages does this approach introduce?
Portable models such as SYCL, Kokkos, and RAJA aim for performance portability. What challenges might arise when trying to achieve both portability and high performance?
Do you think performance portability can completely replace vendor-specific optimization in future HPC applications? Why or why not?
Productivity and software maintainability
How do higher-level approaches (e.g., OpenMP, OpenACC, Python GPU libraries) reduce the effort required to GPU-enable existing applications?
What types of applications benefit most from low-level GPU programming, and what types benefit from higher-level abstractions?
How should developers balance short-term productivity with long-term software maintainability?
Hardware ecosystems and vendor dependence
CUDA is highly optimized for NVIDIA GPUs, while HIP targets AMD and NVIDIA platforms, and SYCL aims for broader portability. What are the benefits and risks of relying on vendor-specific ecosystems?
How important is support for multiple GPU vendors when developing scientific and engineering software?
Should application developers prioritize portability for future hardware generations, even if vendor-specific solutions currently provide better performance?
The Future of GPU programming
GPU programming models are evolving from low-level APIs toward higher-level abstractions and language-integrated approaches. What are the advantages of this trend?
Will future GPU programming converge toward a single dominant programming model, or will multiple models continue to coexist?
What features would an ideal GPU programming model need to provide for both application developers and hardware vendors?
Keypoints
GPU programming approaches can generally be categorized into four major groups:
directive-based programming models
non-portable kernel-based programming models
portable kernel-based programming models
high-level language support.
Each approach includes multiple programming models, frameworks, or language-specific solutions, offering different trade-offs in terms of performance, portability, productivity, hardware support, and development complexity.
The choice of an appropriate approach depends on application requirements, target hardware platforms, performance goals, and the level of control desired by developers.