Speed up creation of tensors from compressed TensorProtos by 2-3x.
This should speed up some TF models optimized by Grappler in particular, since Grappler tries to compress all constants in a graph.
Run on XXXXX (72 X 2991 MHz CPUs); 2019-09-13T15:55:01.194485871-07:00
CPU: Intel Skylake Xeon with HyperThreading (36 cores) dL1:32KB dL2:1024KB dL3:24MB
Benchmark Base (ns) New (ns) Improvement
------------------------------------------------------------------
BM_FromProto/512 114 116 -1.8%
BM_FromProto/4k 692 671 +3.0%
BM_FromProto/32k 8675 8713 -0.4%
BM_FromProto/256k 183931 184131 -0.1%
BM_FromProto/1M 640952 638278 +0.4%
BM_FromProtoCompressed/512 215 118 +45.1%
BM_FromProtoCompressed/4k 1283 490 +61.8%
BM_FromProtoCompressed/32k 14115 8324 +41.0%
BM_FromProtoCompressed/256k 76930 32191 +58.2%
BM_FromProtoCompressed/1M 326284 170167 +47.8%
BM_FromProtoCompressedZero/512 215 119 +44.7%
BM_FromProtoCompressedZero/4k 1302 490 +62.4%
BM_FromProtoCompressedZero/32k 14333 8160 +43.1%
BM_FromProtoCompressedZero/256k 77032 32110 +58.3%
BM_FromProtoCompressedZero/1M 329943 171449 +48.0%
PiperOrigin-RevId: 269027674
diff --git a/tensorflow/core/framework/tensor.cc b/tensorflow/core/framework/tensor.cc
index b91c3f6..799945f 100644
--- a/tensorflow/core/framework/tensor.cc
+++ b/tensorflow/core/framework/tensor.cc
@@ -514,8 +514,13 @@
std::copy_n(begin, n, data);
} else {
std::copy_n(begin, in_n, data);
- const T& last = *(data + in_n - 1);
- std::fill_n(data + in_n, n - in_n, last);
+ if (std::is_trivially_copyable<T>::value) {
+ const T last = *(data + in_n - 1);
+ std::fill_n(data + in_n, n - in_n, last);
+ } else {
+ const T& last = *(data + in_n - 1);
+ std::fill_n(data + in_n, n - in_n, last);
+ }
}
}
@@ -648,14 +653,14 @@
}
void Tensor::CheckType(DataType expected_dtype) const {
- CHECK_EQ(dtype(), expected_dtype) << " "
- << DataTypeString(expected_dtype) << " expected, got "
+ CHECK_EQ(dtype(), expected_dtype)
+ << " " << DataTypeString(expected_dtype) << " expected, got "
<< DataTypeString(dtype());
}
void Tensor::CheckTypeAndIsAligned(DataType expected_dtype) const {
- CHECK_EQ(dtype(), expected_dtype) << " "
- << DataTypeString(expected_dtype) << " expected, got "
+ CHECK_EQ(dtype(), expected_dtype)
+ << " " << DataTypeString(expected_dtype) << " expected, got "
<< DataTypeString(dtype());
CHECK(IsAligned()) << "ptr = " << base<void>();
}
diff --git a/tensorflow/core/framework/tensor_test.cc b/tensorflow/core/framework/tensor_test.cc
index 01a0971..4658216 100644
--- a/tensorflow/core/framework/tensor_test.cc
+++ b/tensorflow/core/framework/tensor_test.cc
@@ -17,6 +17,7 @@
#include "tensorflow/core/framework/tensor.pb.h"
#include "tensorflow/core/framework/tensor_testutil.h"
+#include "tensorflow/core/framework/tensor_util.h"
#include "tensorflow/core/framework/types.h"
#include "tensorflow/core/framework/variant.h"
#include "tensorflow/core/framework/variant_encode_decode.h"
@@ -1518,5 +1519,59 @@
}
BENCHMARK(BM_CreateAndDestroyHostScalarOptimized);
+static void BM_FromProto(int iters, int size) {
+ testing::StopTiming();
+ TensorShape shape({size});
+ Allocator* allocator = cpu_allocator();
+ Tensor a(allocator, DT_FLOAT, shape);
+ std::fill_n(a.flat<float>().data(), size, 42.0);
+ TensorProto p;
+ a.AsProtoField(&p);
+ testing::StartTiming();
+ while (--iters) {
+ Tensor b;
+ ASSERT_TRUE(b.FromProto(p));
+ }
+ testing::StopTiming();
+}
+BENCHMARK(BM_FromProto)->Range(1, 1 << 20);
+
+static void BM_FromProtoCompressed(int iters, int size) {
+ testing::StopTiming();
+ TensorShape shape({size});
+ Allocator* allocator = cpu_allocator();
+ Tensor a(allocator, DT_FLOAT, shape);
+ std::fill_n(a.flat<float>().data(), size, 42.0f);
+ TensorProto p;
+ a.AsProtoField(&p);
+ tensor::CompressTensorProtoInPlace(&p);
+ testing::StartTiming();
+ while (--iters) {
+ Tensor b;
+ ASSERT_TRUE(b.FromProto(p));
+ }
+ testing::StopTiming();
+}
+BENCHMARK(BM_FromProtoCompressed)->Range(1, 1 << 20);
+
+static void BM_FromProtoCompressedZero(int iters, int size) {
+ testing::StopTiming();
+ TensorShape shape({size});
+ Allocator* allocator = cpu_allocator();
+ Tensor a(allocator, DT_FLOAT, shape);
+ std::fill_n(a.flat<float>().data(), size, 0);
+ a.flat<float>()(0) = 1;
+ TensorProto p;
+ a.AsProtoField(&p);
+ tensor::CompressTensorProtoInPlace(&p);
+ testing::StartTiming();
+ while (--iters) {
+ Tensor b;
+ ASSERT_TRUE(b.FromProto(p));
+ }
+ testing::StopTiming();
+}
+BENCHMARK(BM_FromProtoCompressedZero)->Range(1, 1 << 20);
+
} // namespace
} // namespace tensorflow
diff --git a/tensorflow/core/framework/tensor_util.cc b/tensorflow/core/framework/tensor_util.cc
index 896d83f..cff33ac 100644
--- a/tensorflow/core/framework/tensor_util.cc
+++ b/tensorflow/core/framework/tensor_util.cc
@@ -243,6 +243,12 @@
}
tensor->clear_tensor_content();
}
+ if (new_num_values == 1) {
+ const T value = TypeHelper::GetValue(0, *tensor);
+ if (value == T()) {
+ TypeHelper::Truncate(0, tensor);
+ }
+ }
return true;
}
@@ -287,7 +293,8 @@
last_index = i + 1;
}
}
- const int64 num_truncated_proto_values = last_index + 1;
+ const int64 num_truncated_proto_values =
+ (last_value == T() && last_index == 0) ? 0 : last_index + 1;
const int64 num_bytes_as_field =
num_truncated_proto_values * sizeof(FieldType);
const int64 num_bytes_as_tensor_content = num_tensor_values * sizeof(T);
diff --git a/tensorflow/core/framework/tensor_util_test.cc b/tensorflow/core/framework/tensor_util_test.cc
index fe98801..afb5b15 100644
--- a/tensorflow/core/framework/tensor_util_test.cc
+++ b/tensorflow/core/framework/tensor_util_test.cc
@@ -455,43 +455,83 @@
EXPECT_FALSE(tensor::CompressTensorProtoInPlace(&tensor_proto));
}
-TEST(TensorProtoUtil, CompressTensorProtoInPlaceAllEqual) {
+TEST(TensorProtoUtil, CompressTensorProtoInPlaceAllZero) {
const int kLength = 64;
TensorProto tensor_proto =
tensor::CreateTensorProto(std::vector<float>(kLength), {kLength});
EXPECT_TRUE(tensor::CompressTensorProtoInPlace(&tensor_proto));
EXPECT_EQ(tensor::internal::TensorProtoHelper<float>::NumValues(tensor_proto),
- 1);
+ 0);
tensor_proto =
tensor::CreateTensorProto(std::vector<int>(kLength), {kLength});
EXPECT_TRUE(tensor::CompressTensorProtoInPlace(&tensor_proto));
EXPECT_EQ(tensor::internal::TensorProtoHelper<int>::NumValues(tensor_proto),
- 1);
+ 0);
tensor_proto =
tensor::CreateTensorProto(std::vector<uint8>(kLength), {kLength});
EXPECT_TRUE(tensor::CompressTensorProtoInPlace(&tensor_proto));
EXPECT_EQ(tensor::internal::TensorProtoHelper<uint8>::NumValues(tensor_proto),
- 1);
+ 0);
tensor_proto =
tensor::CreateTensorProto(std::vector<bool>(kLength), {kLength});
EXPECT_TRUE(tensor::CompressTensorProtoInPlace(&tensor_proto));
EXPECT_EQ(tensor::internal::TensorProtoHelper<bool>::NumValues(tensor_proto),
- 1);
+ 0);
tensor_proto =
tensor::CreateTensorProto(std::vector<Eigen::half>(kLength), {kLength});
EXPECT_TRUE(tensor::CompressTensorProtoInPlace(&tensor_proto));
EXPECT_EQ(
tensor::internal::TensorProtoHelper<Eigen::half>::NumValues(tensor_proto),
- 1);
+ 0);
tensor_proto = tensor::CreateTensorProto(
std::vector<std::complex<float>>(kLength), {kLength});
EXPECT_TRUE(tensor::CompressTensorProtoInPlace(&tensor_proto));
EXPECT_EQ(tensor::internal::TensorProtoHelper<std::complex<float>>::NumValues(
tensor_proto),
+ 0);
+}
+
+TEST(TensorProtoUtil, CompressTensorProtoInPlaceAllOnes) {
+ const int kLength = 64;
+ TensorProto tensor_proto =
+ tensor::CreateTensorProto(std::vector<float>(kLength, 1), {kLength});
+ EXPECT_TRUE(tensor::CompressTensorProtoInPlace(&tensor_proto));
+ EXPECT_EQ(tensor::internal::TensorProtoHelper<float>::NumValues(tensor_proto),
+ 1);
+
+ tensor_proto =
+ tensor::CreateTensorProto(std::vector<int>(kLength, 1), {kLength});
+ EXPECT_TRUE(tensor::CompressTensorProtoInPlace(&tensor_proto));
+ EXPECT_EQ(tensor::internal::TensorProtoHelper<int>::NumValues(tensor_proto),
+ 1);
+
+ tensor_proto =
+ tensor::CreateTensorProto(std::vector<uint8>(kLength, 1), {kLength});
+ EXPECT_TRUE(tensor::CompressTensorProtoInPlace(&tensor_proto));
+ EXPECT_EQ(tensor::internal::TensorProtoHelper<uint8>::NumValues(tensor_proto),
+ 1);
+ tensor_proto =
+ tensor::CreateTensorProto(std::vector<bool>(kLength, true), {kLength});
+ EXPECT_TRUE(tensor::CompressTensorProtoInPlace(&tensor_proto));
+ EXPECT_EQ(tensor::internal::TensorProtoHelper<bool>::NumValues(tensor_proto),
+ 1);
+
+ tensor_proto = tensor::CreateTensorProto(
+ std::vector<Eigen::half>(kLength, Eigen::half(1.0)), {kLength});
+ EXPECT_TRUE(tensor::CompressTensorProtoInPlace(&tensor_proto));
+ EXPECT_EQ(
+ tensor::internal::TensorProtoHelper<Eigen::half>::NumValues(tensor_proto),
+ 1);
+
+ tensor_proto = tensor::CreateTensorProto(
+ std::vector<std::complex<float>>(kLength, 1), {kLength});
+ EXPECT_TRUE(tensor::CompressTensorProtoInPlace(&tensor_proto));
+ EXPECT_EQ(tensor::internal::TensorProtoHelper<std::complex<float>>::NumValues(
+ tensor_proto),
1);
}
diff --git a/tensorflow/core/grappler/optimizers/meta_optimizer_test.cc b/tensorflow/core/grappler/optimizers/meta_optimizer_test.cc
index 0b40363..42dd636 100644
--- a/tensorflow/core/grappler/optimizers/meta_optimizer_test.cc
+++ b/tensorflow/core/grappler/optimizers/meta_optimizer_test.cc
@@ -991,8 +991,7 @@
found_zeros = true;
EXPECT_EQ(node.op(), "Const");
const TensorProto& zeroes_t = node.attr().at("value").tensor();
- EXPECT_EQ(zeroes_t.float_val_size(), 1);
- EXPECT_EQ(zeroes_t.float_val(0), 0.0f);
+ EXPECT_EQ(zeroes_t.float_val_size(), 0);
} else if (node.name() == "host_ones") {
found_host_ones = true;
EXPECT_EQ(node.op(), "HostConst");