build_strategy.cc 14.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/* 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. */

#include "paddle/fluid/framework/details/build_strategy.h"

D
dzhwinter 已提交
17 18
#include <glog/logging.h>
#include <memory>
19
#include <unordered_set>
Q
Qiao Longfei 已提交
20
#include <utility>
21
#include "paddle/fluid/framework/details/reduce_op_handle.h"
22
#include "paddle/fluid/framework/ir/graph.h"
D
dzhwinter 已提交
23
#include "paddle/fluid/framework/ir/graph_helper.h"
W
WangZhen 已提交
24
#include "paddle/fluid/framework/ir/graph_to_program_pass.h"
25
#include "paddle/fluid/framework/ir/graph_viz_pass.h"
26 27 28
#include "paddle/fluid/framework/ir/memory_optimize_pass/memory_optimize_helper.h"
#include "paddle/fluid/framework/ir/multi_devices_graph_pass/multi_devices_graph_pass.h"
#include "paddle/fluid/framework/ir/multi_devices_graph_pass/multi_devices_graph_print_pass.h"
29

30 31
DECLARE_bool(use_mkldnn);

32 33 34 35
namespace paddle {
namespace framework {
namespace details {

36
static inline bool SeqOnlyAllReduceOps(const BuildStrategy &strategy) {
Y
Yancey1989 已提交
37 38
  // Should fix the allreduce op order if scheduling
  // them in multiple threads or processes to avoid hang.
Y
Yancey1989 已提交
39
  // NOTE: ParallelGraph would execute this pass on each graph, so
Y
Yancey1989 已提交
40
  // don't need to append it here.
Y
Yancey1989 已提交
41
  return (!strategy.enable_sequential_execution_ &&
Y
Yancey1989 已提交
42 43
          strategy.num_trainers_ > 1) &&
         !strategy.enable_parallel_graph_;
44 45
}

46 47 48 49
class ParallelExecutorPassBuilder : public ir::PassBuilder {
 public:
  explicit ParallelExecutorPassBuilder(const BuildStrategy &strategy)
      : ir::PassBuilder(), strategy_(strategy) {
C
chengduo 已提交
50 51 52 53 54 55 56 57
    // Add a graph viz pass to record a graph.
    if (!strategy_.debug_graphviz_path_.empty()) {
      auto viz_pass = AppendPass("graph_viz_pass");
      const std::string graph_path = string::Sprintf(
          "%s%s", strategy_.debug_graphviz_path_.c_str(), "_original_graph");
      viz_pass->Set<std::string>("graph_viz_path", new std::string(graph_path));
    }

Z
Zeng Jinle 已提交
58 59 60
    // Note(zcd): record_skip_memory_opt_vars_pass should be the first pass.
    AppendPass("record_skip_memory_opt_vars_pass");

61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
#ifdef PADDLE_WITH_MKLDNN
    if (FLAGS_use_mkldnn) {
      VLOG(5) << "Add mkldnn_placement_pass";
      AppendPass("mkldnn_placement_pass");
    } else if (!strategy_.mkldnn_enabled_op_types_.empty()) {
      LOG(WARNING)
          << "mkldnn_enabled_op_types specify the operator type list to "
             "use MKLDNN acceleration. It is null in default, means "
             "that all the operators supported by MKLDNN will be "
             "accelerated. And it should not be set when "
             "FLAGS_use_mkldnn=false.";
    }
#else
    PADDLE_ENFORCE(!FLAGS_use_mkldnn,
                   "Please compile with MKLDNN first to use MKLDNN");
#endif
S
sneaxiy 已提交
77
    if (strategy_.enable_sequential_execution_) {
C
chengduo 已提交
78
      VLOG(5) << "Add sequential_execution_pass";
S
sneaxiy 已提交
79 80 81
      AppendPass("sequential_execution_pass");
    }

Q
qingqing01 已提交
82 83 84 85 86
    // Add op fusion.
    if (strategy.sync_batch_norm_) {
      AppendPass("sync_batch_norm_pass");
    }

D
dzhwinter 已提交
87 88
    // Add op fusion.
    if (strategy.fuse_relu_depthwise_conv_) {
C
chengduo 已提交
89
      VLOG(5) << "Add fuse_relu_depthwise_conv_pass";
D
dzhwinter 已提交
90
      AppendPass("fuse_relu_depthwise_conv_pass");
D
dzhwinter 已提交
91
    }
92

D
dzhwinter 已提交
93 94 95 96 97 98
    // NOTE(dzhwinter): A note for automatical inplace.
    // 1. modify program desc passes should put
    // before inplace pass.
    // 2. manually configured inplace should put
    // before inplace_pass

D
dzhwinter 已提交
99 100
    // Add automatically inplace.
    if (strategy_.enable_inplace_) {
C
chengduo 已提交
101
      VLOG(5) << "Add inplace_pass";
D
dzhwinter 已提交
102
      AppendPass("inplace_pass");
S
sneaxiy 已提交
103 104
    }

C
chengduo 已提交
105
    if (strategy_.fuse_elewise_add_act_ops_) {
C
chengduo 已提交
106
      VLOG(5) << "Add fuse_elewise_add_act_pass";
C
chengduo 已提交
107 108 109 110 111
      AppendPass("fuse_elewise_add_act_pass");
    }

    // for single card training, fuse_all_reduce_ops is unnecessary.
    // alloc_continuous_space_for_grad_pass should be before of MultiDevPass.
C
chengduo 已提交
112
    if (strategy_.fuse_all_reduce_ops_) {
C
chengduo 已提交
113
      VLOG(5) << "Add alloc_continuous_space_for_grad_pass";
C
chengduo 已提交
114 115 116
      AppendPass("alloc_continuous_space_for_grad_pass");
    }

C
chengduo 已提交
117 118 119 120 121 122 123 124 125 126 127
    if (strategy_.fuse_all_optimizer_ops_) {
      if (strategy_.reduce_ == BuildStrategy::ReduceStrategy::kReduce ||
          strategy_.is_distribution_) {
        VLOG(3)
            << "Currently, fuse_all_optimizer_ops only works under AllReduce "
               "mode.";
        strategy_.fuse_all_optimizer_ops_ = false;
      } else {
        // NOTE: fuse_all_xx_ops will count the number of xx operator first,
        // if the number is zero, fuse_all_reduce_ops will do nothing.
        // Currently, only one type of optimization algorithm can be fused.
C
chengduo 已提交
128
        VLOG(5) << "Add fuse_adam_op_pass";
C
chengduo 已提交
129
        AppendPass("fuse_adam_op_pass");
C
chengduo 已提交
130
        VLOG(5) << "Add fuse_sgd_op_pass";
C
chengduo 已提交
131
        AppendPass("fuse_sgd_op_pass");
C
chengduo 已提交
132 133
        VLOG(5) << "Add fuse_momentum_op_pass";
        AppendPass("fuse_momentum_op_pass");
C
chengduo 已提交
134 135 136
      }
    }

X
Xin Pan 已提交
137
    // Add a graph viz pass to record a graph.
C
chengduo 已提交
138
    if (!strategy.debug_graphviz_path_.empty()) {
139 140
      auto viz_pass = AppendPass("graph_viz_pass");
      const std::string graph_path = string::Sprintf(
C
chengduo 已提交
141
          "%s%s", strategy_.debug_graphviz_path_.c_str(), "_fused_graph");
142 143 144
      viz_pass->Set<std::string>("graph_viz_path", new std::string(graph_path));
    }

145 146 147 148
    CollectiveContext *context = CollectiveContext::GetInstance();
    context->endpoints_ = strategy_.trainers_endpoints_;
    context->trainer_id_ = strategy_.trainer_id_;
    PADDLE_ENFORCE(strategy_.trainer_id_ >= 0, "trainer_id_ >= 0");
149
    if (strategy_.trainer_id_ > 0 && strategy_.trainers_endpoints_.size() > 0) {
150 151 152 153 154 155
      PADDLE_ENFORCE((unsigned)(strategy_.trainer_id_) <
                         strategy_.trainers_endpoints_.size(),
                     "trainer_id_ < endpoints_ size");
    }
    VLOG(1) << "CollectiveContext:" << context->String();

D
dzhwinter 已提交
156 157 158 159 160
    // NOTE(dzh): memory optimize should be a runtime pass.
    // However, after multi_devices_pass, VarHandle, OpHandle is
    // the de-fact IR, any reuse on Graph is meaningless.
    // A side-effect of that, memory optimize cannot forsee the fetched vars
    // , so fetchlist should be set persistable before call the Run interface.
C
chengduo 已提交
161
    if (strategy_.memory_optimize_) {
C
chengduo 已提交
162
      VLOG(5) << "Add memory_optimize_pass";
C
chengduo 已提交
163
      AppendPass("memory_optimize_pass");
D
dzhwinter 已提交
164
    }
165

166 167 168 169
    // runtime_context_cache pass should be the last pass to enable the attr of
    // all original and fused operators. But no operators can be enabled this
    // attr if putting it after MultiDevPass.
    if (strategy_.cache_runtime_context_) {
C
chengduo 已提交
170
      VLOG(5) << "Add runtime_context_cache_pass";
171 172 173
      AppendPass("runtime_context_cache_pass");
    }

C
chengduo 已提交
174
    AppendMultiDevPass(strategy_);
175

C
chengduo 已提交
176
    if (strategy_.fuse_all_reduce_ops_) {
C
chengduo 已提交
177 178
      // NOTE: fuse_all_reduce_ops will count the number of all_reduce operator
      // first, if the number is zero, fuse_all_reduce_ops will do nothing.
C
chengduo 已提交
179
      VLOG(5) << "Add fuse_all_reduce_op_pass";
C
chengduo 已提交
180 181 182
      AppendPass("fuse_all_reduce_op_pass");
    }

X
Xin Pan 已提交
183
    // Add a graph print pass to record a graph with device info.
184 185
    if (!strategy_.debug_graphviz_path_.empty()) {
      auto multi_devices_print_pass = AppendPass("multi_devices_print_pass");
D
dzhwinter 已提交
186 187 188
      const std::string graph_path =
          string::Sprintf("%s%s", strategy_.debug_graphviz_path_.c_str(),
                          "_multi_devices_graph");
189
      multi_devices_print_pass->Set<std::string>(ir::kGraphvizPath,
D
dzhwinter 已提交
190
                                                 new std::string(graph_path));
191 192
      multi_devices_print_pass->Set<ir::GraphvizSSAGraphPrinter>(
          "graph_printer", new ir::GraphvizSSAGraphPrinter);
193 194
    }

195 196 197 198 199
    // experimental shows that the program will be faster if append
    // all_reduce_deps_pass here.
    if (!strategy_.enable_parallel_graph_ &&
        (SeqOnlyAllReduceOps(strategy_) ||
         strategy.reduce_ == BuildStrategy::ReduceStrategy::kAllReduce)) {
C
chengduo 已提交
200
      VLOG(5) << "Add all_reduce_deps_pass";
201 202 203
      AppendPass("all_reduce_deps_pass");
    }

S
sneaxiy 已提交
204
    if (strategy_.remove_unnecessary_lock_) {
C
chengduo 已提交
205
      VLOG(5) << "Add modify_op_lock_and_record_event_pass";
S
sneaxiy 已提交
206 207
      AppendPass("modify_op_lock_and_record_event_pass");
    }
208 209 210

    // Verify that the graph is correct for multi-device executor.
    AppendPass("multi_devices_check_pass");
211 212
  }

213 214
  // Convert graph to run on multi-devices.
  void AppendMultiDevPass(const BuildStrategy &strategy) {
C
chengduo 已提交
215
    ir::Pass *multi_devices_pass = nullptr;
Q
can run  
Qiao Longfei 已提交
216

Q
Qiao Longfei 已提交
217 218 219
    if (strategy_.async_mode_) {
      multi_devices_pass = AppendPass("async_multi_devices_pass").get();
    } else if (strategy_.is_distribution_) {
C
chengduo 已提交
220
      VLOG(5)
221
          << "Add dist_multi_devices_pass, multi device parameter server mode";
222 223 224
      multi_devices_pass = AppendPass("dist_multi_devices_pass").get();
    } else {
      if (strategy.reduce_ == BuildStrategy::ReduceStrategy::kAllReduce) {
C
chengduo 已提交
225
        VLOG(5) << "Add all_reduce_mode_multi_devices_pass";
226
        multi_devices_pass =
C
chengduo 已提交
227
            AppendPass("all_reduce_mode_multi_devices_pass").get();
228
      } else if (strategy.reduce_ == BuildStrategy::ReduceStrategy::kReduce) {
C
chengduo 已提交
229
        VLOG(5) << "Add reduce_mode_multi_devices_pass";
230 231 232 233 234 235 236 237 238
        multi_devices_pass = AppendPass("reduce_mode_multi_devices_pass").get();
      } else {
        PADDLE_THROW("Unknown reduce strategy.");
      }
    }
    multi_devices_pass->SetNotOwned<const BuildStrategy>("strategy",
                                                         &strategy_);
  }

239 240 241 242
 private:
  BuildStrategy strategy_;
};

243
std::shared_ptr<ir::PassBuilder> BuildStrategy::CreatePassesFromStrategy(
X
Xin Pan 已提交
244 245
    bool finalize_strategy) const {
  if (is_finalized_) {
246 247
    return pass_builder_;
  }
248
  pass_builder_.reset(new ParallelExecutorPassBuilder(*this));
X
Xin Pan 已提交
249 250
  if (finalize_strategy) {
    is_finalized_ = true;
251
  }
X
fix  
Xin Pan 已提交
252
  return pass_builder_;
253 254
}

255
bool BuildStrategy::IsMultiDevPass(const std::string &pass_name) const {
256
  return framework::ir::MultiDevSSAGraphBuilder().count(pass_name) > 0;
257 258
}

259 260 261 262 263
ir::Graph *BuildStrategy::Apply(ir::Graph *graph,
                                const std::vector<platform::Place> &places,
                                const std::string &loss_var_name,
                                const std::vector<Scope *> &local_scopes,
                                const size_t &nranks,
P
peizhilin 已提交
264
#if defined(PADDLE_WITH_CUDA) && !defined(_WIN32)
265 266
                                const bool use_cuda,
                                platform::NCCLContextMap *nccl_ctxs) const {
267
#else
268
                                const bool use_cuda) const {
269
#endif
270
  VLOG(3) << "apply all passes";
271 272
  // Create a default one if not finalized by user.
  CreatePassesFromStrategy(false);
X
fix  
Xin Pan 已提交
273 274

  for (std::shared_ptr<ir::Pass> &pass : pass_builder_->AllPasses()) {
G
gongweibao 已提交
275
    VLOG(3) << "BuildStrategy::Apply pass:" << pass->Type();
276 277 278
    if (IsMultiDevPass(pass->Type())) {
      pass->Erase(kPlaces);
      pass->SetNotOwned<const std::vector<platform::Place>>(kPlaces, &places);
279 280
      pass->Erase(ir::kLossVarName);
      pass->SetNotOwned<const std::string>(ir::kLossVarName, &loss_var_name);
281 282
      pass->Erase(kLocalScopes);
      pass->SetNotOwned<const std::vector<Scope *>>(kLocalScopes,
X
fix  
Xin Pan 已提交
283
                                                    &local_scopes);
284 285
      pass->Erase(ir::kNRanks);
      pass->Set<size_t>(ir::kNRanks, new size_t(nranks));
Y
Yancey1989 已提交
286

P
peizhilin 已提交
287
#if defined(PADDLE_WITH_CUDA) && !defined(_WIN32)
X
fix  
Xin Pan 已提交
288
      platform::NCCLContextMap *nctx = use_cuda ? nccl_ctxs : nullptr;
C
chengduo 已提交
289 290
      pass->Erase(kNCCLCtxs);
      pass->SetNotOwned<platform::NCCLContextMap>(kNCCLCtxs, nctx);
291
#endif
C
chengduo 已提交
292 293 294
    } else if (pass->Type() == "alloc_continuous_space_for_grad_pass" ||
               pass->Type() == "fuse_adam_op_pass" ||
               pass->Type() == "fuse_sgd_op_pass" ||
C
chengduo 已提交
295
               pass->Type() == "fuse_momentum_op_pass" ||
C
chengduo 已提交
296
               pass->Type() == "fuse_all_reduce_op_pass") {
C
chengduo 已提交
297 298 299 300 301
      pass->Erase(kPlaces);
      pass->SetNotOwned<const std::vector<platform::Place>>(kPlaces, &places);
      pass->Erase(kLocalScopes);
      pass->SetNotOwned<const std::vector<Scope *>>(kLocalScopes,
                                                    &local_scopes);
C
chengduo 已提交
302
      if (pass->Type() == "fuse_all_reduce_op_pass") {
C
chengduo 已提交
303
#if defined(PADDLE_WITH_CUDA) && !defined(_WIN32)
C
chengduo 已提交
304 305 306
        platform::NCCLContextMap *nctx = use_cuda ? nccl_ctxs : nullptr;
        pass->Erase(kNCCLCtxs);
        pass->SetNotOwned<platform::NCCLContextMap>(kNCCLCtxs, nctx);
307
#endif
C
chengduo 已提交
308
      }
C
chengduo 已提交
309 310 311 312 313 314
    } else if (pass->Type() == "alloc_continuous_space_for_grad_pass") {
      pass->Erase(kPlaces);
      pass->SetNotOwned<const std::vector<platform::Place>>(kPlaces, &places);
      pass->Erase(kLocalScopes);
      pass->SetNotOwned<const std::vector<Scope *>>(kLocalScopes,
                                                    &local_scopes);
S
sneaxiy 已提交
315
    } else if (pass->Type() == "sequential_execution_pass") {
316 317
      LOG(INFO) << "set enable_sequential_execution:"
                << enable_sequential_execution_;
318
    } else if (pass->Type() == "all_reduce_deps_pass") {
319 320
      LOG(INFO) << "SeqOnlyAllReduceOps:" << SeqOnlyAllReduceOps(*this)
                << ", num_trainers:" << num_trainers_;
321 322 323 324 325 326
    } else if (pass->Type() == "fuse_relu_depthwise_conv_pass") {
      if (!use_cuda) {
        LOG(WARNING) << "fuse_relu_depthwise_conv_pass is only supported on "
                        "GPU, skipped.";
        continue;
      }
327
    } else if (pass->Type() == "inplace_pass") {
328 329
      pass->Erase(ir::kUseCuda);
      pass->Set<bool>(ir::kUseCuda, new bool(use_cuda));
330 331 332
    } else if (pass->Type() == "mkldnn_placement_pass") {
      pass->Set("mkldnn_enabled_op_types",
                new std::unordered_set<std::string>(mkldnn_enabled_op_types_));
X
fix  
Xin Pan 已提交
333
    }
334
    VLOG(3) << "Start Apply Pass " << pass->Type();
335
    graph = pass->Apply(graph);
336
    VLOG(3) << "Finish Apply Pass " << pass->Type();
X
fix  
Xin Pan 已提交
337
  }
Q
Qiao Longfei 已提交
338
  VLOG(3) << "All Passes Applied";
339 340
  return graph;
}
D
dzhwinter 已提交
341

342 343 344 345
}  // namespace details
}  // namespace framework
}  // namespace paddle

Q
qingqing01 已提交
346
USE_PASS(sync_batch_norm_pass);
347
USE_PASS(fuse_relu_depthwise_conv_pass);
348 349
USE_PASS(fuse_elewise_add_act_pass);
USE_PASS(graph_viz_pass);
350
USE_PASS(multi_batch_merge_pass);
351
USE_PASS(reduce_mode_multi_devices_pass);
C
chengduo 已提交
352
USE_PASS(all_reduce_mode_multi_devices_pass);
353
USE_PASS(dist_multi_devices_pass);
354 355
USE_PASS(multi_devices_check_pass);
USE_PASS(multi_devices_print_pass);
D
dzhwinter 已提交
356
USE_PASS(memory_optimize_pass);
S
sneaxiy 已提交
357
USE_PASS(sequential_execution_pass);
358
USE_PASS(all_reduce_deps_pass);
S
sneaxiy 已提交
359
USE_PASS(modify_op_lock_and_record_event_pass);
D
dzhwinter 已提交
360
USE_PASS(inplace_pass);
M
minqiyang 已提交
361
USE_PASS(lock_free_optimize_pass);
C
chengduo 已提交
362
USE_PASS(alloc_continuous_space_for_grad_pass);
W
WangZhen 已提交
363
USE_PASS(graph_to_program_pass);
C
chengduo 已提交
364 365
USE_PASS(fuse_adam_op_pass);
USE_PASS(fuse_sgd_op_pass);
C
chengduo 已提交
366
USE_PASS(fuse_momentum_op_pass);
C
chengduo 已提交
367
USE_PASS(fuse_all_reduce_op_pass);
368
USE_PASS(runtime_context_cache_pass);
Z
Zeng Jinle 已提交
369
USE_PASS(record_skip_memory_opt_vars_pass);
370 371 372
#ifdef PADDLE_WITH_MKLDNN
USE_PASS(mkldnn_placement_pass);
#endif