1 /* Copyright 2016 The TensorFlow Authors. All Rights Reserved.
2
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6
7 http://www.apache.org/licenses/LICENSE-2.0
8
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15 #include "tensorflow/core/kernels/immutable_constant_op.h"
16
17 #include <algorithm>
18 #include <tuple>
19
20 #include "tensorflow/cc/ops/standard_ops.h"
21 #include "tensorflow/core/framework/allocator.h"
22 #include "tensorflow/core/framework/tensor.h"
23 #include "tensorflow/core/graph/graph_def_builder.h"
24 #include "tensorflow/core/lib/core/status_test_util.h"
25 #include "tensorflow/core/lib/io/path.h"
26 #include "tensorflow/core/platform/null_file_system.h"
27 #include "tensorflow/core/platform/test.h"
28 #include "tensorflow/core/platform/test_benchmark.h"
29 #include "tensorflow/core/public/session.h"
30
31 namespace tensorflow {
32 namespace {
33 // A safe alignment that equal to memmapped page alignment on many modern
34 // architectures.
35 constexpr size_t kTestAlignment = 4096;
36 constexpr size_t kTestTensorSize = 4;
37 constexpr size_t kTestTensorSizeBytes = kTestTensorSize * sizeof(float);
38
39 // A test ReadOnlyMemoryRegion implementation.
40 class TestReadOnlyMemoryRegion : public ReadOnlyMemoryRegion {
41 public:
42 TestReadOnlyMemoryRegion() = delete;
TestReadOnlyMemoryRegion(uint64 length)43 explicit TestReadOnlyMemoryRegion(uint64 length)
44 : memptr_(cpu_allocator()->AllocateRaw(kTestAlignment, length)),
45 length_(length) {}
~TestReadOnlyMemoryRegion()46 ~TestReadOnlyMemoryRegion() override {
47 cpu_allocator()->DeallocateRaw(memptr_);
48 }
data()49 const void* data() override { return memptr_; }
GetWritableDataStart()50 float* GetWritableDataStart() { return reinterpret_cast<float*>(memptr_); }
length()51 uint64 length() override { return length_; }
52
53 protected:
54 void* memptr_;
55 uint64 length_;
56 };
57
58 // A mock file system and environment class that creates ReadOnlyMemoryRegion
59 // from allocated memory.
60 class TestFileSystem : public NullFileSystem {
61 public:
62 ~TestFileSystem() override = default;
63
64 // import non-transactional method from the base class
65 using NullFileSystem::NewReadOnlyMemoryRegionFromFile;
66
NewReadOnlyMemoryRegionFromFile(const string & fname,TransactionToken * token,std::unique_ptr<ReadOnlyMemoryRegion> * result)67 Status NewReadOnlyMemoryRegionFromFile(
68 const string& fname, TransactionToken* token,
69 std::unique_ptr<ReadOnlyMemoryRegion>* result) override {
70 float val = 0;
71 StringPiece scheme, host, path;
72 io::ParseURI(fname, &scheme, &host, &path);
73 // For the tests create in-memory regions with float values equal to the
74 // region name.
75 if (path == "/2") {
76 val = 2.0f;
77 } else if (path == "/3") {
78 val = 3.0f;
79 } else {
80 val = 0.0f;
81 }
82
83 auto region = new TestReadOnlyMemoryRegion(kTestTensorSizeBytes);
84 std::fill_n(region->GetWritableDataStart(), kTestTensorSize, val);
85 result->reset(region);
86 return OkStatus();
87 }
88 };
89
90 REGISTER_FILE_SYSTEM("test", TestFileSystem);
91
92 struct ImmutableConstantOpTest {};
93
TEST(ImmutableConstantOpTest,Simple)94 TEST(ImmutableConstantOpTest, Simple) {
95 const TensorShape kTestTensorShape({4, 1});
96 const TensorShape kTestTensorShapeT({1, 4});
97 auto root = Scope::NewRootScope().ExitOnError();
98 auto node1 =
99 ops::ImmutableConst(root, DT_FLOAT, kTestTensorShape, "test:///2");
100 auto node2 =
101 ops::ImmutableConst(root, DT_FLOAT, kTestTensorShapeT, "test:///3");
102 auto result = ops::MatMul(root, node1, node2);
103 GraphDef graph_def;
104 TF_ASSERT_OK(root.ToGraphDef(&graph_def));
105 SessionOptions session_options;
106 session_options.env = Env::Default();
107 session_options.config.mutable_graph_options()
108 ->mutable_optimizer_options()
109 ->set_opt_level(OptimizerOptions::L0);
110 std::unique_ptr<Session> session(NewSession(session_options));
111 ASSERT_TRUE(session != nullptr) << "Failed to create session";
112 TF_ASSERT_OK(session->Create(graph_def)) << "Can't create test graph";
113 std::vector<Tensor> outputs;
114 TF_ASSERT_OK(session->Run({}, {result.node()->name() + ":0"}, {}, &outputs));
115 ASSERT_EQ(outputs.size(), 1);
116 EXPECT_EQ(outputs.front().flat<float>()(0), 2.0f * 3.0f);
117 EXPECT_EQ(outputs.front().flat<float>()(1), 2.0f * 3.0f);
118 EXPECT_EQ(outputs.front().flat<float>()(2), 2.0f * 3.0f);
119 EXPECT_EQ(outputs.front().flat<float>()(kTestTensorSize - 1), 2.0f * 3.0f);
120 }
121
122 // Creates a test graph with two immutable_const tensors and a simple math
123 // operation, one of nodes has wrong size, check that error properly reported.
124
TEST(ImmutableConstantOpTest,ExecutionError)125 TEST(ImmutableConstantOpTest, ExecutionError) {
126 const TensorShape kBadTensorShape({40, 100});
127 const TensorShape kTestTensorShapeT({1, 4});
128
129 auto root = Scope::DisabledShapeInferenceScope().ExitOnError();
130 auto node1 =
131 ops::ImmutableConst(root, DT_FLOAT, kBadTensorShape, "test:///2");
132 auto node2 =
133 ops::ImmutableConst(root, DT_FLOAT, kTestTensorShapeT, "test:///3");
134 auto result = ops::MatMul(root, node1, node2);
135 GraphDef graph_def;
136 TF_ASSERT_OK(root.ToGraphDef(&graph_def));
137 SessionOptions session_options;
138 session_options.env = Env::Default();
139 std::unique_ptr<Session> session(NewSession(session_options));
140 ASSERT_TRUE(session != nullptr) << "Failed to create session";
141 TF_ASSERT_OK(session->Create(graph_def)) << "Can't create test graph";
142 std::vector<Tensor> outputs;
143 // Check that the run returned error.
144 EXPECT_EQ(
145 session->Run({}, {result.node()->name() + ":0"}, {}, &outputs).code(),
146 error::INTERNAL);
147 }
148
CreateTempFileFloat(Env * env,float value,uint64 size,string * filename)149 Status CreateTempFileFloat(Env* env, float value, uint64 size,
150 string* filename) {
151 const string dir = testing::TmpDir();
152 *filename = io::JoinPath(dir, strings::StrCat("file_", value));
153 std::unique_ptr<WritableFile> file;
154 TF_RETURN_IF_ERROR(env->NewWritableFile(*filename, &file));
155 for (uint64 i = 0; i < size; ++i) {
156 StringPiece sp(static_cast<char*>(static_cast<void*>(&value)),
157 sizeof(value));
158 TF_RETURN_IF_ERROR(file->Append(sp));
159 }
160 TF_RETURN_IF_ERROR(file->Close());
161 return OkStatus();
162 }
163
TEST(ImmutableConstantOpTest,FromFile)164 TEST(ImmutableConstantOpTest, FromFile) {
165 const TensorShape kFileTensorShape({1000, 1});
166 Env* env = Env::Default();
167 auto root = Scope::NewRootScope().ExitOnError();
168
169 string two_file, three_file;
170 TF_ASSERT_OK(CreateTempFileFloat(env, 2.0f, 1000, &two_file));
171 TF_ASSERT_OK(CreateTempFileFloat(env, 3.0f, 1000, &three_file));
172 auto node1 = ops::ImmutableConst(root, DT_FLOAT, kFileTensorShape, two_file);
173 auto node2 =
174 ops::ImmutableConst(root, DT_FLOAT, kFileTensorShape, three_file);
175 auto result = ops::MatMul(root, node1, node2, ops::MatMul::TransposeB(true));
176
177 GraphDef graph_def;
178 TF_ASSERT_OK(root.ToGraphDef(&graph_def));
179 SessionOptions session_options;
180 session_options.config.mutable_graph_options()
181 ->mutable_optimizer_options()
182 ->set_opt_level(OptimizerOptions::L0);
183 std::unique_ptr<Session> session(NewSession(session_options));
184 ASSERT_TRUE(session != nullptr) << "Failed to create session";
185 TF_ASSERT_OK(session->Create(graph_def)) << "Can't create test graph";
186 std::vector<Tensor> outputs;
187 TF_ASSERT_OK(session->Run({}, {result.node()->name() + ":0"}, {}, &outputs));
188 ASSERT_EQ(outputs.size(), 1);
189 EXPECT_EQ(outputs.front().flat<float>()(0), 2.0f * 3.0f);
190 EXPECT_EQ(outputs.front().flat<float>()(1), 2.0f * 3.0f);
191 EXPECT_EQ(outputs.front().flat<float>()(2), 2.0f * 3.0f);
192 }
193
CreateTempFileBadString(Env * env,char value,uint64 size,const string suffix,string * filename)194 Status CreateTempFileBadString(Env* env, char value, uint64 size,
195 const string suffix, string* filename) {
196 const string dir = testing::TmpDir();
197 *filename = io::JoinPath(dir, strings::StrCat("file_", suffix));
198 std::unique_ptr<WritableFile> file;
199 TF_RETURN_IF_ERROR(env->NewWritableFile(*filename, &file));
200 TF_RETURN_IF_ERROR(file->Append(std::string(size, value)));
201 TF_RETURN_IF_ERROR(file->Close());
202 return OkStatus();
203 }
204
TEST(ImmutableConstantOpTest,FromFileStringUnimplmented)205 TEST(ImmutableConstantOpTest, FromFileStringUnimplmented) {
206 const TensorShape kFileTensorShape({1});
207 Env* env = Env::Default();
208 auto root = Scope::NewRootScope().ExitOnError();
209
210 string bad_file;
211 TF_ASSERT_OK(CreateTempFileBadString(env, '\xe2', 128, "bad_e2", &bad_file));
212 auto result =
213 ops::ImmutableConst(root, DT_STRING, kFileTensorShape, bad_file);
214 GraphDef graph_def;
215 TF_ASSERT_OK(root.ToGraphDef(&graph_def));
216 SessionOptions session_options;
217 session_options.env = Env::Default();
218 std::unique_ptr<Session> session(NewSession(session_options));
219 ASSERT_TRUE(session != nullptr) << "Failed to create session";
220 TF_ASSERT_OK(session->Create(graph_def)) << "Can't create test graph";
221 std::vector<Tensor> outputs;
222 // Check that the run returned error.
223 EXPECT_EQ(
224 session->Run({}, {result.node()->name() + ":0"}, {}, &outputs).code(),
225 error::UNIMPLEMENTED);
226 }
227
228 } // namespace
229 } // namespace tensorflow
230