aboutsummaryrefslogtreecommitdiffhomepage
path: root/tensorflow/compiler
diff options
context:
space:
mode:
authorGravatar A. Unique TensorFlower <gardener@tensorflow.org>2018-10-03 13:22:52 -0700
committerGravatar TensorFlower Gardener <gardener@tensorflow.org>2018-10-03 13:27:54 -0700
commit7566f3d5ad690c71c36e78611b1ae5913ec3e845 (patch)
tree04928655aeb640ef86d49c81e04242ade199ae40 /tensorflow/compiler
parent808b1dcb318b1feb5a8c9fed5558f95cd05728e4 (diff)
Fix handling of tuples in CreateCopyWithNewLayout.
If the layout of a single tensor in a tuple is different from its use, then CreateCopyWithNewLayout will do a deep copy of the entire tuple. Not only does this operation create unnecessary copies of elements where the layout is the same, it will throw an error if the tuple contains elements like token[] that cannot be copied. As a result, layout assignment on TPU occassionally causes mysterious compilation failures for code that runs correctly on CPU and GPU. PiperOrigin-RevId: 215615731
Diffstat (limited to 'tensorflow/compiler')
-rw-r--r--tensorflow/compiler/xla/service/layout_assignment.cc28
-rw-r--r--tensorflow/compiler/xla/service/layout_assignment_test.cc59
2 files changed, 76 insertions, 11 deletions
diff --git a/tensorflow/compiler/xla/service/layout_assignment.cc b/tensorflow/compiler/xla/service/layout_assignment.cc
index 68a08a0886..cc4a342e9d 100644
--- a/tensorflow/compiler/xla/service/layout_assignment.cc
+++ b/tensorflow/compiler/xla/service/layout_assignment.cc
@@ -792,21 +792,27 @@ StatusOr<HloInstruction*> LayoutAssignment::CreateCopyWithNewLayout(
<< " instruction: " << instruction->ToString();
if (ShapeUtil::IsTuple(instruction->shape())) {
- // Deep-copy tuples.
+ // Copy tuple elements which have differing layouts.
std::vector<HloInstruction*> element_copies;
for (int64 i = 0; i < ShapeUtil::TupleElementCount(instruction->shape());
++i) {
+ const Shape& target_shape =
+ ShapeUtil::GetSubshape(shape_with_layout, {i});
+ const Shape& instr_shape =
+ ShapeUtil::GetSubshape(instruction->shape(), {i});
HloInstruction* gte = instruction->parent()->AddInstruction(
- HloInstruction::CreateGetTupleElement(
- ShapeUtil::GetSubshape(instruction->shape(), {i}), instruction,
- i));
- SetupCopiedInstruction(*instruction, gte, {i});
- // Recurse to copy each elements.
- TF_ASSIGN_OR_RETURN(
- HloInstruction * element_copy,
- CreateCopyWithNewLayout(
- ShapeUtil::GetSubshape(shape_with_layout, {i}), gte));
- element_copies.push_back(element_copy);
+ HloInstruction::CreateGetTupleElement(instr_shape, instruction, i));
+
+ if (ShapeUtil::Equal(target_shape, instr_shape)) {
+ // Shapes and layouts are equal, no need to copy.
+ element_copies.push_back(gte);
+ } else {
+ SetupCopiedInstruction(*instruction, gte, {i});
+ // Recurse to copy each element.
+ TF_ASSIGN_OR_RETURN(HloInstruction * element_copy,
+ CreateCopyWithNewLayout(target_shape, gte));
+ element_copies.push_back(element_copy);
+ }
}
// Gather element copies into a tuple with a new Tuple instruction.
HloInstruction* tuple_copy = instruction->parent()->AddInstruction(
diff --git a/tensorflow/compiler/xla/service/layout_assignment_test.cc b/tensorflow/compiler/xla/service/layout_assignment_test.cc
index 15c16d667c..2c549cd872 100644
--- a/tensorflow/compiler/xla/service/layout_assignment_test.cc
+++ b/tensorflow/compiler/xla/service/layout_assignment_test.cc
@@ -1043,5 +1043,64 @@ TEST_F(LayoutAssignmentTest, PropagatingLayoutFromResultToOperand) {
op::ShapeWithLayout(shape_copy))));
}
+TEST_F(LayoutAssignmentTest, TupleCopyOnLayoutMismatch) {
+ // The first infeed uses layout {0,1}, while the second uses layout {1,0}.
+ // The mismatch forces a copy of the tuple. The tuple contains a token, so
+ // layout assignment will fail if it tries to copy the whole tuple.
+ const char* module_str = R"(
+ HloModule TupleCopyOnLayoutMismatch
+
+ condition.1 (tup: (s32[], token[], f32[512,1024]{0,1})) -> pred[] {
+ tup.1 = (s32[], token[], f32[512,1024]{0,1}) parameter(0)
+ counter.1 = s32[] get-tuple-element(tup.1), index=0
+ five = s32[] constant(5)
+ ROOT lt = pred[] less-than(counter.1, five)
+ }
+
+ body.2 (tup: (s32[], token[], f32[512,1024]{0,1})) -> (s32[], token[], f32[512,1024]{0,1}) {
+ tup.2 = (s32[], token[], f32[512,1024]{0,1}) parameter(0)
+ counter.2 = s32[] get-tuple-element(tup.2), index=0
+ tok.2 = token[] get-tuple-element(tup.2), index=1
+
+ ifeed.2 = (f32[512,1024]{1,0}, token[]) infeed(tok.2)
+ next_tok = token[] get-tuple-element(ifeed.2), index=1
+ next_buf = f32[512,1024]{1,0} get-tuple-element(ifeed.2), index=0
+
+ one = s32[] constant(1)
+ next_counter = s32[] add(counter.2, one)
+ ROOT tup = (s32[], token[], f32[512,1024]{0,1}) tuple(next_counter, next_tok, next_buf)
+ }
+
+ ENTRY main () -> f32[512,1024]{0,1} {
+ start_tok = token[] after-all()
+
+ ifeed.3 = (f32[512,1024]{0,1}, token[]) infeed(start_tok)
+ itok = token[] get-tuple-element(ifeed.3), index=1
+ ibuf = f32[512,1024]{0,1} get-tuple-element(ifeed.3), index=0
+
+ zero = s32[] constant(0)
+ itup = (s32[], token[], f32[512,1024]{0,1}) tuple(zero, itok, ibuf)
+
+ loop = (s32[], token[], f32[512,1024]{0,1}) while(itup), condition=condition.1, body=body.2
+ ROOT result = f32[512,1024]{0,1} get-tuple-element(loop), index=2
+ }
+ )";
+
+ ParseAndVerifyModule(module_str);
+ ComputationLayout computation_layout(
+ module().entry_computation()->ComputeProgramShape());
+
+ // Sanity check to verify that there's a layout mismatch.
+ EXPECT_THAT(LayoutOf(&module(), "ibuf"), ElementsAre(0, 1));
+ EXPECT_THAT(LayoutOf(&module(), "next_buf"), ElementsAre(1, 0));
+
+ AssignLayouts(&module(), &computation_layout);
+
+ // Make sure that layout assignment did not magically eliminate the mismatch,
+ // in which case the test didn't prove anything.
+ EXPECT_THAT(LayoutOf(&module(), "ibuf"), ElementsAre(0, 1));
+ EXPECT_THAT(LayoutOf(&module(), "next_buf"), ElementsAre(1, 0));
+}
+
} // namespace
} // namespace xla