1 /* Copyright 2017 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 #define EIGEN_USE_THREADS 16 #include "tensorflow/compiler/xla/service/cpu/cpu_runtime.h" 17 18 #include <memory> 19 #include <string> 20 #include <tuple> 21 22 #include "third_party/eigen3/unsupported/Eigen/CXX11/Tensor" 23 #include "tensorflow/compiler/xla/array2d.h" 24 #include "tensorflow/compiler/xla/client/local_client.h" 25 #include "tensorflow/compiler/xla/ptr_util.h" 26 #include "tensorflow/compiler/xla/service/cpu/runtime_matmul.h" 27 #include "tensorflow/compiler/xla/service/cpu/runtime_single_threaded_matmul.h" 28 #include "tensorflow/compiler/xla/types.h" 29 #include "tensorflow/core/common_runtime/eigen_thread_pool.h" 30 #include "tensorflow/core/lib/strings/stringprintf.h" 31 #include "tensorflow/core/platform/env.h" 32 #include "tensorflow/core/platform/logging.h" 33 #include "tensorflow/core/platform/test.h" 34 35 namespace xla { 36 namespace { 37 38 class CpuRuntimeTest : public ::testing::Test {}; 39 40 template <typename T> 41 std::unique_ptr<Array2D<float>> MaybeTransposeArray2D(const Array2D<T>& array, 42 bool transpose) { 43 int64 output_height = array.height(); 44 int64 output_width = array.width(); 45 if (transpose) { 46 std::swap(output_width, output_height); 47 } 48 auto output = MakeUnique<Array2D<float>>(output_height, output_width); 49 for (int y = 0; y < array.height(); y++) { 50 for (int x = 0; x < array.width(); x++) { 51 if (transpose) { 52 (*output)(x, y) = array(y, x); 53 } else { 54 (*output)(y, x) = array(y, x); 55 } 56 } 57 } 58 return output; 59 } 60 61 // Verifies that matrix 'c' equals the result of matrix 'a' times matrix 'b'. 62 // Each element is compared to within a small error bound. 63 void CheckMatrixMultiply(const Array2D<float>& a, const Array2D<float>& b, 64 const Array2D<float>& c) { 65 for (int i = 0; i < a.height(); ++i) { 66 for (int j = 0; j < b.width(); ++j) { 67 float sum = 0.0; 68 for (int k = 0; k < a.width(); ++k) { 69 sum += a(i, k) * b(k, j); 70 } 71 EXPECT_NEAR(sum, c(i, j), 0.01); 72 } 73 } 74 } 75 76 std::unique_ptr<Array2D<float>> EigenMatrixMultiply(const Array2D<float>& a, 77 const Array2D<float>& b, 78 bool transpose_lhs, 79 bool transpose_rhs, 80 bool single_threaded) { 81 CHECK_EQ(a.width(), b.height()); 82 int64 m = a.height(); 83 int64 n = b.width(); 84 int64 k = a.width(); 85 86 // The Eigen matmul runtime function expects the matrix to be in column major 87 // order and array2d is in row-major order. Create transposes of a and b. The 88 // 'data' buffer in the transposed array is the original array in column major 89 // order. 90 auto a_transpose = MaybeTransposeArray2D(a, !transpose_lhs); 91 auto b_transpose = MaybeTransposeArray2D(b, !transpose_rhs); 92 93 // Since we're going to transpose c before returning it. Swap the order of the 94 // dimension sizes to ensure the returned array is properly dimensioned. 95 auto c_transpose = MakeUnique<Array2D<float>>(n, m); 96 if (single_threaded) { 97 __xla_cpu_runtime_EigenSingleThreadedMatMulF32( 98 nullptr, c_transpose->data(), a_transpose->data(), b_transpose->data(), 99 m, n, k, transpose_lhs, transpose_rhs); 100 } else { 101 tensorflow::thread::ThreadPool pool(tensorflow::Env::Default(), "XLAEigen", 102 2); 103 tensorflow::EigenThreadPoolWrapper tp(&pool); 104 Eigen::ThreadPoolDevice device(&tp, tp.NumThreads()); 105 ExecutableRunOptions run_options; 106 run_options.set_intra_op_thread_pool(&device); 107 108 __xla_cpu_runtime_EigenMatMulF32(&run_options, c_transpose->data(), 109 a_transpose->data(), b_transpose->data(), 110 m, n, k, transpose_lhs, transpose_rhs); 111 } 112 return MaybeTransposeArray2D(*c_transpose, true); 113 } 114 115 struct MatMulShape { 116 int64 m; 117 int64 k; 118 int64 n; 119 }; 120 121 MatMulShape MatMulShapes[] = { 122 MatMulShape{2, 2, 3}, MatMulShape{256, 512, 1024}, 123 MatMulShape{128, 128, 1}, MatMulShape{1, 128, 128}, 124 MatMulShape{1, 32, 128}, MatMulShape{1, 32, 16}, 125 MatMulShape{32, 16, 1}, MatMulShape{32, 128, 1}, 126 }; 127 128 // This takes 4 parameters: 129 // * shape of the matmul 130 // * transpose_lhs 131 // * transpose_rhs 132 // * single_threaded 133 using EigenMatMulTestParam = std::tuple<MatMulShape, bool, bool, bool>; 134 135 class EigenMatMulTest 136 : public CpuRuntimeTest, 137 public ::testing::WithParamInterface<EigenMatMulTestParam> { 138 public: 139 static string Name( 140 const ::testing::TestParamInfo<EigenMatMulTestParam>& info) { 141 MatMulShape shape = std::get<0>(info.param); 142 bool transpose_lhs = std::get<1>(info.param); 143 bool transpose_rhs = std::get<2>(info.param); 144 bool single_threaded = std::get<3>(info.param); 145 146 return tensorflow::strings::Printf( 147 "MatMul_%lld_%lld_%lld_%s%s%s_threaded", shape.m, shape.k, shape.n, 148 transpose_lhs ? "Tlhs_" : "", transpose_rhs ? "Trhs_" : "", 149 single_threaded ? "single" : "multi"); 150 } 151 }; // namespace xla 152 153 TEST_P(EigenMatMulTest, DoIt) { 154 MatMulShape shape = std::get<0>(GetParam()); 155 bool transpose_lhs = std::get<1>(GetParam()); 156 bool transpose_rhs = std::get<2>(GetParam()); 157 bool single_threaded = std::get<3>(GetParam()); 158 159 auto a = MakeLinspaceArray2D(0.0, 1.0, shape.m, shape.k); 160 auto b = MakeLinspaceArray2D(-2.0, 2.0, shape.k, shape.n); 161 auto c = EigenMatrixMultiply(*a, *b, transpose_lhs, transpose_rhs, 162 single_threaded); 163 CheckMatrixMultiply(*a, *b, *c); 164 } 165 166 INSTANTIATE_TEST_CASE_P(EigenMatMulTestInstantiaion, EigenMatMulTest, 167 ::testing::Combine(::testing::ValuesIn(MatMulShapes), 168 ::testing::Bool(), ::testing::Bool(), 169 ::testing::Bool()), 170 EigenMatMulTest::Name); 171 172 } // namespace 173 } // namespace xla 174