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 37 38
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);
USE_OP(sgd);

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

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

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

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

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

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

Q
qijun 已提交
102 103 104
  return result;
}

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

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

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

    // flush
    init_program.Proto();

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

Y
Yang Yang 已提交
135
    // feed data
Y
Yang Yang 已提交
136
    inputs_.push_back({1.0, 1.0, 1.0, 1.0, 1.0, 1.0});
Y
Yang Yang 已提交
137 138 139 140 141
    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 已提交
142
    // forward
Y
Yang Yang 已提交
143
    AddOp("mul", {{"X", {"a"}}, {"Y", {"w1"}}}, {{"Out", {"b"}}}, {},
Y
Yang Yang 已提交
144
          root_block);
Y
Yang Yang 已提交
145
    AddOp("mul", {{"X", {"b"}}, {"Y", {"w2"}}}, {{"Out", {"a_out"}}}, {},
Y
Yang Yang 已提交
146
          root_block);
Y
Yang Yang 已提交
147 148
    AddOp("squared_l2_distance", {{"X", {"a"}}, {"Y", {"a_out"}}},
          {{"Out", {"l2_distance"}}, {"sub_result", {"l2_distance_sub"}}}, {},
Y
Yang Yang 已提交
149
          root_block);
Y
Yang Yang 已提交
150

Y
Yang Yang 已提交
151 152 153 154 155 156 157 158
    // backward
    AddOp("fill_constant", {}, {{"Out", {"l2_distance@GRAD"}}},
          {{"shape", std::vector<int>{batch_size, 1}}, {"value", float(1.0)}},
          root_block);
    AppendBackward(program, {});

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

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

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

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

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

197 198
    std::vector<int> dim{6};

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

Y
Yang Yang 已提交
206 207 208
    // flush
    program.Proto();

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

#endif