executor_test.cc 11.3 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 31 32 33 34 35 36
USE_OP(elementwise_add);
USE_OP(gaussian_random);
USE_OP(feed);
USE_OP(fetch);
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);

Q
qijun 已提交
40 41 42
using namespace paddle::platform;
using namespace paddle::framework;

Y
Yang Yang 已提交
43 44
void AddOp(const std::string& type, const VariableNameMap& inputs,
           const VariableNameMap& outputs, AttributeMap attrs,
Y
Yang Yang 已提交
45
           paddle::framework::BlockDescBind* block) {
Y
Yang Yang 已提交
46 47 48
  // insert output
  for (auto kv : outputs) {
    for (auto v : kv.second) {
49 50 51 52
      if (!block->HasVar(v)) {
        auto var = block->NewVar(v);
        var->SetDataType(paddle::framework::DataType::FP32);
      }
Y
Yang Yang 已提交
53 54 55 56
    }
  }

  // insert op
Y
Yang Yang 已提交
57 58
  auto op = block->AppendOp();
  op->SetType(type);
Y
Yang Yang 已提交
59
  for (auto& kv : inputs) {
Y
Yang Yang 已提交
60
    op->SetInput(kv.first, kv.second);
Y
Yang Yang 已提交
61
  }
Y
Yang Yang 已提交
62
  for (auto& kv : outputs) {
Y
Yang Yang 已提交
63
    op->SetOutput(kv.first, kv.second);
Y
Yang Yang 已提交
64
  }
Y
Yang Yang 已提交
65
  op->SetAttrMap(attrs);
66
  op->CheckAttrs();
Y
Yang Yang 已提交
67 68
}

Y
Yang Yang 已提交
69
// Tensors in feed value variable will only be in CPUPlace
Q
qijun 已提交
70
// So we can memcpy the data from vector<T> to feed_value
Q
qijun 已提交
71
template <typename T>
Y
Yang Yang 已提交
72 73
void SetFeedVariable(const std::vector<std::vector<T>>& inputs,
                     const std::vector<std::vector<int64_t>>& dims) {
Q
qijun 已提交
74
  Variable* g_feed_value = GetGlobalScope().FindVar("feed_value");
Q
qijun 已提交
75 76
  auto& feed_inputs =
      *(g_feed_value->GetMutable<std::vector<paddle::framework::Tensor>>());
Y
Yang Yang 已提交
77
  size_t size = inputs.size();
78
  feed_inputs.resize(size);
Q
qijun 已提交
79
  for (size_t i = 0; i < size; i++) {
Y
Yang Yang 已提交
80
    T* dst = feed_inputs[i].mutable_data<T>(make_ddim(dims[i]), CPUPlace());
81
    memcpy(dst, inputs[i].data(), inputs[i].size() * sizeof(T));
Q
qijun 已提交
82 83 84
  }
}

Y
Yang Yang 已提交
85 86
// Tensors in fetch value variable will only be in CPUPlace
// So we can memcpy the data from fetch_value to vector<T>
Q
qijun 已提交
87
template <typename T>
Y
Yang Yang 已提交
88
std::vector<std::vector<T>> GetFetchVariable() {
Q
qijun 已提交
89
  Variable* g_fetch_value = GetGlobalScope().FindVar("fetch_value");
Q
qijun 已提交
90 91
  auto& fetch_outputs =
      *(g_fetch_value->GetMutable<std::vector<paddle::framework::Tensor>>());
Q
qijun 已提交
92

Y
Yang Yang 已提交
93
  size_t size = fetch_outputs.size();
Q
qijun 已提交
94 95 96 97
  std::vector<std::vector<T>> result;
  result.reserve(size);
  for (size_t i = 0; i < size; i++) {
    std::vector<T> tmp;
98
    tmp.resize(fetch_outputs[i].numel());
Q
qijun 已提交
99 100 101 102
    memcpy(tmp.data(), fetch_outputs[i].data<T>(),
           fetch_outputs[i].numel() * sizeof(T));
    result.push_back(tmp);
  }
Y
Yang Yang 已提交
103

Q
qijun 已提交
104 105 106
  return result;
}

Q
qijun 已提交
107
class ExecutorTesterRandom : public ::testing::Test {
Q
qijun 已提交
108 109
 public:
  virtual void SetUp() override {
Y
Yang Yang 已提交
110
    int input_dim = 3, batch_size = 2, embed_dim = 5;
Y
Yang Yang 已提交
111

Y
Yang Yang 已提交
112 113 114 115 116 117
    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 已提交
118

Y
Yang Yang 已提交
119
    AddOp("gaussian_random", {}, {{"Out", {"w1"}}},
Y
Yang Yang 已提交
120
          {{"dims", std::vector<int>{input_dim, embed_dim}}}, init_root_block);
Y
Yang Yang 已提交
121
    AddOp("gaussian_random", {}, {{"Out", {"w2"}}},
Y
Yang Yang 已提交
122
          {{"dims", std::vector<int>{embed_dim, input_dim}}}, init_root_block);
123 124
    AddOp("fetch", {{"Input", {"w1"}}}, {}, {{"col", 0}}, init_root_block);
    AddOp("fetch", {{"Input", {"w2"}}}, {}, {{"col", 1}}, init_root_block);
Y
Yang Yang 已提交
125 126 127 128

    // flush
    init_program.Proto();

Y
Yang Yang 已提交
129
    // run block
Y
Yang Yang 已提交
130 131 132 133 134 135
    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 已提交
136

Y
Yang Yang 已提交
137
    // feed data
Y
Yang Yang 已提交
138
    inputs_.push_back({1.0, 1.0, 1.0, 1.0, 1.0, 1.0});
Y
Yang Yang 已提交
139 140 141 142 143
    dims_.push_back({batch_size, input_dim});
    AddOp("feed", {}, {{"Out", {"a"}}},
          {{"dims", std::vector<int>{batch_size, input_dim}}, {"col", 0}},
          root_block);

Y
Yang Yang 已提交
144
    // forward
Y
Yang Yang 已提交
145
    AddOp("mul", {{"X", {"a"}}, {"Y", {"w1"}}}, {{"Out", {"b"}}}, {},
Y
Yang Yang 已提交
146
          root_block);
Y
Yang Yang 已提交
147
    AddOp("mul", {{"X", {"b"}}, {"Y", {"w2"}}}, {{"Out", {"a_out"}}}, {},
Y
Yang Yang 已提交
148
          root_block);
Y
Yang Yang 已提交
149 150
    AddOp("squared_l2_distance", {{"X", {"a"}}, {"Y", {"a_out"}}},
          {{"Out", {"l2_distance"}}, {"sub_result", {"l2_distance_sub"}}}, {},
Y
Yang Yang 已提交
151
          root_block);
152 153
    AddOp("mean", {{"X", {"l2_distance"}}}, {{"Out", {"mean_out"}}}, {},
          root_block);
Y
Yang Yang 已提交
154

Y
Yang Yang 已提交
155
    // backward
156 157
    auto target = VarDescBind("mean_out");
    AppendBackward(program, target, {});
Y
Yang Yang 已提交
158 159 160

    // update
    AddOp("fill_constant", {}, {{"Out", {"learning_rate"}}},
Y
Yang Yang 已提交
161 162
          {{"shape", std::vector<int>{1}}, {"value", float(0.001)}},
          root_block);
Y
Yang Yang 已提交
163 164 165 166 167 168 169 170 171
    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);

172 173
    AddOp("fetch", {{"Input", {"w1"}}}, {}, {{"col", 0}}, root_block);
    AddOp("fetch", {{"Input", {"w2"}}}, {}, {{"col", 1}}, root_block);
Y
Yang Yang 已提交
174
    AddOp("fetch", {{"Input", {"l2_distance"}}}, {}, {{"col", 0}}, root_block);
Y
Yang Yang 已提交
175

Y
Yang Yang 已提交
176 177
    // flush
    program.Proto();
Q
qijun 已提交
178
  }
Y
Yang Yang 已提交
179

Q
qijun 已提交
180
 protected:
Y
Yang Yang 已提交
181
  ProgramDesc init_pdesc_;
Q
qijun 已提交
182
  ProgramDesc pdesc_;
Y
Yang Yang 已提交
183 184
  std::vector<std::vector<float>> inputs_;
  std::vector<std::vector<int64_t>> dims_;
Q
qijun 已提交
185 186
};

Y
Yang Yang 已提交
187
class ExecutorTesterFeedAndFetch : public ::testing::Test {
Q
qijun 已提交
188 189
 public:
  virtual void SetUp() override {
Y
Yang Yang 已提交
190 191 192 193 194 195 196 197
    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 已提交
198

199 200
    std::vector<int> dim{6};

Y
Yang Yang 已提交
201 202 203 204
    AddOp("feed", {}, {{"Out", {"a"}}}, {{"dims", dim}, {"col", 0}},
          root_block);
    AddOp("feed", {}, {{"Out", {"b"}}}, {{"dims", dim}, {"col", 1}},
          root_block);
205 206
    AddOp("fetch", {{"Input", {"a"}}}, {}, {{"col", 0}}, root_block);
    AddOp("fetch", {{"Input", {"b"}}}, {}, {{"col", 1}}, root_block);
Q
qijun 已提交
207

Y
Yang Yang 已提交
208 209 210
    // flush
    program.Proto();

211 212
    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 已提交
213 214
    inputs_.push_back(vec1);
    inputs_.push_back(vec2);
Y
Yang Yang 已提交
215 216
    dims_.push_back({static_cast<int64_t>(vec1.size())});
    dims_.push_back({static_cast<int64_t>(vec2.size())});
Q
qijun 已提交
217 218 219 220 221
  }

 protected:
  ProgramDesc pdesc_;
  std::vector<std::vector<float>> inputs_;
Y
Yang Yang 已提交
222
  std::vector<std::vector<int64_t>> dims_;
Q
qijun 已提交
223 224
};

Q
qijun 已提交
225
#ifndef PADDLE_WITH_CUDA
Q
qijun 已提交
226
TEST_F(ExecutorTesterRandom, CPU) {
Q
qijun 已提交
227
  std::vector<Place> places;
228 229 230 231 232 233 234 235
  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 已提交
236

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

Q
qijun 已提交
239 240 241
  executor->Run(init_pdesc_, &GetGlobalScope(), 0);
  SetFeedVariable<float>(inputs_, dims_);
  executor->Run(pdesc_, &GetGlobalScope(), 0);
Y
Yang Yang 已提交
242
  std::vector<std::vector<float>> result = GetFetchVariable<float>();
Q
qijun 已提交
243 244
}

Y
Yang Yang 已提交
245
TEST_F(ExecutorTesterFeedAndFetch, CPU) {
Q
qijun 已提交
246 247 248 249
  std::vector<Place> places;
  CPUPlace cpu_place;
  places.push_back(cpu_place);

250 251 252 253 254 255
  // 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 已提交
256
  std::unique_ptr<Executor> executor(new Executor(places));
Q
qijun 已提交
257

Y
Yang Yang 已提交
258
  for (int batch_id = 0; batch_id < 3; batch_id++) {
Y
Yang Yang 已提交
259
    SetFeedVariable<float>(inputs_, dims_);
Q
qijun 已提交
260
    executor->Run(pdesc_, &GetGlobalScope(), 0);
Y
Yang Yang 已提交
261
    std::vector<std::vector<float>> result = GetFetchVariable<float>();
Y
Yang Yang 已提交
262 263 264 265 266
    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 已提交
267 268
      }
    }
Q
qijun 已提交
269
  }
Q
qijun 已提交
270
}
Q
qijun 已提交
271
#else
Q
qijun 已提交
272 273 274 275 276
TEST_F(ExecutorTesterRandom, GPU) {
  std::vector<Place> places;
  GPUPlace gpu_place(0);
  places.push_back(gpu_place);

Q
qijun 已提交
277 278 279 280 281 282 283
  // 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());
284 285
  paddle::memory::Used(gpu_place);

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

Q
qijun 已提交
288
  executor->Run(init_pdesc_, &GetGlobalScope(), 0);
Y
Yang Yang 已提交
289
  for (int batch_id = 0; batch_id < 3; batch_id++) {
Y
Yang Yang 已提交
290
    SetFeedVariable<float>(inputs_, dims_);
Q
qijun 已提交
291
    executor->Run(pdesc_, &GetGlobalScope(), 0);
Y
Yang Yang 已提交
292
  }
Q
qijun 已提交
293 294
}

Y
Yang Yang 已提交
295
TEST_F(ExecutorTesterFeedAndFetch, GPU) {
Q
qijun 已提交
296
  std::vector<Place> places;
Q
qijun 已提交
297 298
  GPUPlace gpu_place(0);
  places.push_back(gpu_place);
Q
qijun 已提交
299 300 301 302 303 304 305
  // 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());
306 307
  paddle::memory::Used(gpu_place);

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

Y
Yang Yang 已提交
310
  for (int batch_id = 0; batch_id < 3; batch_id++) {
Y
Yang Yang 已提交
311
    SetFeedVariable<float>(inputs_, dims_);
Q
qijun 已提交
312
    executor->Run(pdesc_, &GetGlobalScope(), 0);
Y
Yang Yang 已提交
313
    std::vector<std::vector<float>> result = GetFetchVariable<float>();
Y
Yang Yang 已提交
314 315 316 317 318
    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 已提交
319 320 321
      }
    }
  }
Y
Yang Yang 已提交
322
}
Y
Yu Yang 已提交
323 324 325 326 327 328 329 330

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 已提交
331 332
}

333
#endif