Update docs 2025 04 14 (#54)

* Update docs 2025 03 31

- Docs: remove virtual_rocr.rst
- Fix documentation  warnings
- Reformat HIP RTC
- Docs: Refactor HIP porting guide
- Docs: Expand HIP porting guide and CUDA driver porting guide
- Minor fix
- Docs: Update environment variables file
- Bump rocm-docs-core[api_reference] from 1.15.0 to 1.17.0 in /docs/sphinx
- Docs: Update FP8 page to show both FP8 and FP16 types
- Bump sphinxcontrib-doxylink from 1.12.4 to 1.13.0 in /docs/sphinx
- Bumps [rocm-docs-core[api_reference]](https://github.com/ROCm/rocm-docs-core) from 1.17.0 to 1.17.1.
- Remove external link
- Update programming model
- Bump rocm-docs-core[api_reference] from 1.17.1 to 1.18.1 in /docs/sphinx
- Docs: Add page for Complex Math API
- Docs: Add page about HIP error codes
- Update docs: the compilation cache is enabled by default
- Fix fns32 function mask type in doc

* Bump rocm-docs-core[api_reference] from 1.18.1 to 1.18.2 in /docs/sphinx

Bumps [rocm-docs-core[api_reference]](https://github.com/ROCm/rocm-docs-core) from 1.18.1 to 1.18.2.
- [Release notes](https://github.com/ROCm/rocm-docs-core/releases)
- [Changelog](https://github.com/ROCm/rocm-docs-core/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/ROCm/rocm-docs-core/compare/v1.18.1...v1.18.2)

---
updated-dependencies:
- dependency-name: rocm-docs-core[api_reference]
  dependency-version: 1.18.2
  dependency-type: direct:production
  update-type: version-update:semver-patch

* Fix readme link

* Docs: Fix verbose paths generated by doxygen

* Handle git ssh in docs conf.py

[ROCm/hip commit: d0cf32a63a]
This commit is contained in:
Kiss, Istvan
2025-06-02 17:10:41 +02:00
committed by GitHub
parent be1d99f411
commit ed5a6441a9
46 changed files with 6457 additions and 3181 deletions
@@ -0,0 +1,446 @@
.. meta::
:description: This chapter describes the complex math functions that are accessible in HIP.
:keywords: AMD, ROCm, HIP, CUDA, complex math functions, HIP complex math functions
.. _complex_math_api_reference:
********************************************************************************
HIP complex math API
********************************************************************************
HIP provides built-in support for complex number operations through specialized types and functions,
available for both single-precision (float) and double-precision (double) calculations. All complex types
and functions are available on both host and device.
For any complex number ``z``, the form is:
.. math::
z = x + yi
where ``x`` is the real part and ``y`` is the imaginary part.
Complex Number Types
====================
A brief overview of the specialized data types used to represent complex numbers in HIP, available
in both single and double precision formats.
.. list-table::
:header-rows: 1
:widths: 40 60
* - Type
- Description
* - ``hipFloatComplex``
- | Complex number using single-precision (float) values
| (note: ``hipComplex`` is an alias of ``hipFloatComplex``)
* - ``hipDoubleComplex``
- Complex number using double-precision (double) values
Complex Number Functions
========================
A comprehensive collection of functions for creating and manipulating complex numbers, organized by
functional categories for easy reference.
Type Construction
-----------------
Functions for creating complex number objects and extracting their real and imaginary components.
.. tab-set::
.. tab-item:: Single Precision
.. list-table::
:header-rows: 1
:widths: 40 60
* - Function
- Description
* - | ``hipFloatComplex``
| ``make_hipFloatComplex(``
| ``float a,``
| ``float b``
| ``)``
- | Creates a complex number
| (note: ``make_hipComplex`` is an alias of ``make_hipFloatComplex``)
| :math:`z = a + bi`
* - | ``float``
| ``hipCrealf(``
| ``hipFloatComplex z``
| ``)``
- | Returns real part of z
| :math:`\Re(z) = x`
* - | ``float``
| ``hipCimagf(``
| ``hipFloatComplex z``
| ``)``
- | Returns imaginary part of z
| :math:`\Im(z) = y`
.. tab-item:: Double Precision
.. list-table::
:header-rows: 1
:widths: 40 60
* - Function
- Description
* - | ``hipDoubleComplex``
| ``make_hipDoubleComplex(``
| ``double a,``
| ``double b``
| ``)``
- | Creates a complex number
| :math:`z = a + bi`
* - | ``double``
| ``hipCreal(``
| ``hipDoubleComplex z``
| ``)``
- | Returns real part of z
| :math:`\Re(z) = x`
* - | ``double``
| ``hipCimag(``
| ``hipDoubleComplex z``
| ``)``
- | Returns imaginary part of z
| :math:`\Im(z) = y`
Basic Arithmetic
----------------
Operations for performing standard arithmetic with complex numbers, including addition,
subtraction, multiplication, division, and fused multiply-add.
.. tab-set::
.. tab-item:: Single Precision
.. list-table::
:header-rows: 1
:widths: 40 60
* - Function
- Description
* - | ``hipFloatComplex``
| ``hipCaddf(``
| ``hipFloatComplex p,``
| ``hipFloatComplex q``
| ``)``
- | Addition of two single-precision complex values
| :math:`(a + bi) + (c + di) = (a + c) + (b + d)i`
* - | ``hipFloatComplex``
| ``hipCsubf(``
| ``hipFloatComplex p,``
| ``hipFloatComplex q``
| ``)``
- | Subtraction of two single-precision complex values
| :math:`(a + bi) - (c + di) = (a - c) + (b - d)i`
* - | ``hipFloatComplex``
| ``hipCmulf(``
| ``hipFloatComplex p,``
| ``hipFloatComplex q``
| ``)``
- | Multiplication of two single-precision complex values
| :math:`(a + bi)(c + di) = (ac - bd) + (bc + ad)i`
* - | ``hipFloatComplex``
| ``hipCdivf(``
| ``hipFloatComplex p,``
| ``hipFloatComplex q``
| ``)``
- | Division of two single-precision complex values
| :math:`\frac{a + bi}{c + di} = \frac{(ac + bd) + (bc - ad)i}{c^2 + d^2}`
* - | ``hipFloatComplex``
| ``hipCfmaf(``
| ``hipComplex p,``
| ``hipComplex q,``
| ``hipComplex r``
| ``)``
- | Fused multiply-add of three single-precision complex values
| :math:`(a + bi)(c + di) + (e + fi)`
.. tab-item:: Double Precision
.. list-table::
:header-rows: 1
:widths: 40 60
* - Function
- Description
* - | ``hipDoubleComplex``
| ``hipCadd(``
| ``hipDoubleComplex p,``
| ``hipDoubleComplex q``
| ``)``
- | Addition of two double-precision complex values
| :math:`(a + bi) + (c + di) = (a + c) + (b + d)i`
* - | ``hipDoubleComplex``
| ``hipCsub(``
| ``hipDoubleComplex p,``
| ``hipDoubleComplex q``
| ``)``
- | Subtraction of two double-precision complex values
| :math:`(a + bi) - (c + di) = (a - c) + (b - d)i`
* - | ``hipDoubleComplex``
| ``hipCmul(``
| ``hipDoubleComplex p,``
| ``hipDoubleComplex q``
| ``)``
- | Multiplication of two double-precision complex values
| :math:`(a + bi)(c + di) = (ac - bd) + (bc + ad)i`
* - | ``hipDoubleComplex``
| ``hipCdiv(``
| ``hipDoubleComplex p,``
| ``hipDoubleComplex q``
| ``)``
- | Division of two double-precision complex values
| :math:`\frac{a + bi}{c + di} = \frac{(ac + bd) + (bc - ad)i}{c^2 + d^2}`
* - | ``hipDoubleComplex``
| ``hipCfma(``
| ``hipDoubleComplex p,``
| ``hipDoubleComplex q,``
| ``hipDoubleComplex r``
| ``)``
- | Fused multiply-add of three double-precision complex values
| :math:`(a + bi)(c + di) + (e + fi)`
Complex Operations
------------------
Functions for complex-specific calculations, including conjugate determination and magnitude
(absolute value) computation.
.. tab-set::
.. tab-item:: Single Precision
.. list-table::
:header-rows: 1
:widths: 40 60
* - Function
- Description
* - | ``hipFloatComplex``
| ``hipConjf(``
| ``hipFloatComplex z``
| ``)``
- | Complex conjugate
| :math:`\overline{a + bi} = a - bi`
* - | ``float``
| ``hipCabsf(``
| ``hipFloatComplex z``
| ``)``
- | Absolute value (magnitude)
| :math:`|a + bi| = \sqrt{a^2 + b^2}`
* - | ``float``
| ``hipCsqabsf(``
| ``hipFloatComplex z``
| ``)``
- | Squared absolute value
| :math:`|a + bi|^2 = a^2 + b^2`
.. tab-item:: Double Precision
.. list-table::
:header-rows: 1
:widths: 40 60
* - Function
- Description
* - | ``hipDoubleComplex``
| ``hipConj(``
| ``hipDoubleComplex z``
| ``)``
- | Complex conjugate
| :math:`\overline{a + bi} = a - bi`
* - | ``double``
| ``hipCabs(``
| ``hipDoubleComplex z``
| ``)``
- | Absolute value (magnitude)
| :math:`|a + bi| = \sqrt{a^2 + b^2}`
* - | ``double``
| ``hipCsqabs(``
| ``hipDoubleComplex z``
| ``)``
- | Squared absolute value
| :math:`|a + bi|^2 = a^2 + b^2`
Type Conversion
---------------
Utility functions for conversion between single-precision and double-precision complex number formats.
.. list-table::
:header-rows: 1
:widths: 40 60
* - Function
- Description
* - | ``hipFloatComplex``
| ``hipComplexDoubleToFloat(``
| ``hipDoubleComplex z``
| ``)``
- Converts double-precision to single-precision complex
* - | ``hipDoubleComplex``
| ``hipComplexFloatToDouble(``
| ``hipFloatComplex z``
| ``)``
- Converts single-precision to double-precision complex
Example Usage
=============
The following example demonstrates using complex numbers to compute the Discrete Fourier Transform (DFT)
of a simple signal on the GPU. The DFT converts a signal from the time domain to the frequency domain.
The kernel function ``computeDFT`` shows various HIP complex math operations in action:
* Creating complex numbers with ``make_hipFloatComplex``
* Performing complex multiplication with ``hipCmulf``
* Accumulating complex values with ``hipCaddf``
The example also demonstrates proper use of complex number handling on both host and device, including
memory allocation, transfer, and validation of results between CPU and GPU implementations.
.. code-block:: cpp
#include <hip/hip_runtime.h>
#include <hip/hip_complex.h>
#include <iostream>
#include <vector>
#include <cmath>
#define HIP_CHECK(expression) \
{ \
const hipError_t err = expression; \
if (err != hipSuccess) { \
std::cerr << "HIP error: " \
<< hipGetErrorString(err) \
<< " at " << __LINE__ << "\n"; \
exit(EXIT_FAILURE); \
} \
}
// Kernel to compute DFT
__global__ void computeDFT(const float* input,
hipFloatComplex* output,
const int N)
{
int k = blockIdx.x * blockDim.x + threadIdx.x;
if (k >= N) return;
hipFloatComplex sum = make_hipFloatComplex(0.0f, 0.0f);
for (int n = 0; n < N; n++) {
float angle = -2.0f * M_PI * k * n / N;
hipFloatComplex w = make_hipFloatComplex(cosf(angle), sinf(angle));
hipFloatComplex x = make_hipFloatComplex(input[n], 0.0f);
sum = hipCaddf(sum, hipCmulf(x, w));
}
output[k] = sum;
}
// CPU implementation of DFT for verification
std::vector<hipFloatComplex> cpuDFT(const std::vector<float>& input) {
const int N = input.size();
std::vector<hipFloatComplex> result(N);
for (int k = 0; k < N; k++) {
hipFloatComplex sum = make_hipFloatComplex(0.0f, 0.0f);
for (int n = 0; n < N; n++) {
float angle = -2.0f * M_PI * k * n / N;
hipFloatComplex w = make_hipFloatComplex(cosf(angle), sinf(angle));
hipFloatComplex x = make_hipFloatComplex(input[n], 0.0f);
sum = hipCaddf(sum, hipCmulf(x, w));
}
result[k] = sum;
}
return result;
}
int main() {
const int N = 256; // Signal length
const int blockSize = 256;
// Generate input signal: sum of two sine waves
std::vector<float> signal(N);
for (int i = 0; i < N; i++) {
float t = static_cast<float>(i) / N;
signal[i] = sinf(2.0f * M_PI * 10.0f * t) + // 10 Hz component
0.5f * sinf(2.0f * M_PI * 20.0f * t); // 20 Hz component
}
// Compute reference solution on CPU
std::vector<hipFloatComplex> cpu_output = cpuDFT(signal);
// Allocate device memory
float* d_signal;
hipFloatComplex* d_output;
HIP_CHECK(hipMalloc(&d_signal, N * sizeof(float)));
HIP_CHECK(hipMalloc(&d_output, N * sizeof(hipFloatComplex)));
// Copy input to device
HIP_CHECK(hipMemcpy(d_signal, signal.data(), N * sizeof(float),
hipMemcpyHostToDevice));
// Launch kernel
dim3 grid((N + blockSize - 1) / blockSize);
dim3 block(blockSize);
computeDFT<<<grid, block>>>(d_signal, d_output, N);
HIP_CHECK(hipGetLastError());
// Get GPU results
std::vector<hipFloatComplex> gpu_output(N);
HIP_CHECK(hipMemcpy(gpu_output.data(), d_output, N * sizeof(hipFloatComplex),
hipMemcpyDeviceToHost));
// Verify results
bool passed = true;
const float tolerance = 1e-5f; // Adjust based on precision requirements
for (int i = 0; i < N; i++) {
float diff_real = std::abs(hipCrealf(gpu_output[i]) - hipCrealf(cpu_output[i]));
float diff_imag = std::abs(hipCimagf(gpu_output[i]) - hipCimagf(cpu_output[i]));
if (diff_real > tolerance || diff_imag > tolerance) {
passed = false;
break;
}
}
std::cout << "DFT Verification: " << (passed ? "PASSED" : "FAILED") << "\n";
// Cleanup
HIP_CHECK(hipFree(d_signal));
HIP_CHECK(hipFree(d_output));
return passed ? 0 : 1;
}
File diff suppressed because it is too large Load Diff
-230
View File
@@ -1,230 +0,0 @@
.. meta::
:description: This page describes FP8 numbers present in HIP.
:keywords: AMD, ROCm, HIP, fp8, fnuz, ocp
*******************************************************************************
FP8 Numbers
*******************************************************************************
`FP8 numbers <https://arxiv.org/pdf/2209.05433>`_ were introduced to accelerate deep learning inferencing. They provide higher throughput of matrix operations because the smaller size allows more of them in the available fixed memory.
HIP has two FP8 number representations called *FP8-OCP* and *FP8-FNUZ*.
Open Compute Project(OCP) number definition can be found `here <https://www.opencompute.org/documents/ocp-8-bit-floating-point-specification-ofp8-revision-1-0-2023-12-01-pdf-1>`_.
Definition of FNUZ: fnuz suffix means only finite and NaN values are supported. Unlike other types, Inf are not supported.
NaN is when sign bit is set and all other exponent and mantissa bits are 0. All other values are finite.
This provides one extra value of exponent and adds to the range of supported FP8 numbers.
FP8 Definition
==============
FP8 numbers are composed of a sign, an exponent and a mantissa. Their sizes are dependent on the format.
There are two formats of FP8 numbers, E4M3 and E5M2.
- E4M3: 1 bit sign, 4 bit exponent, 3 bit mantissa
- E5M2: 1 bit sign, 5 bit exponent, 2 bit mantissa
HIP Header
==========
The `HIP header <https://github.com/ROCm/clr/blob/develop/hipamd/include/hip/amd_detail/amd_hip_fp8.h>`_ defines the FP8 ocp/fnuz numbers.
Supported Devices
=================
.. list-table:: Supported devices for fp8 numbers
:header-rows: 1
* - Device Type
- FNUZ FP8
- OCP FP8
* - Host
- Yes
- Yes
* - gfx942
- Yes
- No
* - gfx1200/gfx1201
- No
- Yes
Usage
=====
To use the FP8 numbers inside HIP programs.
.. code-block:: c
#include <hip/hip_fp8.h>
FP8 numbers can be used on CPU side:
.. code-block:: c
__hip_fp8_storage_t convert_float_to_fp8(
float in, /* Input val */
__hip_fp8_interpretation_t interpret, /* interpretation of number E4M3/E5M2 */
__hip_saturation_t sat /* Saturation behavior */
) {
return __hip_cvt_float_to_fp8(in, sat, interpret);
}
The same can be done in kernels as well.
.. code-block:: c
__device__ __hip_fp8_storage_t d_convert_float_to_fp8(
float in,
__hip_fp8_interpretation_t interpret,
__hip_saturation_t sat) {
return __hip_cvt_float_to_fp8(in, sat, interpret);
}
An important thing to note here is if you use this on gfx94x GPU, it will be fnuz number but on any other GPU it will be an OCP number.
The following code example does roundtrip FP8 conversions on both the CPU and GPU and compares the results.
.. code-block:: c
#include <hip/hip_fp8.h>
#include <hip/hip_runtime.h>
#include <iostream>
#include <vector>
#define hip_check(hip_call) \
{ \
auto hip_res = hip_call; \
if (hip_res != hipSuccess) { \
std::cerr << "Failed in hip call: " << #hip_call \
<< " with error: " << hipGetErrorName(hip_res) << std::endl; \
std::abort(); \
} \
}
__device__ __hip_fp8_storage_t d_convert_float_to_fp8(
float in, __hip_fp8_interpretation_t interpret, __hip_saturation_t sat) {
return __hip_cvt_float_to_fp8(in, sat, interpret);
}
__device__ float d_convert_fp8_to_float(float in,
__hip_fp8_interpretation_t interpret) {
__half hf = __hip_cvt_fp8_to_halfraw(in, interpret);
return hf;
}
__global__ void float_to_fp8_to_float(float *in,
__hip_fp8_interpretation_t interpret,
__hip_saturation_t sat, float *out,
size_t size) {
int i = threadIdx.x;
if (i < size) {
auto fp8 = d_convert_float_to_fp8(in[i], interpret, sat);
out[i] = d_convert_fp8_to_float(fp8, interpret);
}
}
__hip_fp8_storage_t
convert_float_to_fp8(float in, /* Input val */
__hip_fp8_interpretation_t
interpret, /* interpretation of number E4M3/E5M2 */
__hip_saturation_t sat /* Saturation behavior */
) {
return __hip_cvt_float_to_fp8(in, sat, interpret);
}
float convert_fp8_to_float(
__hip_fp8_storage_t in, /* Input val */
__hip_fp8_interpretation_t
interpret /* interpretation of number E4M3/E5M2 */
) {
__half hf = __hip_cvt_fp8_to_halfraw(in, interpret);
return hf;
}
int main() {
constexpr size_t size = 32;
hipDeviceProp_t prop;
hip_check(hipGetDeviceProperties(&prop, 0));
bool is_supported = (std::string(prop.gcnArchName).find("gfx94") != std::string::npos) || // gfx94x
(std::string(prop.gcnArchName).find("gfx120") != std::string::npos); // gfx120x
if(!is_supported) {
std::cerr << "Need a gfx94x or gfx120x, but found: " << prop.gcnArchName << std::endl;
std::cerr << "No device conversions are supported, only host conversions are supported." << std::endl;
return -1;
}
const __hip_fp8_interpretation_t interpret = (std::string(prop.gcnArchName).find("gfx94") != std::string::npos)
? __HIP_E4M3_FNUZ // gfx94x
: __HIP_E4M3; // gfx120x
constexpr __hip_saturation_t sat = __HIP_SATFINITE;
std::vector<float> in;
in.reserve(size);
for (size_t i = 0; i < size; i++) {
in.push_back(i + 1.1f);
}
std::cout << "Converting float to fp8 and back..." << std::endl;
// CPU convert
std::vector<float> cpu_out;
cpu_out.reserve(size);
for (const auto &fval : in) {
auto fp8 = convert_float_to_fp8(fval, interpret, sat);
cpu_out.push_back(convert_fp8_to_float(fp8, interpret));
}
// GPU convert
float *d_in, *d_out;
hip_check(hipMalloc(&d_in, sizeof(float) * size));
hip_check(hipMalloc(&d_out, sizeof(float) * size));
hip_check(hipMemcpy(d_in, in.data(), sizeof(float) * in.size(),
hipMemcpyHostToDevice));
float_to_fp8_to_float<<<1, size>>>(d_in, interpret, sat, d_out, size);
std::vector<float> gpu_out(size, 0.0f);
hip_check(hipMemcpy(gpu_out.data(), d_out, sizeof(float) * gpu_out.size(),
hipMemcpyDeviceToHost));
hip_check(hipFree(d_in));
hip_check(hipFree(d_out));
// Validation
for (size_t i = 0; i < size; i++) {
if (cpu_out[i] != gpu_out[i]) {
std::cerr << "cpu round trip result: " << cpu_out[i]
<< " - gpu round trip result: " << gpu_out[i] << std::endl;
std::abort();
}
}
std::cout << "...CPU and GPU round trip convert matches." << std::endl;
}
There are C++ style classes available as well.
.. code-block:: c
__hip_fp8_e4m3_fnuz fp8_val(1.1f); // gfx94x
__hip_fp8_e4m3 fp8_val(1.1f); // gfx120x
Each type of FP8 number has its own class:
- __hip_fp8_e4m3
- __hip_fp8_e5m2
- __hip_fp8_e4m3_fnuz
- __hip_fp8_e5m2_fnuz
There is support of vector of FP8 types.
- __hip_fp8x2_e4m3: holds 2 values of OCP FP8 e4m3 numbers
- __hip_fp8x4_e4m3: holds 4 values of OCP FP8 e4m3 numbers
- __hip_fp8x2_e5m2: holds 2 values of OCP FP8 e5m2 numbers
- __hip_fp8x4_e5m2: holds 4 values of OCP FP8 e5m2 numbers
- __hip_fp8x2_e4m3_fnuz: holds 2 values of FP8 fnuz e4m3 numbers
- __hip_fp8x4_e4m3_fnuz: holds 4 values of FP8 fnuz e4m3 numbers
- __hip_fp8x2_e5m2_fnuz: holds 2 values of FP8 fnuz e5m2 numbers
- __hip_fp8x4_e5m2_fnuz: holds 4 values of FP8 fnuz e5m2 numbers
FNUZ extensions will be available on gfx94x only.
@@ -0,0 +1,470 @@
.. meta::
:description: This page describes the FP8 and FP16 types present in HIP.
:keywords: AMD, ROCm, HIP, fp8, fnuz, ocp
*******************************************************************************
Low precision floating point types
*******************************************************************************
Modern computing tasks often require balancing numerical precision against hardware resources
and processing speed. Low precision floating point number formats in HIP include FP8 (Quarter Precision)
and FP16 (Half Precision), which reduce memory and bandwidth requirements compared to traditional
32-bit or 64-bit formats. The following sections detail their specifications, variants, and provide
practical guidance for implementation in HIP.
FP8 (Quarter Precision)
=======================
`FP8 (Floating Point 8-bit) numbers <https://arxiv.org/pdf/2209.05433>`_ were introduced
as a compact numerical format specifically tailored for deep learning inference. By reducing
precision while maintaining computational effectiveness, FP8 allows for significant memory
savings and improved processing speed. This makes it particularly beneficial for deploying
large-scale models with strict efficiency constraints.
Unlike traditional floating-point formats such as FP32 or even FP16, FP8 further optimizes
performance by enabling a higher volume of matrix operations per second. Its reduced bit-width
minimizes bandwidth requirements, making it an attractive choice for hardware accelerators
in deep learning applications.
There are two primary FP8 formats:
- **E4M3 Format**
- Sign: 1 bit
- Exponent: 4 bits
- Mantissa: 3 bits
- **E5M2 Format**
- Sign: 1 bit
- Exponent: 5 bits
- Mantissa: 2 bits
The E4M3 format offers higher precision with a narrower range, while the E5M2 format provides
a wider range at the cost of some precision.
Additionally, FP8 numbers have two representations:
- **FP8-OCP (Open Compute Project)**
- `This <https://www.opencompute.org/documents/ocp-8-bit-floating-point-specification-ofp8-revision-1-0-2023-12-01-pdf-1>`_
is a standardized format developed by the Open Compute Project to ensure compatibility
across various hardware and software implementations.
- **FP8-FNUZ (Finite and NaN Only)**
- A specialized format optimized for specific computations, supporting only finite and NaN values
(no Inf support).
- This provides one extra value of exponent and adds to the range of supported FP8 numbers.
- **NaN Definition**: When the sign bit is set, and all other exponent and mantissa bits are zero.
The FNUZ representation provides an extra exponent value, expanding the range of representable
numbers compared to standard FP8 formats.
HIP Header
----------
The `HIP FP8 header <https://github.com/ROCm/clr/blob/develop/hipamd/include/hip/amd_detail/amd_hip_fp8.h>`_
defines the FP8 ocp/fnuz numbers.
Supported Devices
-----------------
Different GPU models support different FP8 formats. Here's a breakdown:
.. list-table:: Supported devices for fp8 numbers
:header-rows: 1
* - Device Type
- FNUZ FP8
- OCP FP8
* - Host
- Yes
- Yes
* - CDNA1
- No
- No
* - CDNA2
- No
- No
* - CDNA3
- Yes
- No
* - RDNA2
- No
- No
* - RDNA3
- No
- No
Using FP8 Numbers in HIP Programs
---------------------------------
To use the FP8 numbers inside HIP programs.
.. code-block:: cpp
#include <hip/hip_fp8.h>
FP8 numbers can be used on CPU side:
.. code-block:: cpp
__hip_fp8_storage_t convert_float_to_fp8(
float in, /* Input val */
__hip_fp8_interpretation_t interpret, /* interpretation of number E4M3/E5M2 */
__hip_saturation_t sat /* Saturation behavior */
) {
return __hip_cvt_float_to_fp8(in, sat, interpret);
}
The same can be done in kernels as well.
.. code-block:: cpp
__device__ __hip_fp8_storage_t d_convert_float_to_fp8(
float in,
__hip_fp8_interpretation_t interpret,
__hip_saturation_t sat) {
return __hip_cvt_float_to_fp8(in, sat, interpret);
}
Note: On a gfx94x GPU, the type will default to the fnuz type.
The following code example does roundtrip FP8 conversions on both the CPU and GPU and compares the results.
.. code-block:: cpp
#include <hip/hip_fp8.h>
#include <hip/hip_runtime.h>
#include <iostream>
#include <vector>
#define hip_check(hip_call) \
{ \
auto hip_res = hip_call; \
if (hip_res != hipSuccess) { \
std::cerr << "Failed in HIP call: " << #hip_call \
<< " at " << __FILE__ << ":" << __LINE__ \
<< " with error: " << hipGetErrorString(hip_res) << std::endl; \
std::abort(); \
} \
}
__device__ __hip_fp8_storage_t d_convert_float_to_fp8(
float in, __hip_fp8_interpretation_t interpret, __hip_saturation_t sat) {
return __hip_cvt_float_to_fp8(in, sat, interpret);
}
__device__ float d_convert_fp8_to_float(float in,
__hip_fp8_interpretation_t interpret) {
__half hf = __hip_cvt_fp8_to_halfraw(in, interpret);
return hf;
}
__global__ void float_to_fp8_to_float(float *in,
__hip_fp8_interpretation_t interpret,
__hip_saturation_t sat, float *out,
size_t size) {
int i = threadIdx.x;
if (i < size) {
auto fp8 = d_convert_float_to_fp8(in[i], interpret, sat);
out[i] = d_convert_fp8_to_float(fp8, interpret);
}
}
__hip_fp8_storage_t
convert_float_to_fp8(float in, /* Input val */
__hip_fp8_interpretation_t
interpret, /* interpretation of number E4M3/E5M2 */
__hip_saturation_t sat /* Saturation behavior */
) {
return __hip_cvt_float_to_fp8(in, sat, interpret);
}
float convert_fp8_to_float(
__hip_fp8_storage_t in, /* Input val */
__hip_fp8_interpretation_t
interpret /* interpretation of number E4M3/E5M2 */
) {
__half hf = __hip_cvt_fp8_to_halfraw(in, interpret);
return hf;
}
int main() {
constexpr size_t size = 32;
hipDeviceProp_t prop;
hip_check(hipGetDeviceProperties(&prop, 0));
bool is_supported = (std::string(prop.gcnArchName).find("gfx94") != std::string::npos); // gfx94x
if(!is_supported) {
std::cerr << "Need a gfx94x, but found: " << prop.gcnArchName << std::endl;
std::cerr << "No device conversions are supported, only host conversions are supported." << std::endl;
return -1;
}
const __hip_fp8_interpretation_t interpret = (std::string(prop.gcnArchName).find("gfx94") != std::string::npos)
? __HIP_E4M3_FNUZ // gfx94x
: __HIP_E4M3;
constexpr __hip_saturation_t sat = __HIP_SATFINITE;
std::vector<float> in;
in.reserve(size);
for (size_t i = 0; i < size; i++) {
in.push_back(i + 1.1f);
}
std::cout << "Converting float to fp8 and back..." << std::endl;
// CPU convert
std::vector<float> cpu_out;
cpu_out.reserve(size);
for (const auto &fval : in) {
auto fp8 = convert_float_to_fp8(fval, interpret, sat);
cpu_out.push_back(convert_fp8_to_float(fp8, interpret));
}
// GPU convert
float *d_in, *d_out;
hip_check(hipMalloc(&d_in, sizeof(float) * size));
hip_check(hipMalloc(&d_out, sizeof(float) * size));
hip_check(hipMemcpy(d_in, in.data(), sizeof(float) * in.size(),
hipMemcpyHostToDevice));
float_to_fp8_to_float<<<1, size>>>(d_in, interpret, sat, d_out, size);
std::vector<float> gpu_out(size, 0.0f);
hip_check(hipMemcpy(gpu_out.data(), d_out, sizeof(float) * gpu_out.size(),
hipMemcpyDeviceToHost));
hip_check(hipFree(d_in));
hip_check(hipFree(d_out));
// Validation
for (size_t i = 0; i < size; i++) {
if (cpu_out[i] != gpu_out[i]) {
std::cerr << "cpu round trip result: " << cpu_out[i]
<< " - gpu round trip result: " << gpu_out[i] << std::endl;
std::abort();
}
}
std::cout << "...CPU and GPU round trip convert matches." << std::endl;
}
There are C++ style classes available as well.
.. code-block:: cpp
__hip_fp8_e4m3_fnuz fp8_val(1.1f); // gfx94x
__hip_fp8_e4m3 fp8_val(1.1f);
Each type of FP8 number has its own class:
- __hip_fp8_e4m3
- __hip_fp8_e5m2
- __hip_fp8_e4m3_fnuz
- __hip_fp8_e5m2_fnuz
There is support of vector of FP8 types.
- __hip_fp8x2_e4m3: holds 2 values of OCP FP8 e4m3 numbers
- __hip_fp8x4_e4m3: holds 4 values of OCP FP8 e4m3 numbers
- __hip_fp8x2_e5m2: holds 2 values of OCP FP8 e5m2 numbers
- __hip_fp8x4_e5m2: holds 4 values of OCP FP8 e5m2 numbers
- __hip_fp8x2_e4m3_fnuz: holds 2 values of FP8 fnuz e4m3 numbers
- __hip_fp8x4_e4m3_fnuz: holds 4 values of FP8 fnuz e4m3 numbers
- __hip_fp8x2_e5m2_fnuz: holds 2 values of FP8 fnuz e5m2 numbers
- __hip_fp8x4_e5m2_fnuz: holds 4 values of FP8 fnuz e5m2 numbers
FNUZ extensions will be available on gfx94x only.
FP16 (Half Precision)
=====================
FP16 (Floating Point 16-bit) numbers offer a balance between precision and
efficiency, making them a widely adopted standard for accelerating deep learning
inference. With higher precision than FP8 but lower memory requirements than FP32,
FP16 enables faster computations while preserving model accuracy.
Deep learning workloads often involve massive datasets and complex calculations,
making FP32 computationally expensive. FP16 helps mitigate these costs by reducing
storage and bandwidth demands, allowing for increased throughput without significant
loss of numerical stability. This format is particularly useful for training and
inference in GPUs and TPUs optimized for half-precision arithmetic.
There are two primary FP16 formats:
- **float16 Format**
- Sign: 1 bit
- Exponent: 5 bits
- Mantissa: 10 bits
- **bfloat16 Format**
- Sign: 1 bit
- Exponent: 8 bits
- Mantissa: 7 bits
The float16 format offers higher precision with a narrower range, while the bfloat16
format provides a wider range at the cost of some precision.
Additionally, FP16 numbers have standardized representations developed by industry
initiatives to ensure compatibility across various hardware and software implementations.
Unlike FP8, which has specific representations like OCP and FNUZ, FP16 is more uniformly
supported with its two main formats, float16 and bfloat16.
HIP Header
----------
The `HIP FP16 header <https://github.com/ROCm/clr/blob/develop/hipamd/include/hip/amd_detail/amd_hip_fp16.h>`_
defines the float16 format.
The `HIP BF16 header <https://github.com/ROCm/clr/blob/develop/hipamd/include/hip/amd_detail/amd_hip_bf16.h>`_
defines the bfloat16 format.
Supported Devices
-----------------
Different GPU models support different FP16 formats. Here's a breakdown:
.. list-table:: Supported devices for fp16 numbers
:header-rows: 1
* - Device Type
- float16
- bfloat16
* - Host
- Yes
- Yes
* - CDNA1
- Yes
- Yes
* - CDNA2
- Yes
- Yes
* - CDNA3
- Yes
- Yes
* - RDNA2
- Yes
- Yes
* - RDNA3
- Yes
- Yes
Using FP16 Numbers in HIP Programs
----------------------------------
To use the FP16 numbers inside HIP programs.
.. code-block:: cpp
#include <hip/hip_fp16.h> // for float16
#include <hip/hip_bf16.h> // for bfloat16
The following code example adds two float16 values on the GPU and compares the results
against summed float values on the CPU.
.. code-block:: cpp
#include <hip/hip_fp16.h>
#include <hip/hip_runtime.h>
#include <iostream>
#include <vector>
#define hip_check(hip_call) \
{ \
auto hip_res = hip_call; \
if (hip_res != hipSuccess) { \
std::cerr << "Failed in HIP call: " << #hip_call \
<< " at " << __FILE__ << ":" << __LINE__ \
<< " with error: " << hipGetErrorString(hip_res) << std::endl; \
std::abort(); \
} \
}
__global__ void add_half_precision(__half* in1, __half* in2, float* out, size_t size) {
int idx = threadIdx.x;
if (idx < size) {
// Load as half, perform addition in float, store as float
float sum = __half2float(in1[idx] + in2[idx]);
out[idx] = sum;
}
}
int main() {
constexpr size_t size = 32;
constexpr float tolerance = 1e-1f; // Allowable numerical difference
// Initialize input vectors as floats
std::vector<float> in1(size), in2(size);
for (size_t i = 0; i < size; i++) {
in1[i] = i + 1.1f;
in2[i] = i + 2.2f;
}
// Compute expected results in full precision on CPU
std::vector<float> cpu_out(size);
for (size_t i = 0; i < size; i++) {
cpu_out[i] = in1[i] + in2[i]; // Direct float addition
}
// Allocate device memory (store input as half, output as float)
__half *d_in1, *d_in2;
float *d_out;
hip_check(hipMalloc(&d_in1, sizeof(__half) * size));
hip_check(hipMalloc(&d_in2, sizeof(__half) * size));
hip_check(hipMalloc(&d_out, sizeof(float) * size));
// Convert input to half and copy to device
std::vector<__half> in1_half(size), in2_half(size);
for (size_t i = 0; i < size; i++) {
in1_half[i] = __float2half(in1[i]);
in2_half[i] = __float2half(in2[i]);
}
hip_check(hipMemcpy(d_in1, in1_half.data(), sizeof(__half) * size, hipMemcpyHostToDevice));
hip_check(hipMemcpy(d_in2, in2_half.data(), sizeof(__half) * size, hipMemcpyHostToDevice));
// Launch kernel
add_half_precision<<<1, size>>>(d_in1, d_in2, d_out, size);
// Copy result back to host
std::vector<float> gpu_out(size, 0.0f);
hip_check(hipMemcpy(gpu_out.data(), d_out, sizeof(float) * size, hipMemcpyDeviceToHost));
// Free device memory
hip_check(hipFree(d_in1));
hip_check(hipFree(d_in2));
hip_check(hipFree(d_out));
// Validation with tolerance
for (size_t i = 0; i < size; i++) {
if (std::fabs(cpu_out[i] - gpu_out[i]) > tolerance) {
std::cerr << "Mismatch at index " << i << ": CPU result = " << cpu_out[i]
<< ", GPU result = " << gpu_out[i] << std::endl;
std::abort();
}
}
std::cout << "Success: CPU and GPU half-precision addition match within tolerance!" << std::endl;
}
There are C++ style classes available as well.
.. code-block:: cpp
__half fp16_val(1.1f); // float16
__hip_bfloat16 fp16_val(1.1f); // bfloat16
Each type of FP16 number has its own class:
- __half
- __hip_bfloat16
There is support of vector of FP16 types.
- __half2: holds 2 values of float16 numbers
- __hip_bfloat162: holds 2 values of bfloat16 numbers
File diff suppressed because it is too large Load Diff
@@ -1,35 +0,0 @@
.. meta::
:description: This chapter lists user-mode API interfaces and libraries
necessary for host applications to launch compute kernels to
available HSA ROCm kernel agents.
:keywords: AMD, ROCm, HIP, HSA, ROCR runtime, virtual memory management
*******************************************************************************
HSA runtime API for ROCm
*******************************************************************************
The following functions are located in the https://github.com/ROCm/ROCR-Runtime repository.
.. doxygenfunction:: hsa_amd_vmem_address_reserve
.. doxygenfunction:: hsa_amd_vmem_address_free
.. doxygenfunction:: hsa_amd_vmem_handle_create
.. doxygenfunction:: hsa_amd_vmem_handle_release
.. doxygenfunction:: hsa_amd_vmem_map
.. doxygenfunction:: hsa_amd_vmem_unmap
.. doxygenfunction:: hsa_amd_vmem_set_access
.. doxygenfunction:: hsa_amd_vmem_get_access
.. doxygenfunction:: hsa_amd_vmem_export_shareable_handle
.. doxygenfunction:: hsa_amd_vmem_import_shareable_handle
.. doxygenfunction:: hsa_amd_vmem_retain_alloc_handle
.. doxygenfunction:: hsa_amd_vmem_get_alloc_properties_from_handle