Skip to content

Commit e3884bb

Browse files
authored
[CK_BUILDER] Debug utilities (#3528)
* ck-builder: make toString to_string We are using snake case for CK-Builder * ck-builder: add debug.hpp with tensor descriptor printing function This adds some initial functionality to debug.hpp, a header which will be used to house some debug utilities. * ck-builder: abstract nd-iteration Abstracting this makes it easier to test, clearer, and allows us to use it elsewhere (such as in debug.hpp soon) * ck-builder: tensor printing * ck-builder: rename INT32 to I32 This makes it more in line with the other data type definitions.
1 parent 770a144 commit e3884bb

14 files changed

Lines changed: 1327 additions & 64 deletions

experimental/builder/include/ck_tile/builder/factory/helpers/ck/conv_tensor_type.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ struct DataTypeToCK<DataType::FP32>
3333
using type = float;
3434
};
3535
template <>
36-
struct DataTypeToCK<DataType::INT32>
36+
struct DataTypeToCK<DataType::I32>
3737
{
3838
using type = int32_t;
3939
};

experimental/builder/include/ck_tile/builder/testing/debug.hpp

Lines changed: 634 additions & 0 deletions
Large diffs are not rendered by default.

experimental/builder/include/ck_tile/builder/testing/tensor_descriptor.hpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <array>
88
#include <vector>
99
#include <sstream>
10+
#include <iosfwd>
1011
#include <concepts>
1112
#include <algorithm>
1213
#include <hip/hip_runtime.h>
@@ -123,6 +124,33 @@ struct Extent : std::array<size_t, RANK>
123124
template <typename... T>
124125
Extent(T...) -> Extent<sizeof...(T)>;
125126

127+
/// @brief Extent printer
128+
///
129+
/// This function implements an ostream printing overload for `Extent`, so that
130+
/// they can be printed in the usual `stream << extent` fashion.
131+
///
132+
/// @tparam RANK Rank (number of spatial dimensions) of the extent.
133+
///
134+
/// @param stream The stream to print the extent to.
135+
/// @param extent The extent to print to the stream.
136+
template <size_t RANK>
137+
std::ostream& operator<<(std::ostream& stream, const Extent<RANK>& extent)
138+
{
139+
stream << '[';
140+
bool first = true;
141+
for(const auto x : extent)
142+
{
143+
if(first)
144+
first = false;
145+
else
146+
stream << ", ";
147+
148+
stream << x;
149+
}
150+
151+
return stream << ']';
152+
}
153+
126154
/// @brief Concept for automatically deriving tensor memory layout.
127155
///
128156
/// A `TensorStridesGenerator` is a type which can be used to automatically

experimental/builder/include/ck_tile/builder/testing/tensor_foreach.hpp

Lines changed: 119 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,102 @@
1818

1919
namespace ck_tile::builder::test {
2020

21+
/// @brief Utility structure for N-dimensional iteration using a flat index
22+
///
23+
/// This structure's main purpose is to "unmerge" a flattened index into a
24+
/// multi-dimensional index, which helps when iterating over multi-dimensional
25+
/// indices without having to write an arbitrary amount of nested for loops.
26+
/// A minimal amount of precomputation must be done to do this efficiently,
27+
/// which is handled in the constructor of this type.
28+
///
29+
/// @details Decoding a flat index into a multi-dimensional index is done by
30+
/// first computing a reverse scan of the shape. These values can then be
31+
/// used to decode the index in the usual way:
32+
///
33+
/// x = flat_idx / (size_y * size_z)
34+
/// y = flat_idx % (size_y * size_z) / size_z
35+
/// z = flat_idx % (size_y * size_z) % size_z
36+
/// etc
37+
///
38+
/// The decode order is such that the innermost dimension (right in
39+
/// the shape extent) changes the fastest.
40+
///
41+
/// @tparam RANK The rank (number of spatial dimensions) of the tensor to
42+
/// iterate.
43+
template <size_t RANK>
44+
struct NdIter
45+
{
46+
/// @brief Prepare N-dimensional iteration over a particular shape.
47+
///
48+
/// Precompute ashape into a form that can be used to easily decode a flat
49+
/// index into a multi-dimensional index.
50+
///
51+
/// @param shape The shape to iterate over.
52+
explicit NdIter(const Extent<RANK>& shape)
53+
{
54+
// Precompute shape_scan = [..., shape[-2] * shape[-1], shape[-1], 1]
55+
56+
numel_ = 1;
57+
for(int i = RANK; i > 0; --i)
58+
{
59+
shape_scan_[i - 1] = numel_;
60+
numel_ *= shape[i - 1];
61+
}
62+
}
63+
64+
/// @brief Unflatten a flat index into a multi-dimensional index
65+
///
66+
/// This applies the usual multi-dimensional indexing method over the
67+
/// precomputed shape scan to get back a multi-dimensional index.
68+
/// The decode order is such that the innermost dimension (right in
69+
/// the shape extent) changes the fastest.
70+
///
71+
/// @param flat_index The "flattened" (1-dimensional) index of the tensor
72+
///
73+
/// @returns A multi-dimensional index into the tensor
74+
///
75+
/// @pre `0 <= flat_index < size()` (in other words, the `flat_index` must
76+
/// be in bounds of the tensor shape that this `NdIter` was made from).
77+
__host__ __device__ Extent<RANK> operator()(size_t flat_index) const
78+
{
79+
Extent<RANK> index = {};
80+
auto idx = flat_index;
81+
for(size_t i = 0; i < RANK; ++i)
82+
{
83+
const auto scanned_dim = shape_scan_[i];
84+
index[i] = idx / scanned_dim;
85+
idx %= scanned_dim;
86+
}
87+
88+
return index;
89+
}
90+
91+
/// @brief Return the total elements to iterate over
92+
///
93+
/// Get the total number of elements in the shape to iterate over. This value
94+
/// can be used to construct a complete for loop to iterate over all indices
95+
/// of a tensor, for example:
96+
///
97+
/// for(size_t i = 0; i < iter.numel(); ++i)
98+
/// {
99+
/// const auto index = iter(i);
100+
/// use(index);
101+
/// }
102+
__host__ __device__ size_t numel() const { return numel_; }
103+
104+
private:
105+
/// Reverse (right) scan of the shape to iterate over.
106+
Extent<RANK> shape_scan_;
107+
108+
/// The total number of elements in the shape. This value turns out to be almost
109+
/// always required when iterating over a shape, so just store it in this type
110+
/// so that it is easily accessible.
111+
size_t numel_;
112+
};
113+
114+
template <size_t RANK>
115+
NdIter(Extent<RANK>) -> NdIter<RANK>;
116+
21117
/// @brief Concept for constraining tensor iteration functors.
22118
///
23119
/// This concept checks that a functor has the correct signature for
@@ -50,28 +146,19 @@ constexpr int DEVICE_FOREACH_BLOCK_SIZE = 256;
50146
/// @tparam F The type of the callback to invoke. This function must be
51147
/// compatible with execution as a __device__ function.
52148
///
53-
/// @param numel The total number of elements in the tensor.
54-
/// @param shape_scan A right-exclusive scan of the shape of the tensor.
149+
/// @param iter An NdIter instance to help iterating over the tensor.
55150
/// @param f The callback to invoke for each index of the tensor. This
56151
/// functor must be eligible for running on the GPU.
57152
template <int BLOCK_SIZE, size_t RANK, typename F>
58153
requires ForeachFunctor<F, RANK>
59154
__global__ __launch_bounds__(BLOCK_SIZE) //
60-
void foreach_kernel(const size_t numel, Extent<RANK> shape_scan, F f)
155+
void foreach_kernel(NdIter<RANK> iter, F f)
61156
{
62157
const auto gid = blockIdx.x * BLOCK_SIZE + threadIdx.x;
63-
for(size_t flat_idx = gid; flat_idx < numel; flat_idx += gridDim.x * BLOCK_SIZE)
158+
for(size_t flat_idx = gid; flat_idx < iter.numel(); flat_idx += gridDim.x * BLOCK_SIZE)
64159
{
65160
// Compute the current index.
66-
Extent<RANK> index = {};
67-
68-
size_t idx = flat_idx;
69-
for(size_t i = 0; i < RANK; ++i)
70-
{
71-
const auto scanned_dim = shape_scan[i];
72-
index[i] = idx / scanned_dim;
73-
idx %= scanned_dim;
74-
}
161+
const auto index = iter(flat_idx);
75162

76163
// Then invoke the callback with the index.
77164
f(index);
@@ -160,26 +247,20 @@ void tensor_foreach(const Extent<RANK>& shape, ForeachFunctor<RANK> auto f)
160247
// order in the kernel is from large-to-small. Right layout is the
161248
// easiest solution for that.
162249

163-
Extent<RANK> shape_scan;
164-
size_t numel = 1;
165-
for(int i = RANK; i > 0; --i)
166-
{
167-
shape_scan[i - 1] = numel;
168-
numel *= shape[i - 1];
169-
}
250+
NdIter iter(shape);
170251

171252
// Reset any errors from previous launches.
172253
(void)hipGetLastError();
173254

174-
kernel<<<occupancy * multiprocessors, block_size>>>(numel, shape_scan, f);
255+
kernel<<<occupancy * multiprocessors, block_size>>>(iter, f);
175256
check_hip(hipGetLastError());
176257
}
177258

178259
/// @brief Concept for tensor initializing functors.
179260
///
180261
/// This concept checks that a functor has the correct signature for
181262
/// use with the `fill_tensor` function.
182-
template <typename F, builder::DataType DT, size_t RANK>
263+
template <typename F, DataType DT, size_t RANK>
183264
concept FillTensorFunctor = requires(const F& f, const Extent<RANK>& index) {
184265
{ f(index) } -> std::convertible_to<detail::cpp_type_t<DT>>;
185266
};
@@ -199,7 +280,7 @@ concept FillTensorFunctor = requires(const F& f, const Extent<RANK>& index) {
199280
/// @param f A functor used to get the value at a particular coordinate.
200281
///
201282
/// @see FillTensorFunctor
202-
template <builder::DataType DT, size_t RANK>
283+
template <DataType DT, size_t RANK>
203284
void fill_tensor(const TensorDescriptor<DT, RANK>& desc,
204285
void* buffer,
205286
FillTensorFunctor<DT, RANK> auto f)
@@ -218,7 +299,7 @@ void fill_tensor(const TensorDescriptor<DT, RANK>& desc,
218299
///
219300
/// This concept checks that a functor has the correct signature for
220301
/// use with the `fill_tensor_buffer` function.
221-
template <typename F, builder::DataType DT>
302+
template <typename F, DataType DT>
222303
concept FillTensorBufferFunctor = requires(const F& f, size_t index) {
223304
{ f(index) } -> std::convertible_to<detail::cpp_type_t<DT>>;
224305
};
@@ -239,15 +320,27 @@ concept FillTensorBufferFunctor = requires(const F& f, size_t index) {
239320
/// @param f A functor used to get the value at a particular index.
240321
///
241322
/// @see FillTensorBufferFunctor
242-
template <builder::DataType DT, size_t RANK>
323+
template <DataType DT, size_t RANK>
243324
void fill_tensor_buffer(const TensorDescriptor<DT, RANK>& desc,
244325
void* buffer,
245326
FillTensorBufferFunctor<DT> auto f)
246327
{
247328
fill_tensor(desc.get_space_descriptor(), buffer, [f](auto index) { return f(index[0]); });
248329
}
249330

250-
template <builder::DataType DT, size_t RANK>
331+
/// @brief Utility for clearing tensor buffers to a particular value.
332+
///
333+
/// This function initializes all memory backing a particular tensor buffer to
334+
/// one specific value, zero by default. Note that this function ignores strides,
335+
/// and clears the entire buffer backing the tensor.
336+
///
337+
/// @tparam DT The tensor element datatype
338+
/// @tparam RANK The rank (number of spatial dimensions) of the tensor.
339+
///
340+
/// @param desc The descriptor of the tensor to initialize.
341+
/// @param buffer The memory of the tensor to initialize.
342+
/// @param value The value to initialize the tensor buffer with.
343+
template <DataType DT, size_t RANK>
251344
void clear_tensor_buffer(const TensorDescriptor<DT, RANK>& desc,
252345
void* buffer,
253346
detail::cpp_type_t<DT> value = detail::cpp_type_t<DT>{0})

experimental/builder/include/ck_tile/builder/testing/type_traits.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ constexpr size_t data_type_sizeof(DataType data_type)
3939
case DataType::FP8: return 1;
4040
case DataType::BF8: return 1;
4141
case DataType::FP64: return 8;
42-
case DataType::INT32: return 4;
42+
case DataType::I32: return 4;
4343
case DataType::I8: return 1;
4444
case DataType::I8_I8: return 2;
4545
case DataType::U8: return 1;

experimental/builder/include/ck_tile/builder/testing/validation.hpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
#include "ck_tile/builder/testing/tensor_buffer.hpp"
88
#include "ck_tile/builder/testing/tensor_foreach.hpp"
99
#include "ck_tile/builder/factory/helpers/ck/conv_tensor_type.hpp"
10-
#include "ck/library/utility/check_err.hpp"
1110
#include "ck/utility/type_convert.hpp"
1211
#include <string_view>
1312
#include <vector>

0 commit comments

Comments
 (0)