1 /* Copyright 2015 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
16 #include "tensorflow/core/framework/function_testlib.h"
17
18 #include "tensorflow/core/framework/function.h"
19 #include "tensorflow/core/framework/node_def.pb.h"
20 #include "tensorflow/core/framework/tensor_testutil.h"
21 #include "tensorflow/core/framework/versions.pb.h"
22 #include "tensorflow/core/lib/core/threadpool.h"
23 #include "tensorflow/core/public/version.h"
24
25 namespace tensorflow {
26 namespace test {
27 namespace function {
28
29 typedef FunctionDefHelper FDH;
30
GDef(gtl::ArraySlice<NodeDef> nodes,gtl::ArraySlice<FunctionDef> funcs)31 GraphDef GDef(gtl::ArraySlice<NodeDef> nodes,
32 gtl::ArraySlice<FunctionDef> funcs) {
33 GraphDef g;
34 VersionDef* versions = g.mutable_versions();
35 versions->set_producer(TF_GRAPH_DEF_VERSION);
36 versions->set_min_consumer(TF_GRAPH_DEF_VERSION_MIN_CONSUMER);
37 for (const auto& n : nodes) {
38 *(g.add_node()) = n;
39 }
40 auto lib = g.mutable_library();
41 for (const auto& f : funcs) {
42 *(lib->add_function()) = f;
43 }
44 return g;
45 }
46
47 // Helper to construct a NodeDef.
NDef(StringPiece name,StringPiece op,gtl::ArraySlice<string> inputs,gtl::ArraySlice<std::pair<string,FDH::AttrValueWrapper>> attrs,const string & device)48 NodeDef NDef(StringPiece name, StringPiece op, gtl::ArraySlice<string> inputs,
49 gtl::ArraySlice<std::pair<string, FDH::AttrValueWrapper>> attrs,
50 const string& device) {
51 NodeDef n;
52 n.set_name(string(name));
53 n.set_op(string(op));
54 for (const auto& in : inputs) n.add_input(in);
55 n.set_device(device);
56 for (const auto& na : attrs)
57 n.mutable_attr()->insert({na.first, na.second.proto});
58 return n;
59 }
60
NonZero()61 FunctionDef NonZero() {
62 return FDH::Define(
63 // Name
64 "NonZero",
65 // Args
66 {"x:T"},
67 // Return values
68 {"y:T"},
69 // Attr def
70 {"T:{float, double, int32, int64, string}"},
71 // Nodes
72 {
73 {{"y"}, "Identity", {"x"}, {{"T", "$T"}}},
74 });
75 }
76
IsZero()77 FunctionDef IsZero() {
78 const Tensor kZero = test::AsScalar<int64_t>(0);
79 return FDH::Define(
80 // Name
81 "IsZero",
82 // Args
83 {"x: T"},
84 // Return values
85 {"equal: bool"},
86 // Attr def
87 {"T:{float, double, int32, int64, string}"},
88 {
89 {{"zero"}, "Const", {}, {{"value", kZero}, {"dtype", DT_INT64}}},
90 {{"cast"}, "Cast", {"zero"}, {{"SrcT", DT_INT64}, {"DstT", "$T"}}},
91 {{"equal"}, "Equal", {"x", "cast"}, {{"T", "$T"}}},
92 });
93 }
94
RandomUniform()95 FunctionDef RandomUniform() {
96 const Tensor kZero = test::AsScalar<int64_t>(0);
97
98 return FDH::Define(
99 // Name
100 "RandomUniformFn",
101 // Args
102 {"x: T"},
103 // Return values
104 {"random_uniform: int64"},
105 // Attr def
106 {"T:{float, double, int32, int64, string}"},
107 // NodeDef
108 {{{"random_uniform/shape"},
109 "Const",
110 {},
111 {{"value", kZero}, {"dtype", DT_INT64}}},
112 {{"random_uniform"},
113 "RandomUniform",
114 {"random_uniform/shape"},
115 {{"T", DT_INT32},
116 {"dtype", DT_FLOAT},
117 {"seed", 87654321},
118 {"seed2", 42}}}});
119 }
120
XTimesTwo()121 FunctionDef XTimesTwo() {
122 const Tensor kTwo = test::AsScalar<int64_t>(2);
123 return FDH::Define(
124 // Name
125 "XTimesTwo",
126 // Args
127 {"x: T"},
128 // Return values
129 {"y: T"},
130 // Attr def
131 {"T: {float, double, int32, int64}"},
132 // Nodes
133 {
134 {{"two"}, "Const", {}, {{"value", kTwo}, {"dtype", DT_INT64}}},
135 {{"scale"}, "Cast", {"two"}, {{"SrcT", DT_INT64}, {"DstT", "$T"}}},
136 {{"y"}, "Mul", {"x", "scale"}, {{"T", "$T"}}},
137 });
138 }
139
TwoDeviceMult()140 FunctionDef TwoDeviceMult() {
141 const Tensor kTwo = test::AsScalar<int64_t>(2);
142 const Tensor kThree = test::AsScalar<int64_t>(3);
143 return FDH::Create(
144 // Name
145 "TwoDeviceMult",
146 // Args
147 {"x: T"},
148 // Return values
149 {"y_cpu: T", "y_gpu: T"},
150 // Attr def
151 {"T: {float, double, int32, int64}"},
152 // Nodes
153 {
154 {{"num_2"}, "Const", {}, {{"value", kTwo}, {"dtype", DT_INT64}}},
155 {{"num_3"}, "Const", {}, {{"value", kThree}, {"dtype", DT_INT64}}},
156 {{"factor_2"},
157 "Cast",
158 {"num_2:output:0"},
159 {{"SrcT", DT_INT64}, {"DstT", "$T"}}},
160 {{"factor_3"},
161 "Cast",
162 {"num_3:output:0"},
163 {{"SrcT", DT_INT64}, {"DstT", "$T"}}},
164 {{"y_cpu"},
165 "Mul",
166 {"x", "factor_2:y:0"},
167 {{"T", "$T"}},
168 {},
169 "/device:CPU:0"},
170 {{"y_gpu"},
171 "Mul",
172 {"x", "factor_3:y:0"},
173 {{"T", "$T"}},
174 {},
175 "/device:GPU:0"},
176 },
177 {{"y_cpu", "y_cpu:z:0"}, {"y_gpu", "y_gpu:z:0"}});
178 }
179
TwoDeviceInputOutput()180 FunctionDef TwoDeviceInputOutput() {
181 const Tensor kTwo = test::AsScalar<float>(2);
182 const Tensor kThree = test::AsScalar<float>(3);
183 return FDH::Create(
184 // Name
185 "TwoDeviceInputOutput",
186 // Args
187 {"x1: T", "x2: T"},
188 // Return values
189 {"y_cpu: T", "y_gpu: T"},
190 // Attr def
191 {"T: {float}"},
192 // Nodes
193 {
194 {{"num_2"}, "Const", {}, {{"value", kTwo}, {"dtype", DT_FLOAT}}},
195 {{"num_3"}, "Const", {}, {{"value", kThree}, {"dtype", DT_FLOAT}}},
196 {{"y_cpu"},
197 "Mul",
198 {"x1", "num_2:output:0"},
199 {{"T", "$T"}},
200 {},
201 "/device:CPU:0"},
202 {{"y_gpu"},
203 "Mul",
204 {"x2", "num_3:output:0"},
205 {{"T", "$T"}},
206 {},
207 "/device:GPU:0"},
208 },
209 {{"y_cpu", "y_cpu:z:0"}, {"y_gpu", "y_gpu:z:0"}});
210 }
211
FuncWithListInput()212 FunctionDef FuncWithListInput() {
213 const Tensor kTwo = test::AsScalar<float>(2);
214 return FDH::Create(
215 // Name
216 "FuncWithListInput",
217 // Args
218 {"x1: N * T"},
219 // Return values
220 {},
221 // Attr def
222 {"T: {float}", "N: int >= 1"},
223 // Nodes
224 {
225 {{"num_2"}, "Const", {}, {{"value", kTwo}, {"dtype", DT_FLOAT}}},
226 },
227 {});
228 }
229
FuncWithListOutput()230 FunctionDef FuncWithListOutput() {
231 const Tensor kTwo = test::AsScalar<float>(2);
232 return FDH::Create(
233 // Name
234 "FuncWithListOutput",
235 // Args
236 {},
237 // Return values
238 {"y: N * T"},
239 // Attr def
240 {"T: {float}", "N: int >= 1"},
241 // Nodes
242 {
243 {{"num_2"}, "Const", {}, {{"value", kTwo}, {"dtype", DT_FLOAT}}},
244 },
245 {{"y", "num_2:output:0"}});
246 }
247
XAddX()248 FunctionDef XAddX() {
249 return FDH::Define(
250 // Name
251 "XAddX",
252 // Args
253 {"x: T"},
254 // Return values
255 {"y: T"},
256 // Attr def
257 {"T: {float, double, int32, int64}"},
258 // Nodes
259 {
260 {{"y"}, "Add", {"x", "x"}, {{"T", "$T"}}},
261 });
262 }
263
XAddY()264 FunctionDef XAddY() {
265 return FDH::Define(
266 // Name
267 "XAddY",
268 // Args
269 {"x: T", "y: T"},
270 // Return values
271 {"z: T"},
272 // Attr def
273 {"T: {float, double, int32, int64}"},
274 // Nodes
275 {
276 {{"z"}, "Add", {"x", "y"}, {{"T", "$T"}}},
277 });
278 }
279
XTimesTwoInt32()280 FunctionDef XTimesTwoInt32() {
281 const Tensor kTwo = test::AsScalar<int64_t>(2);
282 return FDH::Define(
283 // Name
284 "XTimesTwoInt32",
285 // Args
286 {"x: int32"},
287 // Return values
288 {"y: int32"}, {},
289 // Nodes
290 {
291 {{"two"}, "Const", {}, {{"value", kTwo}, {"dtype", DT_INT64}}},
292 {{"scale"},
293 "Cast",
294 {"two"},
295 {{"SrcT", DT_INT64}, {"DstT", DT_INT32}}},
296 {{"y"}, "Mul", {"x", "scale"}, {{"T", DT_INT32}}},
297 });
298 }
299
XTimesFour()300 FunctionDef XTimesFour() {
301 return FDH::Create(
302 // Name
303 "XTimesFour",
304 // Args
305 {"x: T"},
306 // Return values
307 {"y: T"},
308 // Attr def
309 {"T: {float, double, int32, int64}"},
310 // Nodes
311 {
312 {{"x2"}, "XTimesTwo", {"x"}, {{"T", "$T"}}},
313 {{"y"}, "XTimesTwo", {"x2:y:0"}, {{"T", "$T"}}},
314 },
315 {{"y", "y:y:0"}});
316 }
317
XTimes16()318 FunctionDef XTimes16() {
319 return FDH::Create(
320 // Name
321 "XTimes16",
322 // Args
323 {"x: T"},
324 // Return values
325 {"y: T"},
326 // Attr def
327 {"T: {float, double, int32, int64}"},
328 // Nodes
329 {
330 {{"x4"}, "XTimesFour", {"x"}, {{"T", "$T"}}},
331 {{"y"}, "XTimesFour", {"x4:y:0"}, {{"T", "$T"}}},
332 },
333 {{"y", "y:y:0"}});
334 }
335
WXPlusB()336 FunctionDef WXPlusB() {
337 return FDH::Define(
338 // Name
339 "WXPlusB",
340 // Args
341 {"w: T", "x: T", "b: T"},
342 // Return values
343 {"y: T"},
344 // Attr def
345 {"T: {float, double}"},
346 // Nodes
347 {{{"mm"},
348 "MatMul",
349 {"w", "x"},
350 {{"T", "$T"}, {"transpose_a", false}, {"transpose_b", false}}},
351 {{"y"}, "Add", {"mm", "b"}, {{"T", "$T"}}}});
352 }
353
Swap()354 FunctionDef Swap() {
355 return FDH::Define(
356 // Name
357 "Swap",
358 // Args
359 {"i0: T", "i1: T"},
360 // Return values
361 {"o0: T", "o1: T"},
362 // Attr def
363 {"T: {float, double, resource}"},
364 // Nodes
365 {{{"o0"}, "Identity", {"i1"}, {{"T", "$T"}}},
366 {{"o1"}, "Identity", {"i0"}, {{"T", "$T"}}}});
367 }
368
EmptyBodySwap()369 FunctionDef EmptyBodySwap() {
370 return FDH::Create(
371 // Name
372 "EmptyBodySwap",
373 // Args
374 {"i0: T", "i1: T"},
375 // Return values
376 {"o0: T", "o1: T"},
377 // Attr def
378 {"T: {float, double, resource}"},
379 // Nodes
380 {},
381 // Output mapping
382 {{"o0", "i1"}, {"o1", "i0"}});
383 }
384
ResourceOutput()385 FunctionDef ResourceOutput() {
386 const Tensor kTwo = test::AsScalar<float>(2);
387 return FDH::Create(
388 // Name
389 "ResourceOutput",
390 // Args
391 {"x: float", "y: resource"},
392 // Return values
393 {"y_out: resource", "two_x: float"},
394 // Attr def
395 {},
396 // Nodes
397 {
398 {{"two"}, "Const", {}, {{"value", kTwo}, {"dtype", DT_FLOAT}}},
399 {{"mul"}, "Mul", {"x", "two:output:0"}, {{"T", DT_FLOAT}}, {}},
400 },
401 {{"y_out", "y"}, {"two_x", "mul:z:0"}});
402 }
403
ResourceIdentity()404 FunctionDef ResourceIdentity() {
405 return FDH::Create(
406 // Name
407 "ResourceIdentity",
408 // Args
409 {"x: resource"},
410 // Return values
411 {"y: resource"},
412 // Attr def
413 {},
414 // Nodes
415 {},
416 // Output mapping
417 {{"y", "x"}});
418 }
419
ReadResourceVariable()420 FunctionDef ReadResourceVariable() {
421 return FDH::Create(
422 // Name
423 "ReadResourceVariable",
424 // Args
425 {"x: resource"},
426 // Return values
427 {"y: float"},
428 // Attr def
429 {},
430 // Nodes
431 {
432 {{"read"}, "ReadVariableOp", {"x"}, {{"dtype", DT_FLOAT}}, {}},
433 },
434 {{"y", "read:value:0"}});
435 }
436
ControlFlow()437 FunctionDef ControlFlow() {
438 return FDH::Create(
439 // Name
440 "ControlFlow",
441 // Args
442 {"i: float"},
443 // Return values
444 {"o: float"},
445 // Attr def
446 {},
447 // Nodes
448 {{{"enter"}, "Enter", {"i"}, {{"T", DT_FLOAT}, {"frame_name", "while"}}}},
449 // Output mapping
450 {{"o", "enter:output"}});
451 }
452
InvalidControlFlow()453 FunctionDef InvalidControlFlow() {
454 return FDH::Create(
455 // Name
456 "InvalidControlFlow",
457 // Args
458 {"i: int32"},
459 // Return values
460 {"o: int32"},
461 // Attr def
462 {},
463 // Nodes
464 {{{"enter"}, "Enter", {"i"}, {{"T", DT_INT32}, {"frame_name", "while"}}},
465 {{"add"}, "Add", {"enter:output", "i"}, {{"T", DT_INT32}}}},
466 // Output mapping
467 {{"o", "add:z"}});
468 }
469
LessThanOrEqualToN(int64_t N)470 FunctionDef LessThanOrEqualToN(int64_t N) {
471 const Tensor kN = test::AsScalar<int64_t>(N);
472 return FDH::Define(
473 // Name
474 "LessThanOrEqualToN",
475 // Args
476 {"x: T"},
477 // Return values
478 {"z: bool"},
479 // Attr def
480 {"T: {float, double, int32, int64}"},
481 // Nodes
482 {
483 {{"N"}, "Const", {}, {{"value", kN}, {"dtype", DT_INT64}}},
484 {{"y"}, "Cast", {"N"}, {{"SrcT", DT_INT64}, {"DstT", "$T"}}},
485 {{"z"}, "LessEqual", {"x", "y"}, {{"T", "$T"}}},
486 });
487 }
488
XPlusOneXTimesY()489 FunctionDef XPlusOneXTimesY() {
490 const Tensor kOne = test::AsScalar<int64_t>(1);
491 return FDH::Define(
492 // Name
493 "XPlusOneXTimesY",
494 // Args
495 {"x: T", "y: T"},
496 // Return values
497 {"s: T", "t: T"},
498 // Attr def
499 {"T: {float, double, int32, int64}"},
500 // Nodes
501 {{{"one"}, "Const", {}, {{"value", kOne}, {"dtype", DT_INT64}}},
502 {{"increment"}, "Cast", {"one"}, {{"SrcT", DT_INT64}, {"DstT", "$T"}}},
503 {{"s"}, "Add", {"x", "increment"}, {{"T", "$T"}}},
504 {{"t"}, "Mul", {"x", "y"}, {{"T", "$T"}}}});
505 }
506
XYXLessThanOrEqualToN(int64_t N)507 FunctionDef XYXLessThanOrEqualToN(int64_t N) {
508 const Tensor kN = test::AsScalar<int64_t>(N);
509 return FDH::Define(
510 // Name
511 "XYXLessThanOrEqualToN",
512 // Args
513 {"x: T", "y: T"},
514 // Return values
515 {"z: bool"},
516 // Attr def
517 {"T: {float, double, int32, int64}"},
518 // Nodes
519 {
520 {{"N"}, "Const", {}, {{"value", kN}, {"dtype", DT_INT64}}},
521 {{"N1"}, "Cast", {"N"}, {{"SrcT", DT_INT64}, {"DstT", "$T"}}},
522 {{"z"}, "LessEqual", {"x", "N1"}, {{"T", "$T"}}},
523 });
524 }
525
RandomUniformLess()526 FunctionDef RandomUniformLess() {
527 const Tensor kZero = test::AsScalar<int32>(0);
528 const Tensor kOne = test::AsScalar<int32>(1);
529 const Tensor k005 = test::AsScalar<float>(0.05);
530
531 return FDH::Define(
532 // Name
533 "RandomUniformLess",
534 // Args
535 {"arg0: int64"},
536 // Return values
537 {"strided_slice: bool"},
538 // Attr def
539 {"T:{float, double, int32, int64, string}"},
540 {{{"random_uniform/shape"},
541 "Const",
542 {},
543 {{"value", kZero}, {"dtype", DT_INT32}}},
544
545 {{"random_uniform/RandomUniform"},
546 "RandomUniform",
547 {"random_uniform/shape"},
548 {{"T", DT_INT32}, {"Tout", DT_FLOAT}, {"seed", 0}, {"seed2", 0}}},
549
550 {{"Less/y"}, "Const", {}, {{"value", k005}, {"dtype", DT_FLOAT}}},
551
552 {{"Less"},
553 "Less",
554 {"random_uniform/RandomUniform", "Less/y"},
555 {{"T", DT_FLOAT}}},
556
557 {{"strided_slice/stack"},
558 "Const",
559 {},
560 {{"value", kZero}, {"dtype", DT_INT32}}},
561
562 {{"strided_slice/stack_1"},
563 "Const",
564 {},
565 {{"value", kOne}, {"dtype", DT_INT32}}},
566
567 {{"strided_slice/stack_2"},
568 "Const",
569 {},
570 {{"value", kOne}, {"dtype", DT_INT32}}},
571
572 {{"strided_slice"},
573 "StridedSlice",
574 {"Less", "strided_slice/stack", "strided_slice/stack_1",
575 "strided_slice/stack_2"},
576 {{"Index", DT_INT32},
577 {"T", DT_BOOL},
578 {"begin_mask", 0},
579 {"ellipsis_mask", 0},
580 {"end_mask", 0},
581 {"new_axis_mask", 0},
582 {"shrink_axis_mask", 0}}}});
583 }
584
MakeRangeDataset()585 FunctionDef MakeRangeDataset() {
586 return FDH::Define(
587 /*name=*/"MakeRangeDataset",
588 /*arg_def=*/{"start: int64", "stop: int64", "step: int64"},
589 /*ret_def=*/{"y:variant"},
590 /*attr_def=*/
591 {"output_types: list(type) >= 1", "output_shapes: list(shape) >= 1"},
592 /*node_def=*/
593 {{/*ret=*/{"y"},
594 /*op=*/"RangeDataset",
595 /*arg=*/{"start", "stop", "step"},
596 /*attr=*/
597 {{"output_types", "$output_types"},
598 {"output_shapes", "$output_shapes"}}}});
599 }
600
MakeBatchDataset()601 FunctionDef MakeBatchDataset() {
602 return FDH::Define(
603 /*name=*/"MakeBatchDataset",
604 /*arg_def=*/
605 {"input_dataset: variant", "batch_size: int64", "drop_remainder: bool"},
606 /*ret_def=*/{"y: variant"},
607 /*attr_def=*/
608 {"parallel_copy: bool = false", "output_types: list(type) >= 1",
609 "output_shapes: list(shape) >= 1"},
610 /*node_def=*/
611 {{/*ret=*/{"y"},
612 /*op=*/"BatchDatasetV2",
613 /*arg=*/{"input_dataset", "batch_size", "drop_remainder"},
614 /*attr=*/
615 {{"parallel_copy", "$parallel_copy"},
616 {"output_types", "$output_types"},
617 {"output_shapes", "$output_shapes"}}}});
618 }
619
MakeMapDataset(bool has_other_args)620 FunctionDef MakeMapDataset(bool has_other_args) {
621 std::vector<string> args = {"input_dataset: variant"};
622 std::vector<string> inputs = {"input_dataset"};
623 if (has_other_args) {
624 args.emplace_back("other_arguments: Targuments");
625 inputs.emplace_back("other_arguments");
626 }
627
628 return FDH::Define(
629 /*name=*/"MakeMapDataset",
630 /*arg_def=*/args,
631 /*ret_def=*/
632 {"y: variant"},
633 /*attr_def=*/
634 {"f: func", "Targuments: list(type) >= 0",
635 "output_types: list(type) >= 1", "output_shapes: list(shape) >= 1",
636 "use_inter_op_parallelism: bool = true",
637 "preserve_cardinality: bool = false"},
638 /*node_def=*/
639 {{/*ret=*/{"y"},
640 /*op=*/"MapDataset",
641 /*arg=*/inputs,
642 /*attr=*/
643 {{"f", "$f"},
644 {"Targuments", "$Targuments"},
645 {"output_types", "$output_types"},
646 {"output_shapes", "$output_shapes"},
647 {"use_inter_op_parallelism", "$use_inter_op_parallelism"},
648 {"preserve_cardinality", "$preserve_cardinality"}}}});
649 }
650
MakeTakeDataset()651 FunctionDef MakeTakeDataset() {
652 return FDH::Define(
653 // Name
654 "TakeDataset",
655 // Args
656 {"input_dataset: variant", "count: int64"},
657 // Return values
658 {"y:variant"},
659 // Attr def
660 {"output_types: list(type) >= 1", "output_shapes: list(shape) >= 1"},
661 // Nodes
662 {{{"y"},
663 "TakeDataset",
664 {"input_dataset", "count"},
665 {{"output_types", "$output_types"},
666 {"output_shapes", "$output_shapes"}}}});
667 }
668
MakeTensorSliceDataset()669 FunctionDef MakeTensorSliceDataset() {
670 return FDH::Define(
671 // Name
672 "MakeTensorSliceDataset",
673 // Args
674 {"x: Toutput_types"},
675 // Return values
676 {"y: variant"},
677 // Attr def
678 {"Toutput_types: list(type) >= 1", "output_shapes: list(shape) >= 1"},
679 // Nodes
680 {{{"y"},
681 "TensorSliceDataset",
682 {"x"},
683 {{"Toutput_types", "$Toutput_types"},
684 {"output_shapes", "$output_shapes"}}}});
685 }
686
Unique()687 FunctionDef Unique() {
688 return FDH::Create(
689 // Name
690 "GetUnique",
691 // Args
692 {"x:T"},
693 // Return values
694 {"y:T", "idx: out_idx"},
695 // Attr def
696 {"T: type", "out_idx: {int32, int64} = DT_INT32"},
697 // Nodes
698 {
699 {{"result"}, "Unique", {"x"}, {{"T", "$T"}, {"out_idx", "$out_idx"}}},
700 },
701 {{"y", "result:y:0"}, {"idx", "result:idx:0"}});
702 }
703
FunctionTestSchedClosure(std::function<void ()> fn)704 void FunctionTestSchedClosure(std::function<void()> fn) {
705 static thread::ThreadPool* w =
706 new thread::ThreadPool(Env::Default(), "Test", 8);
707 w->Schedule(std::move(fn));
708 }
709
710 } // end namespace function
711 } // end namespace test
712 } // end namespace tensorflow
713