Skip to content

Commit 5c70121

Browse files
SS-JIAfacebook-github-bot
authored andcommitted
Enable zero-size tensors (#3640)
Summary: Pull Request resolved: #3640 As title. The approach is slightly different than in PyTorch Vulkan. Instead of binding no memory, we make a small allocation. The reason for this change is to account for the possibility that some zero size tensors are used as input but the output is not zero size. In that case we still need to be able to bind the zero size tensor to a shader. Reviewed By: yipjustin Differential Revision: D57450473 fbshipit-source-id: 753e0dedf40f4a6ee6153980159fce34b8c41b5e
1 parent c7104db commit 5c70121

File tree

5 files changed

+66
-5
lines changed

5 files changed

+66
-5
lines changed

backends/vulkan/runtime/api/memory/Buffer.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,16 +38,18 @@ VulkanBuffer::VulkanBuffer(
3838
memory_{},
3939
owns_memory_(allocate_memory),
4040
handle_(VK_NULL_HANDLE) {
41-
// Only allocate memory if the buffer has non-zero size
41+
// If the buffer size is 0, allocate a buffer with a size of 1 byte. This is
42+
// to ensure that there will be some resource that can be bound to a shader.
4243
if (size == 0) {
43-
return;
44+
buffer_properties_.size = 1u;
45+
buffer_properties_.mem_range = 1u;
4446
}
4547

4648
const VkBufferCreateInfo buffer_create_info{
4749
VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // sType
4850
nullptr, // pNext
4951
0u, // flags
50-
size, // size
52+
buffer_properties_.size, // size
5153
buffer_properties_.buffer_usage, // usage
5254
VK_SHARING_MODE_EXCLUSIVE, // sharingMode
5355
0u, // queueFamilyIndexCount

backends/vulkan/runtime/api/memory/Image.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,11 +129,14 @@ VulkanImage::VulkanImage(
129129
VmaAllocatorInfo allocator_info{};
130130
vmaGetAllocatorInfo(allocator_, &allocator_info);
131131

132-
// If any dims are zero, then no memory will be allocated for the image.
132+
// If any dims are zero, then allocate a 1x1x1 image texture. This is to
133+
// ensure that there will be some resource that can be bound to a shader.
133134
if (image_props.image_extents.width == 0 ||
134135
image_props.image_extents.height == 0 ||
135136
image_props.image_extents.depth == 0) {
136-
return;
137+
image_properties_.image_extents.width = 1u;
138+
image_properties_.image_extents.height = 1u;
139+
image_properties_.image_extents.depth = 1u;
137140
}
138141

139142
const VkImageCreateInfo image_create_info{

backends/vulkan/test/op_tests/cases.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,7 @@ def get_cat_inputs():
490490
test_suite = VkTestSuite(
491491
[
492492
# Cat on Height
493+
([(S1, S1, 3, 5), (S1, S1, 0, 5)], 2),
493494
([(S1, S1, 3, 5), (S1, S1, 4, 5)], 2),
494495
([(S1, 3, 5), (S1, 4, 5)], 1),
495496
([(3, 5), (4, 5)], 0),
@@ -501,6 +502,7 @@ def get_cat_inputs():
501502
# Cat on Width
502503
([(S1, S1, 5, 3), (S1, S1, 5, 4)], 3),
503504
([(S1, 5, 3), (S1, 5, 4)], 2),
505+
([(5, 0), (5, 4)], 1),
504506
([(5, 3), (5, 4)], 1),
505507
([(5, 3), (5, 4), (5, 1)], 1),
506508
(
@@ -521,6 +523,7 @@ def get_cat_inputs():
521523
0,
522524
),
523525
# Cat on Channel
526+
([(S, 5, 4), (0, 5, 4), (S2, 5, 4)], 0),
524527
([(S, 5, 4), (S1, 5, 4), (S2, 5, 4)], 0),
525528
([(XS, 5, 4), (XS, 5, 4), (S2, 5, 4)], 0),
526529
([(XS, S, 5, 4), (XS, S1, 5, 4), (XS, S2, 5, 4)], 1),

backends/vulkan/test/test_vulkan_delegate.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -929,6 +929,27 @@ def forward(self, x, y, z, w):
929929
memory_layouts=[vk_graph_schema.VkMemoryLayout.TENSOR_CHANNELS_PACKED],
930930
)
931931

932+
def test_vulkan_backend_cat_with_zero_size(self):
933+
class TestModule(torch.nn.Module):
934+
def __init__(self):
935+
super().__init__()
936+
937+
def forward(self, x, y, z, w):
938+
return torch.cat([x, y, z, w], dim=1)
939+
940+
sample_inputs = (
941+
torch.randn(size=(3, 6, 2, 7), dtype=torch.float32),
942+
torch.randn(size=(3, 0, 2, 7), dtype=torch.float32),
943+
torch.randn(size=(3, 0, 2, 7), dtype=torch.float32),
944+
torch.randn(size=(3, 3, 2, 7), dtype=torch.float32),
945+
)
946+
947+
self.lower_module_and_test_output(
948+
TestModule(),
949+
sample_inputs,
950+
memory_layouts=[vk_graph_schema.VkMemoryLayout.TENSOR_CHANNELS_PACKED],
951+
)
952+
932953
def test_vulkan_backend_slice(self):
933954
class TestModule(torch.nn.Module):
934955
def __init__(self):

backends/vulkan/test/vulkan_compute_api_test.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,38 @@ TEST_F(VulkanComputeAPITest, test_buffer_int8) {
259259
test_storage_buffer_type<int8_t, api::kQInt8>(16);
260260
}
261261

262+
TEST_F(VulkanComputeAPITest, test_zero_size_tensor) {
263+
// Simple test that performs a + b -> c
264+
265+
std::vector<int64_t> sizes = {0, 5, 7};
266+
vTensor a = CREATE_FLOAT_TEXTURE(sizes, /*allocate_memory = */ true);
267+
vTensor b = CREATE_FLOAT_TEXTURE(sizes, /*allocate_memory = */ true);
268+
vTensor c = CREATE_FLOAT_TEXTURE(sizes, /*allocate_memory = */ true);
269+
270+
// Fill input tensors
271+
fill_vtensor(a, 2.5f);
272+
fill_vtensor(b, 1.5f);
273+
274+
// a + b -> c
275+
record_binary_op(api::context(), "add", a, b, c);
276+
277+
// Extract output tensor
278+
std::vector<float> data_out = extract_vtensor(c);
279+
280+
// Assert all tensors are empty
281+
ASSERT_TRUE(a.numel() == 0);
282+
ASSERT_TRUE(b.numel() == 0);
283+
ASSERT_TRUE(c.numel() == 0);
284+
ASSERT_TRUE(a.nbytes() == 0);
285+
ASSERT_TRUE(b.nbytes() == 0);
286+
ASSERT_TRUE(c.nbytes() == 0);
287+
288+
// Check output
289+
for (size_t i = 0; i < data_out.size(); ++i) {
290+
CHECK_VALUE(data_out, i, 4.0f);
291+
}
292+
}
293+
262294
TEST_F(VulkanComputeAPITest, texture_add_sanity_check) {
263295
// Simple test that performs a + b -> c
264296

0 commit comments

Comments
 (0)