executor_test.cc 11.9 KB
Newer Older
Q
qijun 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve.

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. */

#include "paddle/framework/executor.h"
Y
Yang Yang 已提交
16 17

#include <memory>
Q
qijun 已提交
18
#include <vector>
Y
Yang Yang 已提交
19

Y
Yu Yang 已提交
20
#include "gflags/gflags.h"
Y
Yang Yang 已提交
21
#include "gtest/gtest.h"
Y
Yang Yang 已提交
22
#include "paddle/framework/attribute.h"
Y
Yang Yang 已提交
23
#include "paddle/framework/backward.h"
Y
Yang Yang 已提交
24 25
#include "paddle/framework/block_desc.h"
#include "paddle/framework/op_desc.h"
Y
Yang Yang 已提交
26 27 28
#include "paddle/framework/op_registry.h"
#include "paddle/framework/operator.h"

Q
qijun 已提交
29 30
USE_OP(elementwise_add);
USE_OP(gaussian_random);
Y
Yu Yang 已提交
31 32
USE_NO_KERNEL_OP(feed);
USE_NO_KERNEL_OP(fetch);
Q
qijun 已提交
33 34 35 36
USE_OP(mul);
USE_OP(sum);
USE_OP(squared_l2_distance);
USE_OP(fill_constant);
37
USE_OP(mean);
Q
qijun 已提交
38 39
USE_OP(sgd);

Y
Yu Yang 已提交
40 41 42
constexpr auto kFeedValueName = "feed_value";
constexpr auto kFetchValueName = "fetch_value";

Q
qijun 已提交
43 44 45
using namespace paddle::platform;
using namespace paddle::framework;

Y
Yang Yang 已提交
46 47
void AddOp(const std::string& type, const VariableNameMap& inputs,
           const VariableNameMap& outputs, AttributeMap attrs,
Y
Yang Yang 已提交
48
           paddle::framework::BlockDescBind* block) {
Y
Yang Yang 已提交
49 50 51
  // insert output
  for (auto kv : outputs) {
    for (auto v : kv.second) {
52 53 54 55 56
      // <<<<<<< HEAD
      //       auto var = block->Var(v);
      //       var->SetType(VarDesc::LOD_TENSOR);
      //       var->SetDataType(paddle::framework::DataType::FP32);
      // =======
57
      if (!block->HasVar(v)) {
58
        auto var = block->Var(v);
59 60
        var->SetDataType(paddle::framework::DataType::FP32);
      }
61
      // >>>>>>> origin/develop
Y
Yang Yang 已提交
62 63 64 65
    }
  }

  // insert op
Y
Yang Yang 已提交
66 67
  auto op = block->AppendOp();
  op->SetType(type);
Y
Yang Yang 已提交
68
  for (auto& kv : inputs) {
Y
Yang Yang 已提交
69
    op->SetInput(kv.first, kv.second);
Y
Yang Yang 已提交
70
  }
Y
Yang Yang 已提交
71
  for (auto& kv : outputs) {
Y
Yang Yang 已提交
72
    op->SetOutput(kv.first, kv.second);
Y
Yang Yang 已提交
73
  }
Y
Yang Yang 已提交
74
  op->SetAttrMap(attrs);
75
  op->CheckAttrs();
Y
Yang Yang 已提交
76 77
}

Y
Yang Yang 已提交
78
// Tensors in feed value variable will only be in CPUPlace
Q
qijun 已提交
79
// So we can memcpy the data from vector<T> to feed_value
Q
qijun 已提交
80
template <typename T>
Y
Yang Yang 已提交
81 82
void SetFeedVariable(const std::vector<std::vector<T>>& inputs,
                     const std::vector<std::vector<int64_t>>& dims) {
Y
Yu Yang 已提交
83
  Variable* g_feed_value = GetGlobalScope().FindVar(kFeedValueName);
Q
qijun 已提交
84
  auto& feed_inputs =
Y
Yu Yang 已提交
85
      *(g_feed_value->GetMutable<std::vector<paddle::framework::LoDTensor>>());
Y
Yang Yang 已提交
86
  size_t size = inputs.size();
87
  feed_inputs.resize(size);
Q
qijun 已提交
88
  for (size_t i = 0; i < size; i++) {
Y
Yang Yang 已提交
89
    T* dst = feed_inputs[i].mutable_data<T>(make_ddim(dims[i]), CPUPlace());
90
    memcpy(dst, inputs[i].data(), inputs[i].size() * sizeof(T));
Q
qijun 已提交
91 92 93
  }
}

Y
Yang Yang 已提交
94 95
// Tensors in fetch value variable will only be in CPUPlace
// So we can memcpy the data from fetch_value to vector<T>
Q
qijun 已提交
96
template <typename T>
Y
Yang Yang 已提交
97
std::vector<std::vector<T>> GetFetchVariable() {
Y
Yu Yang 已提交
98
  Variable* g_fetch_value = GetGlobalScope().FindVar(kFetchValueName);
Q
qijun 已提交
99
  auto& fetch_outputs =
Y
Yu Yang 已提交
100
      *(g_fetch_value->GetMutable<std::vector<paddle::framework::LoDTensor>>());
Q
qijun 已提交
101

Y
Yang Yang 已提交
102
  size_t size = fetch_outputs.size();
Q
qijun 已提交
103 104 105 106
  std::vector<std::vector<T>> result;
  result.reserve(size);
  for (size_t i = 0; i < size; i++) {
    std::vector<T> tmp;
107
    tmp.resize(fetch_outputs[i].numel());
Q
qijun 已提交
108 109 110 111
    memcpy(tmp.data(), fetch_outputs[i].data<T>(),
           fetch_outputs[i].numel() * sizeof(T));
    result.push_back(tmp);
  }
Y
Yang Yang 已提交
112

Q
qijun 已提交
113 114 115
  return result;
}

Q
qijun 已提交
116
class ExecutorTesterRandom : public ::testing::Test {
Q
qijun 已提交
117 118
 public:
  virtual void SetUp() override {
Y
Yang Yang 已提交
119
    int input_dim = 3, batch_size = 2, embed_dim = 5;
Y
Yang Yang 已提交
120

Y
Yang Yang 已提交
121 122 123 124 125 126
    auto temp_init_root_block = init_pdesc_.add_blocks();
    temp_init_root_block->set_idx(0);
    temp_init_root_block->set_parent_idx(-1);
    paddle::framework::ProgramDescBind& init_program =
        paddle::framework::ProgramDescBind::Instance(&init_pdesc_);
    paddle::framework::BlockDescBind* init_root_block = init_program.Block(0);
Y
Yang Yang 已提交
127

Y
Yang Yang 已提交
128
    AddOp("gaussian_random", {}, {{"Out", {"w1"}}},
Y
Yang Yang 已提交
129
          {{"dims", std::vector<int>{input_dim, embed_dim}}}, init_root_block);
Y
Yang Yang 已提交
130
    AddOp("gaussian_random", {}, {{"Out", {"w2"}}},
Y
Yang Yang 已提交
131
          {{"dims", std::vector<int>{embed_dim, input_dim}}}, init_root_block);
Y
Yu Yang 已提交
132 133 134 135
    AddOp("fetch", {{"Input", {"w1"}}}, {{"Out", {kFetchValueName}}},
          {{"col", 0}}, init_root_block);
    AddOp("fetch", {{"Input", {"w2"}}}, {{"Out", {kFetchValueName}}},
          {{"col", 1}}, init_root_block);
Y
Yang Yang 已提交
136 137 138 139

    // flush
    init_program.Proto();

Y
Yang Yang 已提交
140
    // run block
Y
Yang Yang 已提交
141 142 143 144 145 146
    auto temp_root_block = pdesc_.add_blocks();
    temp_root_block->set_idx(0);
    temp_root_block->set_parent_idx(-1);
    paddle::framework::ProgramDescBind& program =
        paddle::framework::ProgramDescBind::Instance(&pdesc_);
    paddle::framework::BlockDescBind* root_block = program.Block(0);
Q
qijun 已提交
147

Y
Yang Yang 已提交
148
    // feed data
Y
Yang Yang 已提交
149
    inputs_.push_back({1.0, 1.0, 1.0, 1.0, 1.0, 1.0});
Y
Yang Yang 已提交
150
    dims_.push_back({batch_size, input_dim});
Y
Yu Yang 已提交
151
    AddOp("feed", {{"Input", {kFeedValueName}}}, {{"Out", {"a"}}},
Y
Yang Yang 已提交
152 153 154
          {{"dims", std::vector<int>{batch_size, input_dim}}, {"col", 0}},
          root_block);

Y
Yang Yang 已提交
155
    // forward
Y
Yang Yang 已提交
156
    AddOp("mul", {{"X", {"a"}}, {"Y", {"w1"}}}, {{"Out", {"b"}}}, {},
Y
Yang Yang 已提交
157
          root_block);
Y
Yang Yang 已提交
158
    AddOp("mul", {{"X", {"b"}}, {"Y", {"w2"}}}, {{"Out", {"a_out"}}}, {},
Y
Yang Yang 已提交
159
          root_block);
Y
Yang Yang 已提交
160 161
    AddOp("squared_l2_distance", {{"X", {"a"}}, {"Y", {"a_out"}}},
          {{"Out", {"l2_distance"}}, {"sub_result", {"l2_distance_sub"}}}, {},
Y
Yang Yang 已提交
162
          root_block);
163 164
    AddOp("mean", {{"X", {"l2_distance"}}}, {{"Out", {"mean_out"}}}, {},
          root_block);
Y
Yang Yang 已提交
165

Y
Yang Yang 已提交
166
    // backward
167 168
    auto target = VarDescBind("mean_out");
    AppendBackward(program, target, {});
Y
Yang Yang 已提交
169 170 171

    // update
    AddOp("fill_constant", {}, {{"Out", {"learning_rate"}}},
Y
Yang Yang 已提交
172 173
          {{"shape", std::vector<int>{1}}, {"value", float(0.001)}},
          root_block);
Y
Yang Yang 已提交
174 175 176 177 178 179 180 181 182
    AddOp("sgd", {{"Param", {"w1"}},
                  {"LearningRate", {"learning_rate"}},
                  {"Grad", {"w1@GRAD"}}},
          {{"ParamOut", {"w1"}}}, {}, root_block);
    AddOp("sgd", {{"Param", {"w2"}},
                  {"LearningRate", {"learning_rate"}},
                  {"Grad", {"w2@GRAD"}}},
          {{"ParamOut", {"w2"}}}, {}, root_block);

Y
Yu Yang 已提交
183 184 185 186 187 188
    AddOp("fetch", {{"Input", {"w1"}}}, {{"Out", {kFetchValueName}}},
          {{"col", 0}}, root_block);
    AddOp("fetch", {{"Input", {"w2"}}}, {{"Out", {kFetchValueName}}},
          {{"col", 1}}, root_block);
    AddOp("fetch", {{"Input", {"l2_distance"}}}, {{"Out", {kFetchValueName}}},
          {{"col", 0}}, root_block);
Y
Yang Yang 已提交
189

Y
Yang Yang 已提交
190 191
    // flush
    program.Proto();
Q
qijun 已提交
192
  }
Y
Yang Yang 已提交
193

Q
qijun 已提交
194
 protected:
Y
Yang Yang 已提交
195
  ProgramDesc init_pdesc_;
Q
qijun 已提交
196
  ProgramDesc pdesc_;
Y
Yang Yang 已提交
197 198
  std::vector<std::vector<float>> inputs_;
  std::vector<std::vector<int64_t>> dims_;
Q
qijun 已提交
199 200
};

Y
Yang Yang 已提交
201
class ExecutorTesterFeedAndFetch : public ::testing::Test {
Q
qijun 已提交
202 203
 public:
  virtual void SetUp() override {
Y
Yang Yang 已提交
204 205 206 207 208 209 210 211
    auto temp_root_block = pdesc_.add_blocks();
    temp_root_block->set_idx(0);
    temp_root_block->set_parent_idx(-1);

    // wrap to BlockDescBind
    paddle::framework::ProgramDescBind& program =
        paddle::framework::ProgramDescBind::Instance(&pdesc_);
    paddle::framework::BlockDescBind* root_block = program.Block(0);
Q
qijun 已提交
212

213 214
    std::vector<int> dim{6};

Y
Yu Yang 已提交
215 216 217 218 219 220 221 222
    AddOp("feed", {{"Input", {kFeedValueName}}}, {{"Out", {"a"}}},
          {{"dims", dim}, {"col", 0}}, root_block);
    AddOp("feed", {{"Input", {kFeedValueName}}}, {{"Out", {"b"}}},
          {{"dims", dim}, {"col", 1}}, root_block);
    AddOp("fetch", {{"Input", {"a"}}}, {{"Out", {kFetchValueName}}},
          {{"col", 0}}, root_block);
    AddOp("fetch", {{"Input", {"b"}}}, {{"Out", {kFetchValueName}}},
          {{"col", 1}}, root_block);
Q
qijun 已提交
223

Y
Yang Yang 已提交
224 225 226
    // flush
    program.Proto();

227 228
    std::vector<float> vec1 = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0};
    std::vector<float> vec2 = {4.0, 5.0, 6.0, 7.0, 8.0, 9.0};
Q
qijun 已提交
229 230
    inputs_.push_back(vec1);
    inputs_.push_back(vec2);
Y
Yang Yang 已提交
231 232
    dims_.push_back({static_cast<int64_t>(vec1.size())});
    dims_.push_back({static_cast<int64_t>(vec2.size())});
Q
qijun 已提交
233 234 235 236 237
  }

 protected:
  ProgramDesc pdesc_;
  std::vector<std::vector<float>> inputs_;
Y
Yang Yang 已提交
238
  std::vector<std::vector<int64_t>> dims_;
Q
qijun 已提交
239 240
};

Q
qijun 已提交
241
#ifndef PADDLE_WITH_CUDA
Q
qijun 已提交
242
TEST_F(ExecutorTesterRandom, CPU) {
Q
qijun 已提交
243
  std::vector<Place> places;
244 245 246 247 248 249 250 251
  CPUPlace cpu_place;
  places.push_back(cpu_place);

  // We have a global Scope and BuddyAllocator, and we must ensure
  // global BuddyAllocator is initialized before global Scope. Thus,
  // global Scope will deconstruct before BuddyAllocator. Otherwise,
  // "pointer being freed was not allocated" error will appear.
  paddle::memory::Used(cpu_place);
Q
qijun 已提交
252

Y
Yang Yang 已提交
253
  std::unique_ptr<Executor> executor(new Executor(places));
Q
qijun 已提交
254 255 256
  executor->Run(init_pdesc_, &GetGlobalScope(), 0);
  SetFeedVariable<float>(inputs_, dims_);
  executor->Run(pdesc_, &GetGlobalScope(), 0);
Y
Yang Yang 已提交
257
  std::vector<std::vector<float>> result = GetFetchVariable<float>();
Q
qijun 已提交
258 259
}

Y
Yang Yang 已提交
260
TEST_F(ExecutorTesterFeedAndFetch, CPU) {
Q
qijun 已提交
261 262
  std::vector<Place> places;
  CPUPlace cpu_place;
Y
Yu Yang 已提交
263
  places.emplace_back(cpu_place);
Q
qijun 已提交
264

265 266 267 268 269 270
  // We have a global Scope and BuddyAllocator, and we must ensure
  // global BuddyAllocator is initialized before global Scope. Thus,
  // global Scope will deconstruct before BuddyAllocator. Otherwise,
  // "pointer being freed was not allocated" error will appear.
  paddle::memory::Used(cpu_place);

Y
Yang Yang 已提交
271
  std::unique_ptr<Executor> executor(new Executor(places));
Q
qijun 已提交
272

Y
Yang Yang 已提交
273
  for (int batch_id = 0; batch_id < 3; batch_id++) {
Y
Yang Yang 已提交
274
    SetFeedVariable<float>(inputs_, dims_);
Q
qijun 已提交
275
    executor->Run(pdesc_, &GetGlobalScope(), 0);
Y
Yang Yang 已提交
276
    std::vector<std::vector<float>> result = GetFetchVariable<float>();
Y
Yu Yang 已提交
277
    ASSERT_EQ(result.size(), inputs_.size());
Y
Yang Yang 已提交
278
    for (size_t i = 0; i < result.size(); ++i) {
Y
Yu Yang 已提交
279
      ASSERT_EQ(result[i].size(), inputs_[i].size());
Y
Yang Yang 已提交
280
      for (size_t j = 0; j < result[i].size(); ++j) {
Y
Yu Yang 已提交
281
        ASSERT_EQ(result[i][j], inputs_[i][j]);
Q
qijun 已提交
282 283
      }
    }
Q
qijun 已提交
284
  }
Q
qijun 已提交
285
}
Q
qijun 已提交
286
#else
Q
qijun 已提交
287 288 289 290 291
TEST_F(ExecutorTesterRandom, GPU) {
  std::vector<Place> places;
  GPUPlace gpu_place(0);
  places.push_back(gpu_place);

Q
qijun 已提交
292 293 294 295 296 297 298
  // We have a global Scope and BuddyAllocator, and we must ensure
  // global BuddyAllocator is initialized before global Scope. Thus,
  // global Scope will deconstruct before BuddyAllocator. Otherwise,
  // "pointer being freed was not allocated" error will appear.
  // If paddle is compiled with GPU, both CPU and GPU BuddyAllocator
  // need to be used at first.
  paddle::memory::Used(CPUPlace());
299 300
  paddle::memory::Used(gpu_place);

Y
Yang Yang 已提交
301
  std::unique_ptr<Executor> executor(new Executor(places));
Y
Yang Yang 已提交
302

Q
qijun 已提交
303
  executor->Run(init_pdesc_, &GetGlobalScope(), 0);
Y
Yang Yang 已提交
304
  for (int batch_id = 0; batch_id < 3; batch_id++) {
Y
Yang Yang 已提交
305
    SetFeedVariable<float>(inputs_, dims_);
Q
qijun 已提交
306
    executor->Run(pdesc_, &GetGlobalScope(), 0);
Y
Yang Yang 已提交
307
  }
Q
qijun 已提交
308 309
}

Y
Yang Yang 已提交
310
TEST_F(ExecutorTesterFeedAndFetch, GPU) {
Q
qijun 已提交
311
  std::vector<Place> places;
Q
qijun 已提交
312 313
  GPUPlace gpu_place(0);
  places.push_back(gpu_place);
Q
qijun 已提交
314 315 316 317 318 319 320
  // We have a global Scope and BuddyAllocator, and we must ensure
  // global BuddyAllocator is initialized before global Scope. Thus,
  // global Scope will deconstruct before BuddyAllocator. Otherwise,
  // "pointer being freed was not allocated" error will appear.
  // If paddle is compiled with GPU, both CPU and GPU BuddyAllocator
  // need to be used at first.
  paddle::memory::Used(CPUPlace());
321 322
  paddle::memory::Used(gpu_place);

Y
Yang Yang 已提交
323
  std::unique_ptr<Executor> executor(new Executor(places));
Q
qijun 已提交
324

Y
Yang Yang 已提交
325
  for (int batch_id = 0; batch_id < 3; batch_id++) {
Y
Yang Yang 已提交
326
    SetFeedVariable<float>(inputs_, dims_);
Q
qijun 已提交
327
    executor->Run(pdesc_, &GetGlobalScope(), 0);
Y
Yang Yang 已提交
328
    std::vector<std::vector<float>> result = GetFetchVariable<float>();
Y
Yang Yang 已提交
329 330 331 332 333
    PADDLE_ENFORCE_EQ(result.size(), inputs_.size());
    for (size_t i = 0; i < result.size(); ++i) {
      PADDLE_ENFORCE_EQ(result[i].size(), inputs_[i].size());
      for (size_t j = 0; j < result[i].size(); ++j) {
        PADDLE_ENFORCE_EQ(result[i][j], inputs_[i][j]);
Q
qijun 已提交
334 335 336
      }
    }
  }
Y
Yang Yang 已提交
337
}
Y
Yu Yang 已提交
338 339 340 341 342 343 344 345

DECLARE_double(fraction_of_gpu_memory_to_use);

int main(int argc, char** argv) {
  testing::InitGoogleTest(&argc, argv);
  // Use less GPU memory for unittest.
  FLAGS_fraction_of_gpu_memory_to_use = 0.25;
  return RUN_ALL_TESTS();
Y
Yu Yang 已提交
346 347
}

Q
qijun 已提交
348
#endif