tester_helper.h 11.4 KB
Newer Older
L
luotao1 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
// Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#pragma once

#include <gtest/gtest.h>
L
luotao1 已提交
18
#include <algorithm>
T
Tao Luo 已提交
19
#include <string>
L
luotao1 已提交
20 21 22 23 24 25
#include <thread>  // NOLINT
#include <vector>
#include "paddle/fluid/framework/ir/fuse_pass_base.h"
#include "paddle/fluid/inference/analysis/analyzer.h"
#include "paddle/fluid/inference/analysis/ut_helper.h"
#include "paddle/fluid/inference/api/analysis_predictor.h"
26
#include "paddle/fluid/inference/api/helper.h"
L
luotao1 已提交
27
#include "paddle/fluid/inference/api/paddle_inference_pass.h"
T
Tao Luo 已提交
28
#include "paddle/fluid/inference/tests/test_helper.h"
L
luotao1 已提交
29 30 31 32 33 34 35 36
#include "paddle/fluid/platform/profiler.h"

DEFINE_string(infer_model, "", "model path");
DEFINE_string(infer_data, "", "data file");
DEFINE_int32(batch_size, 1, "batch size.");
DEFINE_int32(repeat, 1, "Running the inference program repeat times.");
DEFINE_bool(test_all_data, false, "Test the all dataset in data file.");
DEFINE_int32(num_threads, 1, "Running the inference program in multi-threads.");
T
Tao Luo 已提交
37 38
DEFINE_bool(use_analysis, true,
            "Running the inference program in analysis mode.");
L
luotao1 已提交
39 40 41 42

namespace paddle {
namespace inference {

Y
Yan Chunwei 已提交
43 44
using contrib::AnalysisConfig;

L
luotao1 已提交
45
void CompareResult(const std::vector<PaddleTensor> &outputs,
T
tensor-tang 已提交
46
                   const std::vector<PaddleTensor> &ref_outputs) {
T
Tao Luo 已提交
47
  EXPECT_GT(outputs.size(), 0UL);
T
tensor-tang 已提交
48
  EXPECT_EQ(outputs.size(), ref_outputs.size());
L
luotao1 已提交
49 50
  for (size_t i = 0; i < outputs.size(); i++) {
    auto &out = outputs[i];
T
tensor-tang 已提交
51
    auto &ref_out = ref_outputs[i];
52 53
    size_t size = VecReduceToInt(out.shape);
    size_t ref_size = VecReduceToInt(ref_out.shape);
54
    EXPECT_GT(size, 0UL);
T
tensor-tang 已提交
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
    EXPECT_EQ(size, ref_size);
    EXPECT_EQ(out.dtype, ref_out.dtype);
    switch (out.dtype) {
      case PaddleDType::INT64: {
        int64_t *pdata = static_cast<int64_t *>(out.data.data());
        int64_t *pdata_ref = static_cast<int64_t *>(ref_out.data.data());
        for (size_t j = 0; j < size; ++j) {
          EXPECT_EQ(pdata_ref[j], pdata[j]);
        }
        break;
      }
      case PaddleDType::FLOAT32: {
        float *pdata = static_cast<float *>(out.data.data());
        float *pdata_ref = static_cast<float *>(ref_out.data.data());
        for (size_t j = 0; j < size; ++j) {
          EXPECT_NEAR(pdata_ref[j], pdata[j], 1e-3);
        }
        break;
      }
L
luotao1 已提交
74 75 76 77
    }
  }
}

78 79
std::unique_ptr<PaddlePredictor> CreateTestPredictor(
    const AnalysisConfig &config, bool use_analysis = true) {
T
Tao Luo 已提交
80
  if (use_analysis) {
S
superjomn 已提交
81
    return CreatePaddlePredictor<contrib::AnalysisConfig>(config);
T
Tao Luo 已提交
82
  } else {
T
Tao Luo 已提交
83
    return CreatePaddlePredictor<NativeConfig>(config);
T
Tao Luo 已提交
84 85 86
  }
}

87
size_t GetSize(const PaddleTensor &out) { return VecReduceToInt(out.shape); }
T
Tao Luo 已提交
88

89
std::unordered_map<std::string, int> GetFuseStatis(PaddlePredictor *predictor,
T
Tao Luo 已提交
90
                                                   int *num_ops) {
91
  auto *analysis_predictor = static_cast<AnalysisPredictor *>(predictor);
T
Tao Luo 已提交
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
  auto &fuse_statis = analysis_predictor->analysis_argument()
                          .Get<std::unordered_map<std::string, int>>(
                              framework::ir::kFuseStatisAttr);
  for (auto &item : fuse_statis) {
    LOG(INFO) << "fused " << item.first << " " << item.second;
  }
  int num = 0;
  for (auto &node :
       analysis_predictor->analysis_argument().main_dfg->nodes.nodes()) {
    if (node->IsFunction()) {
      ++num;
    }
  }
  *num_ops = num;
  return fuse_statis;
}

T
Tao Luo 已提交
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
void SetFakeImageInput(std::vector<std::vector<PaddleTensor>> *inputs,
                       const std::string &dirname,
                       const bool is_combined = true) {
  // Set fake_image_data
  PADDLE_ENFORCE_EQ(FLAGS_test_all_data, 0, "Only have single batch of data.");
  std::vector<std::vector<int64_t>> feed_target_shapes =
      GetFeedTargetShapes(dirname, is_combined);
  int dim1 = feed_target_shapes[0][1];
  int dim2 = feed_target_shapes[0][2];
  int dim3 = feed_target_shapes[0][3];

  PaddleTensor input;
  std::vector<int> shape({FLAGS_batch_size, dim1, dim2, dim3});
  input.shape = shape;
  input.dtype = PaddleDType::FLOAT32;

  // fill input data, for profile easily, do not use random data here.
  size_t size = FLAGS_batch_size * dim1 * dim2 * dim3;
  input.data.Resize(size * sizeof(float));
  float *input_data = static_cast<float *>(input.data.data());
  for (size_t i = 0; i < size; i++) {
    *(input_data + i) = static_cast<float>(i) / size;
  }

  std::vector<PaddleTensor> input_slots;
  input_slots.assign({input});
  (*inputs).emplace_back(input_slots);
}

L
luotao1 已提交
138
void TestOneThreadPrediction(
139 140
    const AnalysisConfig &config,
    const std::vector<std::vector<PaddleTensor>> &inputs,
T
Tao Luo 已提交
141
    std::vector<PaddleTensor> *outputs, bool use_analysis = true) {
L
luotao1 已提交
142 143
  int batch_size = FLAGS_batch_size;
  int num_times = FLAGS_repeat;
144
  auto predictor = CreateTestPredictor(config, use_analysis);
L
luotao1 已提交
145 146 147 148 149 150 151 152 153 154 155 156
  Timer timer;
  timer.tic();
  for (int i = 0; i < num_times; i++) {
    for (size_t j = 0; j < inputs.size(); j++) {
      predictor->Run(inputs[j], outputs);
    }
  }
  PrintTime(batch_size, num_times, 1, 0, timer.toc() / num_times,
            inputs.size());
}

void TestMultiThreadPrediction(
157 158
    const AnalysisConfig &config,
    const std::vector<std::vector<PaddleTensor>> &inputs,
T
Tao Luo 已提交
159 160
    std::vector<PaddleTensor> *outputs, int num_threads,
    bool use_analysis = true) {
L
luotao1 已提交
161 162 163 164 165 166 167
  int batch_size = FLAGS_batch_size;
  int num_times = FLAGS_repeat;
  std::vector<std::thread> threads;
  std::vector<std::unique_ptr<PaddlePredictor>> predictors;
  // TODO(yanchunwei): Bug here, the analyzer phase can't be parallelled
  // because AttentionLSTM's hard code nodeid will be damanged.
  for (int tid = 0; tid < num_threads; ++tid) {
168
    predictors.emplace_back(CreateTestPredictor(config, use_analysis));
L
luotao1 已提交
169 170 171
  }
  for (int tid = 0; tid < num_threads; ++tid) {
    threads.emplace_back([&, tid]() {
S
Sylwester Fraczek 已提交
172
#ifdef PADDLE_WITH_MKLDNN
S
Sylwester Fraczek 已提交
173
      platform::set_cur_thread_id(static_cast<int>(tid) + 1);
S
Sylwester Fraczek 已提交
174
#endif
L
luotao1 已提交
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
      // Each thread should have local inputs and outputs.
      // The inputs of each thread are all the same.
      std::vector<std::vector<PaddleTensor>> inputs_tid = inputs;
      std::vector<PaddleTensor> outputs_tid;
      Timer timer;
      timer.tic();
      for (int i = 0; i < num_times; i++) {
        for (size_t j = 0; j < inputs_tid.size(); j++) {
          predictors[tid]->Run(inputs_tid[j], &outputs_tid);
        }
      }
      PrintTime(batch_size, num_times, num_threads, tid,
                timer.toc() / num_times, inputs_tid.size());
    });
  }
  for (int i = 0; i < num_threads; ++i) {
    threads[i].join();
  }
}

195 196
void TestPrediction(const AnalysisConfig &config,
                    const std::vector<std::vector<PaddleTensor>> &inputs,
T
Tao Luo 已提交
197 198
                    std::vector<PaddleTensor> *outputs, int num_threads,
                    bool use_analysis = FLAGS_use_analysis) {
T
Tao Luo 已提交
199 200
  LOG(INFO) << "use_analysis: " << use_analysis
            << ", use_mkldnn: " << config._use_mkldnn;
L
luotao1 已提交
201
  if (num_threads == 1) {
T
Tao Luo 已提交
202
    TestOneThreadPrediction(config, inputs, outputs, use_analysis);
L
luotao1 已提交
203
  } else {
T
Tao Luo 已提交
204 205
    TestMultiThreadPrediction(config, inputs, outputs, num_threads,
                              use_analysis);
L
luotao1 已提交
206 207 208
  }
}

T
Tao Luo 已提交
209
void CompareNativeAndAnalysis(
210 211
    const AnalysisConfig &config,
    const std::vector<std::vector<PaddleTensor>> &inputs) {
T
Tao Luo 已提交
212
  LOG(INFO) << "use_mkldnn: " << config._use_mkldnn;
T
Tao Luo 已提交
213 214 215 216 217 218
  std::vector<PaddleTensor> native_outputs, analysis_outputs;
  TestOneThreadPrediction(config, inputs, &native_outputs, false);
  TestOneThreadPrediction(config, inputs, &analysis_outputs, true);
  CompareResult(analysis_outputs, native_outputs);
}

L
luotao1 已提交
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340
template <typename T>
std::string LoDTensorSummary(const framework::LoDTensor &tensor) {
  std::stringstream ss;
  ss << "\n---- tensor ---" << '\n';
  ss << "lod: [";
  for (const auto &level : tensor.lod()) {
    ss << "[ ";
    for (auto i : level) {
      ss << i << ", ";
    }
    ss << "]";
  }
  ss << "]\n";

  ss << "shape: [";
  int size = 1;
  for (int i = 0; i < tensor.dims().size(); i++) {
    int dim = tensor.dims()[i];
    ss << dim << ", ";
    size *= dim;
  }
  ss << "]\n";

  ss << "data: ";
  for (int i = 0; i < std::min(20, size); i++) {
    ss << tensor.data<T>()[i] << " ";
  }
  ss << "\n";

  return ss.str();
}

static bool CompareLoD(const framework::LoD &a, const framework::LoD &b) {
  if (a.size() != b.size()) {
    LOG(ERROR) << string::Sprintf("lod size not match %d != %d", a.size(),
                                  b.size());
    return false;
  }
  for (size_t i = 0; i < a.size(); i++) {
    auto &al = a[i];
    auto &bl = b[i];
    if (al.size() != bl.size()) {
      LOG(ERROR) << string::Sprintf("level size %d != %d", al.size(),
                                    bl.size());
      return false;
    }
  }
  return true;
}

static bool CompareShape(const std::vector<int64_t> &a,
                         const std::vector<int64_t> &b) {
  if (a.size() != b.size()) {
    LOG(ERROR) << string::Sprintf("shape size not match %d != %d", a.size(),
                                  b.size());
    return false;
  }
  for (size_t i = 0; i < a.size(); i++) {
    if (a[i] != b[i]) {
      LOG(ERROR) << string::Sprintf("shape %d-th element not match %d != %d", i,
                                    a[i], b[i]);
      return false;
    }
  }
  return true;
}

static bool CompareTensorData(const framework::LoDTensor &a,
                              const framework::LoDTensor &b) {
  auto a_shape = framework::vectorize(a.dims());
  auto b_shape = framework::vectorize(b.dims());
  size_t a_size = std::accumulate(a_shape.begin(), a_shape.end(), 1,
                                  [](int a, int b) { return a * b; });
  size_t b_size = std::accumulate(b_shape.begin(), b_shape.end(), 1,
                                  [](int a, int b) { return a * b; });
  if (a_size != b_size) {
    LOG(ERROR) << string::Sprintf("tensor data size not match, %d != %d",
                                  a_size, b_size);
  }

  for (size_t i = 0; i < a_size; i++) {
    if (a.type() == typeid(float)) {
      const auto *a_data = a.data<float>();
      const auto *b_data = b.data<float>();
      if (std::abs(a_data[i] - b_data[i]) > 1e-3) {
        LOG(ERROR) << string::Sprintf(
            "tensor data %d-th element not match, %f != %f", i, a_data[i],
            b_data[i]);
        return false;
      }
    } else if (a.type() == typeid(int64_t)) {
      const auto *a_data = a.data<int64_t>();
      const auto *b_data = b.data<int64_t>();
      if (std::abs(a_data[i] - b_data[i]) > 1e-3) {
        LOG(ERROR) << string::Sprintf(
            "tensor data %d-th element not match, %f != %f", i, a_data[i],
            b_data[i]);
        return false;
      }
    }
  }

  return true;
}

static bool CompareTensor(const framework::LoDTensor &a,
                          const framework::LoDTensor &b) {
  if (!CompareLoD(a.lod(), b.lod())) {
    return false;
  }
  if (!CompareShape(framework::vectorize(a.dims()),
                    framework::vectorize(b.dims()))) {
    return false;
  }

  if (!CompareTensorData(a, b)) {
    return false;
  }

  return true;
}

L
luotao1 已提交
341 342
}  // namespace inference
}  // namespace paddle