Skip to content

Commit 75766e1

Browse files
SwRawAtlantaPepsi
authored andcommitted
Revamping documentation (#325)
* Revamping documentation * More updates * doc corrections * Peer-review edits * minor corrections * Peter's review edits * Update package_install.rst * Review edits
1 parent 33d0bf7 commit 75766e1

26 files changed

Lines changed: 4828 additions & 109 deletions

LICENSE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Copyright (c) 2019-2025 Advanced Micro Devices, Inc. All rights reserved.
1+
Copyright (c) 2019-2026 Advanced Micro Devices, Inc. All rights reserved.
22

33
Permission is hereby granted, free of charge, to any person obtaining a copy
44
of this software and associated documentation files (the "Software"), to deal
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
.. meta::
2+
:description: Explains how TransferBench validates transfer correctness by comparing destination memory against precomputed expected values derived from source buffers.
3+
:keywords: TransferBench data validation, TransferBench correctness, ValidateAllTransfers, PrepareReference, destination buffer, source buffer
4+
5+
.. _transferbench-data-validation:
6+
7+
==============================
8+
TransferBench data validation
9+
==============================
10+
11+
TransferBench validates the transfer results by comparing the destination (DST) memory to
12+
precomputed expected values. For each transfer, the DST buffer must equal the element-wise sum of all SRC buffers, or zero if there are no sources. A transfer is correct if, for every element ``i``, the value matches the expected value given in the following table:
13+
14+
.. list-table::
15+
:header-rows: 1
16+
17+
* - Number of sources
18+
- Expected value
19+
20+
* - 0
21+
- ``dst[i] == 0`` (or memset value)
22+
23+
* - 1
24+
- ``dst[i] == src0[i]``
25+
26+
* - N
27+
- ``dst[i] == src0[i] + src1[i] + ... + srcN-1[i]``
28+
29+
Source data preparation
30+
=======================
31+
32+
Before any transfers run, TransferBench prepares the SRC and DST memories as discussed in the following sections:
33+
34+
Expected source pattern
35+
-----------------------
36+
37+
Before any transfers run, TransferBench builds reference SRC buffers on the host using
38+
``PrepareReference(cfg, cpuBuffer, bufferIdx)``.
39+
40+
The pattern used depends on the configuration:
41+
42+
.. list-table::
43+
:header-rows: 1
44+
45+
* - Configuration
46+
- Behavior
47+
48+
* - ``fillCompress`` (non-empty)
49+
- Mix of random floats with optional zeroing per 64-byte line:
50+
``0`` = random, ``1`` = 1B0, ``2`` = 2B0, ``3`` = 4B0, ``4`` = 32B0.
51+
Percentages control the mix. For details, see
52+
:ref:`data-validation-var`.
53+
54+
* - ``fillPattern`` (non-empty)
55+
- Repeats the given ``vector<float>`` over all SRC buffers.
56+
57+
* - Default
58+
- Pseudo-random: ``PrepSrcValue(bufferIdx, i) = (((i % 383) * 517) % 383 + 31) * (bufferIdx + 1)``
59+
60+
``bufferIdx`` is the SRC index (0, 1, …) so each SRC buffer gets a different pattern.
61+
62+
Expected destination (``dstReference``)
63+
----------------------------------------
64+
65+
The expected destination is computed once before the iteration loop:
66+
67+
.. code-block:: text
68+
69+
dstReference[0] = memset to MEMSET_CHAR # used when numSrcs == 0
70+
dstReference[1] = srcReference[0] # 1 source
71+
dstReference[2] = dstReference[1] + srcReference[1] # 2 sources
72+
dstReference[k] = dstReference[k-1] + srcReference[k-1] # k sources
73+
74+
``dstReference[numSrcs]`` is the expected result for a transfer with ``numSrcs`` sources.
75+
76+
Initializing source and destination memories
77+
---------------------------------------------
78+
79+
For each transfer, the SRC memory on the rank that owns it is filled from the corresponding
80+
``srcReference`` buffer via ``hipMemcpy`` (host-to-device or device-to-device as appropriate).
81+
DST memory is zeroed (or memset) before transfers run.
82+
83+
How validation is timed
84+
========================
85+
86+
The timing of validation is controlled by the ``alwaysValidate`` option. By default
87+
(``alwaysValidate = 0``), validation runs once after all timed iterations complete,
88+
minimizing overhead during benchmarking. When ``alwaysValidate = 1``, validation is
89+
performed after every iteration; any detected error immediately stops the run.
90+
91+
.. list-table::
92+
:header-rows: 1
93+
94+
* - Option
95+
- When
96+
- Behavior
97+
98+
* - ``alwaysValidate = 0`` (default)
99+
- Once at the end of all iterations
100+
- ``ValidateAllTransfers`` called after the iteration loop.
101+
102+
* - ``alwaysValidate = 1``
103+
- After every timed iteration
104+
- ``ValidateAllTransfers`` called inside the loop; any error stops the run.
105+
106+
How validation (``ValidateAllTransfers``) works
107+
================================================
108+
109+
For each transfer and each DST, the following steps are performed:
110+
111+
1. **Rank check:** Only the rank that owns the destination performs validation.
112+
113+
2. **Get the actual output:**
114+
115+
- **CPU destination** or ``validateDirect = 1``: Point directly at the destination memory.
116+
- **GPU destination** and ``validateDirect = 0``: Copy destination to a host ``outputBuffer``
117+
via ``hipMemcpy``, then compare against ``outputBuffer``.
118+
119+
3. **Comparison:** Performed using ``memcmp(output, expected, numBytes)``. On mismatch, the code finds the first differing index and returns an error with the index, expected value, and actual value.
120+
121+
4. **Expected values:** Calculated using ``expected = dstReference[t.srcs.size()].data()``. The precomputed sum for the number of sources.
122+
123+
Validation options
124+
==================
125+
126+
The following options control when and how validation is performed. They can be set as
127+
environment variables or in a configuration file.
128+
129+
.. list-table::
130+
:header-rows: 1
131+
132+
* - Option
133+
- Environment variable
134+
- Description
135+
136+
* - ``alwaysValidate``
137+
- ``ALWAYS_VALIDATE``
138+
- To validate after each iteration, set to ``1``. To validate once at the end, set to ``0``.
139+
140+
* - ``validateDirect``
141+
- ``VALIDATE_DIRECT``
142+
- To compare GPU DST directly, set to ``1``. Supported on AMD hardware only and requires no host copy.
143+
To copy to host and compare, set to ``0``.
144+
145+
* - ``validateSource``
146+
- ``VALIDATE_SOURCE``
147+
- To validate the SRC memory right after it's initialized, set to ``1`` (optional early check).
148+
149+
.. note::
150+
151+
``validateDirect`` is not supported on NVIDIA. The code falls back to copying to host.
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
.. meta::
2+
:description: Explains how TransferBench measures performance at the test, Executor, and transfer levels using HIP events and CPU wall-clock timing.
3+
:keywords: TransferBench timing, TransferBench measurement, HIP events, CPU wall-clock, Executor timing, transfer timing, overhead
4+
5+
.. _transferbench-timing:
6+
7+
====================
8+
TransferBench timing
9+
====================
10+
11+
TransferBench measures performance at three nested levels: Test, Executor, and Transfer. Each level
12+
captures a different scope of elapsed time, and the timing method used depends on the executor type.
13+
14+
Timing levels
15+
=============
16+
17+
The following diagram illustrates the three levels of timing:
18+
19+
.. image:: /data/timing.png
20+
:width: 100%
21+
:align: center
22+
23+
The following table provides a quick summary of the three timing levels:
24+
25+
.. list-table::
26+
:header-rows: 1
27+
28+
* - Timing level
29+
- What it measures
30+
- How it is timed
31+
32+
* - Test
33+
- All transfers across all executors and all ranks
34+
- CPU wall-clock (``std::chrono::high_resolution_clock``)
35+
36+
* - Executor
37+
- All transfers that run on this executor
38+
- Varies by executor type (see :ref:`timing-methods`)
39+
40+
* - Transfer
41+
- A single transfer
42+
- Varies by executor type (see :ref:`timing-methods`)
43+
44+
.. _timing-methods:
45+
46+
Timing methods
47+
==============
48+
49+
The timing method used for each Executor and transfer depends on the Executor type and the value of
50+
``USE_HIP_EVENTS``.
51+
52+
Executor timing
53+
---------------
54+
55+
.. list-table::
56+
:header-rows: 1
57+
58+
* - Executor type
59+
- Timing method
60+
61+
* - CPU
62+
- CPU wall-clock (``std::chrono::high_resolution_clock``)
63+
64+
* - GFX / DMA
65+
- For ``USE_HIP_EVENTS=1`` (default): HIP events (``hipEventElapsedTime``)
66+
67+
For ``USE_HIP_EVENTS=0``: CPU wall-clock (``std::chrono::high_resolution_clock``)
68+
69+
* - NIC
70+
- CPU wall-clock (``std::chrono::high_resolution_clock``)
71+
72+
Transfer timing
73+
---------------
74+
75+
.. list-table::
76+
:header-rows: 1
77+
78+
* - Executor type
79+
- Timing method
80+
81+
* - CPU
82+
- CPU wall-clock (``std::chrono::high_resolution_clock``)
83+
84+
* - GFX
85+
- For ``USE_HIP_EVENTS=1`` (default): GPU wall-clock timestamp (``wall_clock64()``)
86+
87+
For ``USE_HIP_EVENTS=0``: CPU wall-clock (``std::chrono::high_resolution_clock``)
88+
89+
* - DMA
90+
- For ``USE_HIP_EVENTS=1`` (default): HIP events (``hipEventElapsedTime``)
91+
92+
For ``USE_HIP_EVENTS=0``: CPU wall-clock (``std::chrono::high_resolution_clock``)
93+
94+
* - NIC
95+
- CPU wall-clock (``std::chrono::high_resolution_clock``)
96+
97+
Overhead
98+
========
99+
100+
Overhead is the difference between the total CPU wall-clock time (Test time) and the elapsed time of
101+
the slowest Executor:
102+
103+
.. code-block:: text
104+
105+
Overhead = Test Time - MAX(Executor 0 Time, Executor 1 Time, ...)
106+
107+
Overhead captures scheduling and synchronization costs that fall outside of Executor-measured time,
108+
such as barrier waits and thread management.
109+
110+
Example output
111+
==============
112+
113+
The following example shows TransferBench output for a test with two Executors (CPU and GPU) and
114+
four transfers:
115+
116+
.. code-block:: text
117+
118+
Test 1:
119+
-------------------┬--------------┬------------┬-------------------┬--------------------
120+
Executor: CPU 00 │ 0.027 GB/s │ 77.492 ms │ 2097152 bytes │ 4.489 GB/s (sum)
121+
Executor 0 Time = 77.492 ms
122+
-------------------┼--------------┼------------┼-------------------┼--------------------
123+
Transfer 0 │ 4.476 GB/s │ 0.234 ms │ 1048576 bytes │ C0 -> C0:4 -> N
124+
Transfer 0 Time = 0.234 ms
125+
Transfer 1 │ 0.014 GB/s │ 77.359 ms │ 1048576 bytes │ G0 -> C0:4 -> N
126+
Transfer 1 Time = 77.359 ms
127+
-------------------┼--------------┼------------┼-------------------┼--------------------
128+
Executor: GPU 00 │ 97.436 GB/s │ 0.689 ms │ 67108864 bytes │ 129.692 GB/s (sum)
129+
Executor 1 Time = 0.689 ms
130+
-------------------┼--------------┼------------┼-------------------┼--------------------
131+
Transfer 2 │ 80.886 GB/s │ 0.415 ms │ 33554432 bytes │ G0 -> G0:4 -> G0
132+
Transfer 2 Time = 0.415 ms
133+
Transfer 3 │ 48.807 GB/s │ 0.687 ms │ 33554432 bytes │ G0 -> G0:4 -> G1
134+
Transfer 3 Time = 0.687 ms
135+
-------------------┼--------------┼------------┼-------------------┼--------------------
136+
Aggregate (CPU) │ 0.891 GB/s │ 77.688 ms │ 69206016 bytes │ Overhead 0.197 ms
137+
Test Time = 77.688 ms
138+
-------------------┴--------------┴------------┴-------------------┴--------------------
139+
Overhead = 77.688 - MAX(77.492, 0.689) = 0.197 ms
140+
141+
In this example:
142+
143+
- **Executor 0** (CPU) runs Transfers 0 and 1 and takes 77.492 ms (dominated by Transfer 1 at 77.359 ms).
144+
- **Executor 1** (GPU) runs Transfers 2 and 3 and takes 0.689 ms.
145+
- **Test Time** is 77.688 ms, measured by the CPU wall-clock across all Executors.
146+
- **Overhead** is 0.197 ms, calculated as ``77.688 - MAX(77.492, 0.689)``.

0 commit comments

Comments
 (0)