Skip to content

Commit cae3713

Browse files
Zongheng Yangtensorflower-gardener
authored andcommitted
tensor_bundle: tighten Lookup() semantics, add LookupDtypeShape().
This fixes a subtle allocation issue in CheckpointReader. Change: 135503615
1 parent 2c2f81a commit cae3713

4 files changed

Lines changed: 30 additions & 18 deletions

File tree

tensorflow/c/checkpoint_reader.cc

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,14 @@ void CheckpointReader::GetTensor(
8181
if (reader_ != nullptr) {
8282
status = reader_->GetTensor(name, out_tensor);
8383
} else {
84-
std::unique_ptr<Tensor> tensor(new Tensor);
85-
status = v2_reader_->Lookup(name, tensor.get());
86-
if (status.ok()) std::swap(*out_tensor, tensor);
84+
tensorflow::DataType dtype;
85+
tensorflow::TensorShape shape;
86+
status = v2_reader_->LookupDtypeAndShape(name, &dtype, &shape);
87+
if (status.ok()) {
88+
out_tensor->reset(new Tensor(dtype, shape));
89+
status = v2_reader_->Lookup(name, out_tensor->get());
90+
if (!status.ok()) out_tensor->reset();
91+
}
8792
}
8893
if (!status.ok()) {
8994
Set_TF_Status_from_Status(out_status, status);

tensorflow/core/util/tensor_bundle/tensor_bundle.cc

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -745,11 +745,10 @@ Status BundleReader::GetSliceValue(StringPiece full_tensor_key,
745745
// allocate_temp()? Note that without major refactorings to Saver, it's
746746
// hard for the caller of the tensor bundle module to allocate these
747747
// precisely-shaped scratch storage.
748-
// TODO(zongheng): implement an important optimization: if the stored slice
749-
// is a subset of the to-restore slice, directly read the stored slice into
750-
// the latter's already-allocated backing buffer.
751748

752749
// Optimization for the common case: stored slice == to-restore slice.
750+
// TODO(zongheng): also include the case where "slice_spec" is full ("-"),
751+
// and "stored_slice" is logically full but contains actual extents.
753752
if (stored_slice == slice_spec) {
754753
VLOG(1) << "Optimized for common case: directly copying into "
755754
"pre-allocated buffer; spec: "
@@ -800,14 +799,20 @@ bool BundleReader::Contains(StringPiece key) {
800799
return Valid() && (this->key() == key);
801800
}
802801

803-
Status BundleReader::LookupTensorShape(StringPiece key, TensorShape* shape) {
802+
Status BundleReader::LookupDtypeAndShape(StringPiece key, DataType* dtype,
803+
TensorShape* shape) {
804804
BundleEntryProto entry;
805805
TF_RETURN_IF_ERROR(GetBundleEntryProto(key, &entry));
806-
806+
*dtype = entry.dtype();
807807
*shape = TensorShape(entry.shape());
808808
return Status::OK();
809809
}
810810

811+
Status BundleReader::LookupTensorShape(StringPiece key, TensorShape* shape) {
812+
DataType ignored;
813+
return LookupDtypeAndShape(key, &ignored, shape);
814+
}
815+
811816
string BundleReader::DebugString() {
812817
// Format used below emulates that of TensorSliceReader::DebugString().
813818
string shape_str;

tensorflow/core/util/tensor_bundle/tensor_bundle.h

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,11 @@ class BundleReader {
179179
// REQUIRES: status().ok()
180180
bool Contains(StringPiece key);
181181

182+
// Looks up the dtype and the shape of the tensor keyed by "key".
183+
// REQUIRES: status().ok()
184+
Status LookupDtypeAndShape(StringPiece key, DataType* dtype,
185+
TensorShape* shape) TF_MUST_USE_RESULT;
186+
182187
// Looks up the shape of the tensor keyed by "key".
183188
// Clears "shape" if not found.
184189
// REQUIRES: status().ok()
@@ -188,14 +193,9 @@ class BundleReader {
188193
// Looks up the tensor keyed by "key". If "key" refers to a partitioned
189194
// tensor, attempts to look up the full contents using all stored slices.
190195
//
191-
// Out-tensor "val" can be either empty or initialized with a non-empty shape:
192-
//
193-
// * If empty, this function allocates an exactly-sized Tensor to hold the
194-
// contents found in this bundle.
195-
//
196-
// * If non-empty, caller is responsible for making sure "val" has the same
197-
// shape as the corresponding contents. This function directly uses the
198-
// buffer without extra allocation.
196+
// Caller must make sure "val" has the same shape and dtype as the
197+
// corresponding contents, so that its buffer can be filled without needing
198+
// extra allocation. These can be queried via "LookupDtypeAndShape()".
199199
//
200200
// On error, "val" may contain nonsense data. Returns a NotFound error if
201201
// tensor keyed by "key" does not exist in this bundle.

tensorflow/core/util/tensor_bundle/tensor_bundle_test.cc

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,11 @@ void Expect(BundleReader* reader, const string& key,
5252
const Tensor& expected_val) {
5353
// Tests for Contains().
5454
EXPECT_TRUE(reader->Contains(key));
55-
// Tests for LookupTensorShape().
55+
// Tests for LookupDtypeAndShape().
56+
DataType dtype;
5657
TensorShape shape;
57-
TF_ASSERT_OK(reader->LookupTensorShape(key, &shape));
58+
TF_ASSERT_OK(reader->LookupDtypeAndShape(key, &dtype, &shape));
59+
EXPECT_EQ(expected_val.dtype(), dtype);
5860
EXPECT_EQ(expected_val.shape(), shape);
5961
// Tests for Lookup(), checking tensor contents.
6062
Tensor val(expected_val.dtype(), shape);

0 commit comments

Comments
 (0)