Skip to content

Commit e63b441

Browse files
authored
[ET-VK][refactor] Introduce PackedDimInfo struct for packed dimension metadata (pytorch#16414)
## Context With the introduction of block-packed memory layouts for quantized tensors, the metadata stored by `vTensor` used to describe the data layout within a texture/buffer was no longer sufficient to form a complete description of the data layout. This created an awkward pattern of needing to estimate the `GPUMemoryLayout` which would be needed to compute storage descriptors such as image extents. This diff addresses the problem by introducing the `PackedDimInfo` struct to `vTensor`, which provides a complete description of how data in a tensor may be organized in the GPU buffer/texture used to store the tensor data, and allows simplification of the functions used to compute buffer numel or texture extents. ## `PackedDimInfo` Introduced the `PackedDimInfo` struct that encapsulates all information about packed dimensions in GPU tensors. This improves code organization and makes the relationship between related metadata fields explicit. The `PackedDimInfo` struct contains: - `packed_dim`: Which dimension is tightly packed (WHCN index) / contiguous in memory - `packed_dim_padded`: Whether the packed dimension is padded to multiple of 4; some layouts will do this to accomodate vectorized load/stores - `outer_packed_dim`: Second-level packing for block-packed layouts (4W4C, 4H4W); for layouts with only a single level of packing, will be equal to `packed_dim` - `outer_packed_dim_padded`: Whether outer packed dim is padded (tiled only) ## Changes - Added PackedDimInfo struct with helper function calculate_packed_dim_info() - Replaced packed_dim_ member with packed_dim_info_ in vTensor class - Updated function signatures to accept PackedDimInfo& instead of packed_dim_: * create_hashed_layout * calculate_dim_order * calculate_padded_sizes * calculate_logical_limits * TextureMetadata constructor/update * vTensorStorage constructor - Added packed_dim_info() accessor to vTensor and ComputeGraph classes - Store an additional `padded_sizes_` member in vTensor which is now used to for strides/image extents/GPU buffer numel computation, instead of using `sizes_` directly - Introduce memory layouts for kInt8x4 type that only use a single level of packing Differential Revision: [D89832382](https://our.internmc.facebook.com/intern/diff/D89832382/)
1 parent 45d442c commit e63b441

8 files changed

Lines changed: 933 additions & 330 deletions

File tree

‎backends/vulkan/runtime/api/containers/Tensor.cpp‎

Lines changed: 280 additions & 166 deletions
Large diffs are not rendered by default.

‎backends/vulkan/runtime/api/containers/Tensor.h‎

Lines changed: 63 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -22,48 +22,40 @@ namespace api {
2222
static constexpr size_t kTensorDimLimit = 8;
2323

2424
/*
25-
* Given a GPUMemoryLayout value, produce a dim order vector that matches the
26-
* given memory layout. The produced dim order vector will be in the NCHW
27-
* dimension order
25+
* PackedDimInfo encapsulates metadata about packed dimensions in GPU tensors.
26+
* This includes information about which dimension is packed, whether it's
27+
* padded, and block packing information for special layouts like 4W4C and 4H4W.
2828
*/
29-
std::vector<int64_t> calculate_dim_order(
30-
const size_t ndim,
31-
const int32_t packed_dim);
32-
33-
/*
34-
* Given the sizes of a tensor and the dim order of the tensor (both in NCHW)
35-
* dimension order, calculate the strides of the tensor.
36-
*/
37-
std::vector<int64_t> calculate_strides(
38-
const std::vector<int64_t>& sizes,
39-
const std::vector<int64_t>& dim_order);
40-
41-
/*
42-
* When stored on the GPU, tensor data is stored using texels (i.e. a vector of
43-
* 4 scalar values) in order to take advantage of the GPU's native vectorization
44-
* capabilities. Furthermore, tensor metadata is passed in to shaders as ivec4
45-
* types.
46-
*
47-
* To accommodate these vectorized types, the sizes of a tensor will be modified
48-
* for GPU storage in the following ways:
49-
*
50-
* 1. The dimensionality of the tensor will be padded to a multiple of 4.
51-
* 2. The size of the packed dimension will be padded to a multiple of 4.
52-
*
53-
* The "packed dimension" is determined based on the utils::GPUMemoryLayout
54-
* argument.
55-
*/
56-
std::vector<int64_t> calculate_padded_sizes(
57-
const std::vector<int64_t>& sizes,
58-
const int32_t packed_dim);
59-
60-
/*
61-
* Calculate the image extents required of a texture backed tensor.
62-
*/
63-
utils::uvec3 calculate_image_extents(
64-
const std::vector<int64_t>& padded_sizes,
65-
const std::vector<int64_t>& axis_map,
66-
const int32_t packed_dim);
29+
struct PackedDimInfo {
30+
// Describes which dimension is "tightly packed" using WHCN index (i.e. 0 for
31+
// width, 1 for height, etc.). For texture backed tensors, this describes
32+
// which dimension is packed along a texel. For buffer backed tensors, this
33+
// describes which dimension has a stride of 1 (i.e. is last in the dim
34+
// order).
35+
int32_t packed_dim;
36+
// Describes if the packed dimension is padded to a multiple of 4. This will
37+
// be true for all tensors that use texture storage, and will also be true
38+
// for the PACKED_PADDED memory layouts.
39+
bool packed_dim_padded;
40+
// Describes a second level of packing, if applicable (which will only apply
41+
// to the 4W4C and 4H4W layouts). If there is no second level of packing,
42+
// then this will be equal to packed_dim. Otherwise, it will represent the
43+
// outer dim used to construct block packing. For example, 4W4C will have
44+
// packed_dim = 2 and outer_packed_dim = 0.
45+
int32_t outer_packed_dim;
46+
// Whether the outer packed dim is padded to the next multiple of 4. This is
47+
// true only for block-packed layouts.
48+
bool outer_packed_dim_padded;
49+
// True if this layout uses block packing (i.e., outer_packed_dim !=
50+
// packed_dim). Block packing is used for layouts like 4W4C and 4H4W.
51+
bool is_block_packed;
52+
53+
PackedDimInfo(
54+
int32_t dim,
55+
bool dim_padded,
56+
int32_t outer_dim,
57+
bool outer_dim_padded);
58+
};
6759

6860
struct LastAccess {
6961
vkapi::PipelineStageFlags stage;
@@ -79,18 +71,6 @@ struct LastAccess {
7971
: stage{stage_flags}, access{access_flags} {}
8072
};
8173

82-
/*
83-
* Calculate the number of elements that a GPU buffer would require to store the
84-
* contents of a tensor. This will depend on the storage type and dtype of the
85-
* tensor, as well as the features available on the device.
86-
*/
87-
int64_t calculate_gpu_buffer_numel(
88-
Context* const context,
89-
const std::vector<int64_t>& sizes,
90-
const utils::uvec3 image_extents,
91-
const utils::StorageType storage_type,
92-
const vkapi::ScalarType dtype);
93-
9474
class vTensorStorage final {
9575
public:
9676
// Do not allow empty vTensorStorage construction
@@ -99,11 +79,11 @@ class vTensorStorage final {
9979
vTensorStorage(
10080
Context* context,
10181
const utils::StorageType storage_type,
102-
const utils::GPUMemoryLayout memory_layout,
10382
const std::vector<int64_t>& axis_map,
104-
const int32_t packed_dim,
105-
const std::vector<int64_t>& sizes,
83+
const PackedDimInfo& packed_dim_info,
84+
const std::vector<int64_t>& padded_sizes,
10685
const vkapi::ScalarType dtype,
86+
const int64_t physical_numel,
10787
const bool allocate_memory = true);
10888

10989
vTensorStorage(Context* const context, const vkapi::VulkanImage& image);
@@ -295,13 +275,13 @@ class vTensor final {
295275
const std::vector<int64_t>& sizes,
296276
const TextureLimits& logical_limits,
297277
const std::vector<int64_t>& axis_map,
298-
const int32_t packed_dim);
278+
const PackedDimInfo& packed_dim_info);
299279

300280
void update(
301281
const std::vector<int64_t>& sizes,
302282
const TextureLimits& logical_limits,
303283
const std::vector<int64_t>& axis_map,
304-
const int32_t packed_dim);
284+
const PackedDimInfo& packed_dim_info);
305285
};
306286

307287
private:
@@ -310,16 +290,14 @@ class vTensor final {
310290
* to construct a tensor.
311291
*/
312292

293+
// Information about packed dimension padding and block packing
294+
PackedDimInfo packed_dim_info_;
313295
// Whether the tensor has elements of type float, int, etc.
314296
vkapi::ScalarType dtype_;
315297
// sizes of the tensor in NCHW dimension order
316298
std::vector<int64_t> sizes_;
317-
// Describes which dimension is "tightly packed" using WHCN index (i.e. 0 for
318-
// width, 1 for height, etc.). For texture backed tensors, this describes
319-
// which dimension is packed along a texel. For buffer backed tensors, this
320-
// describes which dimension has a stride of 1 (i.e. is last in the dim
321-
// order).
322-
int32_t packed_dim_;
299+
// padded sizes of the tensor (pre-computed to avoid recalculation)
300+
std::vector<int64_t> padded_sizes_;
323301

324302
/*
325303
* "Layout" metadata. These describe with further detail how tensor data is
@@ -353,6 +331,10 @@ class vTensor final {
353331
// number of elements based on the canonical sizes
354332
size_t numel_;
355333

334+
// number of elements required for GPU buffer storage (with padding/packing)
335+
// This is pre-computed to avoid recomputing calculate_gpu_buffer_numel
336+
int64_t physical_numel_;
337+
356338
// For texture backed tensors, this int32 contains the axis map data packed
357339
// into a single int32. For buffer backed tensors, this int32 contains the
358340
// wchn dim order data packed into a single int32.
@@ -483,7 +465,11 @@ class vTensor final {
483465
utils::GPUMemoryLayout estimate_memory_layout() const;
484466

485467
inline int32_t packed_dim() const {
486-
return packed_dim_;
468+
return packed_dim_info_.packed_dim;
469+
}
470+
471+
inline const PackedDimInfo& packed_dim_info() const {
472+
return packed_dim_info_;
487473
}
488474

489475
/*
@@ -514,10 +500,22 @@ class vTensor final {
514500
return strides_;
515501
}
516502

503+
inline const std::vector<int64_t>& padded_sizes() const {
504+
return padded_sizes_;
505+
}
506+
517507
inline size_t numel() const {
518508
return numel_;
519509
}
520510

511+
inline int64_t physical_numel() const {
512+
return physical_numel_;
513+
}
514+
515+
inline utils::uvec3 image_extents() const {
516+
return storage_->image_extents_;
517+
}
518+
521519
inline size_t nbytes() const {
522520
return element_size(dtype()) * numel();
523521
}

‎backends/vulkan/runtime/graph/ComputeGraph.h‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,10 @@ class ComputeGraph final {
362362
return values_.at(idx).toConstTensor().staging_buffer_numel();
363363
}
364364

365+
inline int64_t physical_numel_of(const ValueRef idx) const {
366+
return values_.at(idx).toConstTensor().physical_numel();
367+
}
368+
365369
inline utils::StorageType storage_type_of(const ValueRef idx) const {
366370
return values_.at(idx).toConstTensor().storage_type();
367371
}
@@ -442,6 +446,11 @@ class ComputeGraph final {
442446
return values_.at(idx).toConstTensor().packed_dim();
443447
}
444448

449+
inline const api::PackedDimInfo& packed_dim_info_of(
450+
const ValueRef idx) const {
451+
return values_.at(idx).toConstTensor().packed_dim_info();
452+
}
453+
445454
inline int32_t concat_dim_of(const ValueRef idx) const {
446455
return values_.at(idx).toConstTensor().concat_dim();
447456
}

‎backends/vulkan/runtime/graph/ops/impl/Concat.cpp‎

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -108,17 +108,19 @@ utils::uvec3 concat_pick_global_wg_size(
108108
// Calculate what the image extents would be of a tensor with the input
109109
// volume's sizes. This produces the number of texels that would need to be
110110
// written to.
111-
const int32_t packed_dim = graph->packed_dim_of(out);
111+
112+
const int32_t packed_dim_idx = graph->packed_dim_of(out);
112113
std::vector<int64_t> inp_volume_texel_sizes =
113-
api::calculate_padded_sizes(inp_volume_sizes, packed_dim);
114+
api::flip_and_unsqueeze<int64_t>(inp_volume_sizes, api::kTensorSizes, 1);
115+
114116
// If the concat_dim is the same as the packed dim, and the concat_offset for
115117
// this input batch is not a multiple of 4, then the data from an input texel
116118
// may be split up between two output texels. For example:
117119
// I0 , I1 , I2 , I2
118120
// O0 , O1 , O2 , X | X , X , X , X
119121
// Therefore, 1 texel is added to the packed dim to account for this.
120-
inp_volume_texel_sizes.at(3 - packed_dim) =
121-
utils::div_up_4(inp_volume_texel_sizes.at(3 - packed_dim)) + 1;
122+
inp_volume_texel_sizes.at(packed_dim_idx) =
123+
utils::div_up_4(inp_volume_texel_sizes.at(packed_dim_idx)) + 1;
122124

123125
const uint32_t inp_volume_texel_numel =
124126
utils::multiply_integers(inp_volume_texel_sizes);
@@ -324,7 +326,7 @@ void add_concat_node(
324326
{1u, 1u, 1u},
325327
{1u, 1u, 1u},
326328
// Inputs and Outputs
327-
{{concat_offset, vkapi::kWrite}},
329+
{{concat_offset, vkapi::kReadWrite}},
328330
// Parameter buffers
329331
param_buffers,
330332
// Push Constants

‎backends/vulkan/runtime/graph/ops/impl/Tan.cpp‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,11 @@ void add_tan_node(ComputeGraph& graph, const ValueRef in, const ValueRef out) {
3434
add_storage_type_suffix(kernel_name, graph.storage_type_of(out));
3535

3636
vkapi::ParamsBindList ubos({});
37-
ubos.append({graph.logical_limits_ubo(out)});
37+
if (graph.storage_type_of(out) == utils::kBuffer) {
38+
ubos.append({graph.numel_ubo(out)});
39+
} else {
40+
ubos.append({graph.logical_limits_ubo(out)});
41+
}
3842

3943
graph.execute_nodes().emplace_back(new DynamicDispatchNode(
4044
graph,

‎backends/vulkan/runtime/utils/StorageUtils.cpp‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ namespace utils {
1313

1414
bool is_packed_int8_layout(const GPUMemoryLayout layout) {
1515
switch (layout) {
16+
case kPackedInt8_4W:
17+
case kPackedInt8_4C:
18+
case kPackedInt8_4H:
1619
case kPackedInt8_4W4C:
1720
case kPackedInt8_4H4W:
1821
return true;

‎backends/vulkan/runtime/utils/StorageUtils.h‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,14 @@ enum class GPUMemoryLayout : uint8_t {
101101
* 16 element block is loaded, rather than 4 elements along one dimension.
102102
*/
103103

104+
// "vector" packed layouts - single level of packing (4 elements along packed
105+
// dim per int32)
106+
TENSOR_PACKED_INT8_4W = 5u,
107+
TENSOR_PACKED_INT8_4C = 6u,
108+
TENSOR_PACKED_INT8_4H = 7u,
109+
110+
// Block packed layouts - two levels of packing (4x4 block composed of
111+
// elements from two packed dims per ivec4)
104112
TENSOR_PACKED_INT8_4W4C = 3u,
105113
TENSOR_PACKED_INT8_4H4W = 4u,
106114
};
@@ -114,6 +122,15 @@ static constexpr GPUMemoryLayout kHeightPacked =
114122
static constexpr GPUMemoryLayout kChannelsPacked =
115123
GPUMemoryLayout::TENSOR_CHANNELS_PACKED;
116124

125+
static constexpr GPUMemoryLayout kPackedInt8_4W =
126+
GPUMemoryLayout::TENSOR_PACKED_INT8_4W;
127+
128+
static constexpr GPUMemoryLayout kPackedInt8_4C =
129+
GPUMemoryLayout::TENSOR_PACKED_INT8_4C;
130+
131+
static constexpr GPUMemoryLayout kPackedInt8_4H =
132+
GPUMemoryLayout::TENSOR_PACKED_INT8_4H;
133+
117134
static constexpr GPUMemoryLayout kPackedInt8_4W4C =
118135
GPUMemoryLayout::TENSOR_PACKED_INT8_4W4C;
119136

@@ -129,6 +146,12 @@ T to_packed_dim(const GPUMemoryLayout layout) {
129146
return 1;
130147
case kChannelsPacked:
131148
return 2;
149+
case kPackedInt8_4W:
150+
return 0;
151+
case kPackedInt8_4C:
152+
return 2;
153+
case kPackedInt8_4H:
154+
return 1;
132155
case kPackedInt8_4W4C:
133156
return 2;
134157
case kPackedInt8_4H4W:
@@ -170,6 +193,15 @@ inline std::ostream& operator<<(
170193
case kChannelsPacked:
171194
os << "TENSOR_CHANNELS_PACKED";
172195
break;
196+
case kPackedInt8_4W:
197+
os << "TENSOR_PACKED_INT8_4W";
198+
break;
199+
case kPackedInt8_4C:
200+
os << "TENSOR_PACKED_INT8_4C";
201+
break;
202+
case kPackedInt8_4H:
203+
os << "TENSOR_PACKED_INT8_4H";
204+
break;
173205
case kPackedInt8_4W4C:
174206
os << "TENSOR_PACKED_INT8_4W4C";
175207
break;

0 commit comments

Comments
 (0)