Skip to content

Commit c9cae14

Browse files
zhaojuanmaofacebook-github-bot
authored andcommitted
fix unflatten_dense_tensor when there is empty tensor inside (pytorch#50321)
Summary: Pull Request resolved: pytorch#50321 Quantization team reported that when there are two empty tensors are replicated among ranks, the two empty tensors start to share storage after resizing. The root cause is unflatten_dense_tensor unflattened the empty tensor as view of flat tensor and thus share storage with other tensors. This PR is trying to avoid unflatten the empty tensor as view of flat tensor so that empty tensor will not share storage with other tensors. Test Plan: unit test Reviewed By: pritamdamania87 Differential Revision: D25859503 fbshipit-source-id: 5b760b31af6ed2b66bb22954cba8d1514f389cca
1 parent e544d74 commit c9cae14

File tree

2 files changed

+49
-2
lines changed

2 files changed

+49
-2
lines changed

test/cpp/api/tensor_flatten.cpp

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#include <gtest/gtest.h>
2+
#include <test/cpp/api/support.h>
3+
4+
#include <torch/torch.h>
5+
#include <torch/csrc/utils/tensor_flatten.h>
6+
#include <torch/csrc/autograd/variable.h>
7+
8+
using namespace torch::test;
9+
10+
TEST(UnflattenDenseTensorTest, TestEmptyTensor) {
11+
auto emptyTensor1 = at::tensor(std::vector<int>());
12+
auto emptyTensor2 = at::tensor(std::vector<int>());
13+
auto tensor1 = at::tensor({1, 2, 3});
14+
auto tensor2 = at::tensor({4, 5});
15+
auto tensorList = std::vector<at::Tensor>({tensor1, emptyTensor1, emptyTensor2, tensor2});
16+
auto flatTensor = at::tensor({1, 2, 3, 4, 5});
17+
auto unflatten_results = torch::utils::unflatten_dense_tensors(flatTensor, tensorList);
18+
ASSERT_EQ(unflatten_results.size(), 4);
19+
ASSERT_EQ(unflatten_results.at(0).numel(), 3);
20+
ASSERT_EQ(unflatten_results.at(1).numel(), 0);
21+
ASSERT_EQ(unflatten_results.at(2).numel(), 0);
22+
ASSERT_EQ(unflatten_results.at(3).numel(), 2);
23+
24+
// empty tensor address is 0 as memory is not allocated yet
25+
ASSERT_EQ(unflatten_results.at(1).data_ptr(), nullptr);
26+
ASSERT_EQ(unflatten_results.at(2).data_ptr(), nullptr);
27+
// without fix in unflatten_dense_tensors() for empty tensors,
28+
// unflattend empty tensor unflatten_results.at(1) will share the same storage
29+
// as other non-empty tenosr like unflatten_results.at(3).
30+
// after fix, the empty tensor and non-empty tensor do not share the same
31+
// storage.
32+
ASSERT_NE(unflatten_results.at(1).data_ptr(), unflatten_results.at(3).data_ptr());
33+
unflatten_results.at(1).resize_(1);
34+
unflatten_results.at(2).resize_(1);
35+
// after resizing the two empty tensors, the resized tensors do not share
36+
// the same storage. without fix in unflatten_dense_tensors() for empty tensors,
37+
// the resized tensors will share the same storage.
38+
ASSERT_NE(unflatten_results.at(1).data_ptr(), unflatten_results.at(2).data_ptr());
39+
}

torch/csrc/utils/tensor_flatten.h

+10-2
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,16 @@ inline std::vector<at::Tensor> unflatten_dense_tensors(const at::Tensor& flat, a
2020
size_t offset = 0;
2121
for (const auto & tensor : tensors) {
2222
auto numel = tensor.numel();
23-
outputs.push_back(flat.narrow(0, offset, numel).view(tensor.sizes()));
24-
offset += numel;
23+
// If unflatten an empty tensor, create a new empty tensor using
24+
// flat tensor Options.
25+
// This can avoid the unflattened empty tensor to share the same storage
26+
// with other unflatten tensors.
27+
if (numel == 0) {
28+
outputs.push_back(at::empty({0}, flat.options()));
29+
} else {
30+
outputs.push_back(flat.narrow(0, offset, numel).view(tensor.sizes()));
31+
offset += numel;
32+
}
2533
}
2634
return outputs;
2735
}

0 commit comments

Comments
 (0)