selected_rows_functor.cc 37.5 KB
Newer Older
1
/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved.
2 3 4 5 6 7 8 9 10 11 12 13 14

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

Y
Yi Wang 已提交
15
#include "paddle/fluid/operators/math/selected_rows_functor.h"
16

17
#include "paddle/fluid/framework/mixed_vector.h"
18
#include "paddle/fluid/platform/device/device_wrapper.h"
19

L
lidanqing 已提交
20 21 22 23
#ifdef PADDLE_WITH_MKLDNN
#include "paddle/fluid/operators/mkldnn/axpy_handler.h"
#endif

24 25 26 27
namespace paddle {
namespace operators {
namespace math {
template <typename T>
Q
QI JUN 已提交
28 29
struct SelectedRowsAdd<platform::CPUDeviceContext, T> {
  void operator()(const platform::CPUDeviceContext& context,
30
                  const phi::SelectedRows& input1,
31 32
                  const phi::SelectedRows& input2,
                  phi::SelectedRows* output) {
33
    auto in1_height = input1.height();
34
    PADDLE_ENFORCE_EQ(
35 36
        in1_height,
        input2.height(),
37
        platform::errors::InvalidArgument("The two inputs height must be equal."
38
                                          "But received first input height  = "
39
                                          "[%d], second input height = [%d]",
40 41
                                          in1_height,
                                          input2.height()));
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
    output->set_height(in1_height);

    auto& in1_rows = input1.rows();
    auto& in2_rows = input2.rows();
    std::vector<int64_t> out_rows;
    out_rows.reserve(in1_rows.size() + in2_rows.size());

    // concat rows
    out_rows.insert(out_rows.end(), in1_rows.begin(), in1_rows.end());
    out_rows.insert(out_rows.end(), in2_rows.begin(), in2_rows.end());
    output->set_rows(out_rows);

    auto* out_value = output->mutable_value();
    auto& in1_value = input1.value();
    auto& in2_value = input2.value();

    auto in1_row_numel = in1_value.numel() / in1_rows.size();
59
    PADDLE_ENFORCE_EQ(
60 61
        in1_row_numel,
        in2_value.numel() / in2_rows.size(),
62 63
        platform::errors::InvalidArgument(
            "The two inputs width must be equal."
64
            "But received first input width = [%d], second input width = [%d]",
65 66
            in1_row_numel,
            in2_value.numel() / in2_rows.size()));
67
    PADDLE_ENFORCE_EQ(
68 69
        in1_row_numel,
        out_value->numel() / out_rows.size(),
70 71
        platform::errors::InvalidArgument(
            "The input and oupput width must be equal."
72
            "But received input width = [%d], output width = [%d]",
73 74
            in1_row_numel,
            out_value->numel() / out_rows.size()));
75 76

    auto in1_place = input1.place();
77 78
    PADDLE_ENFORCE_EQ(platform::is_cpu_place(in1_place),
                      true,
79
                      platform::errors::InvalidArgument(
80
                          "The running environment is not on the CPU place."));
81
    auto in2_place = input2.place();
82 83
    PADDLE_ENFORCE_EQ(platform::is_cpu_place(in2_place),
                      true,
84
                      platform::errors::InvalidArgument(
85
                          "The running environment is not on the CPU place."));
86
    auto out_place = context.GetPlace();
87 88
    PADDLE_ENFORCE_EQ(platform::is_cpu_place(out_place),
                      true,
89
                      platform::errors::InvalidArgument(
90
                          "The running environment is not on the CPU place."));
91 92 93

    auto* out_data = out_value->data<T>();
    auto* in1_data = in1_value.data<T>();
94 95 96 97
    memory::Copy(out_place,
                 out_data,
                 in1_place,
                 in1_data,
98 99 100
                 in1_value.numel() * sizeof(T));

    auto* in2_data = in2_value.data<T>();
101 102 103 104
    memory::Copy(out_place,
                 out_data + in1_value.numel(),
                 in2_place,
                 in2_data,
105 106 107 108
                 in2_value.numel() * sizeof(T));
  }
};

Q
QI JUN 已提交
109 110
template struct SelectedRowsAdd<platform::CPUDeviceContext, float>;
template struct SelectedRowsAdd<platform::CPUDeviceContext, double>;
111 112

template <typename T>
Q
QI JUN 已提交
113 114
struct SelectedRowsAddTensor<platform::CPUDeviceContext, T> {
  void operator()(const platform::CPUDeviceContext& context,
115
                  const phi::SelectedRows& input1,
116 117
                  const framework::Tensor& input2,
                  framework::Tensor* output) {
118
    auto in1_height = input1.height();
119 120
    const auto& in2_dims = input2.dims();
    const auto& out_dims = output->dims();
121
    PADDLE_ENFORCE_EQ(
122 123
        in1_height,
        in2_dims[0],
124
        platform::errors::InvalidArgument("The two inputs height must be equal."
125
                                          "But received first input height = "
126
                                          "[%d], second input height = [%d]",
127 128
                                          in1_height,
                                          in2_dims[0]));
129
    PADDLE_ENFORCE_EQ(
130 131
        in1_height,
        out_dims[0],
132 133
        platform::errors::InvalidArgument(
            "The input and output height must be equal."
134
            "But received input height = [%d], output height = [%d]",
135 136
            in1_height,
            out_dims[0]));
137 138 139 140 141

    auto& in1_value = input1.value();
    auto& in1_rows = input1.rows();

    int64_t in1_row_numel = in1_value.numel() / in1_rows.size();
142
    PADDLE_ENFORCE_EQ(
143 144
        in1_row_numel,
        input2.numel() / in1_height,
145 146
        platform::errors::InvalidArgument(
            "The two inputs width must be equal."
147
            "But received first input width = [%d], second input width = [%d]",
148 149
            in1_row_numel,
            input2.numel() / in1_height));
150
    PADDLE_ENFORCE_EQ(
151 152
        in1_row_numel,
        output->numel() / in1_height,
153 154
        platform::errors::InvalidArgument(
            "The input and output width must be equal."
155
            "But received input width = [%d], output width = [%d]",
156 157
            in1_row_numel,
            output->numel() / in1_height));
158

159
    phi::funcs::SetConstant<platform::CPUDeviceContext, T> functor;
160 161 162 163 164 165 166 167 168 169 170 171 172 173
    functor(context, output, 0.0);

    auto* in1_data = in1_value.data<T>();
    auto* out_data = output->data<T>();

    for (size_t i = 0; i < in1_rows.size(); i++) {
      for (int64_t j = 0; j < in1_row_numel; j++) {
        out_data[in1_rows[i] * in1_row_numel + j] +=
            in1_data[i * in1_row_numel + j];
      }
    }

    auto out_eigen = framework::EigenVector<T>::Flatten(*output);
    auto in2_eigen = framework::EigenVector<T>::Flatten(input2);
Q
QI JUN 已提交
174
    out_eigen.device(*context.eigen_device()) = out_eigen + in2_eigen;
175 176 177
  }
};

Q
QI JUN 已提交
178 179
template struct SelectedRowsAddTensor<platform::CPUDeviceContext, float>;
template struct SelectedRowsAddTensor<platform::CPUDeviceContext, double>;
Q
QI JUN 已提交
180 181

template <typename T>
Q
QI JUN 已提交
182 183
struct SelectedRowsAddTo<platform::CPUDeviceContext, T> {
  void operator()(const platform::CPUDeviceContext& context,
184 185
                  const phi::SelectedRows& input1,
                  const int64_t input2_offset,
186
                  phi::SelectedRows* input2) {
Q
QI JUN 已提交
187
    auto in1_height = input1.height();
188
    PADDLE_ENFORCE_EQ(
189 190
        in1_height,
        input2->height(),
191
        platform::errors::InvalidArgument("The two inputs height must be equal."
192
                                          "But received first input height = "
193
                                          "[%d], second input height = [%d]",
194 195
                                          in1_height,
                                          input2->height()));
Q
QI JUN 已提交
196 197 198 199 200 201 202 203

    auto& in1_rows = input1.rows();
    auto& in2_rows = *(input2->mutable_rows());

    auto& in1_value = input1.value();
    auto* in2_value = input2->mutable_value();

    // concat rows
204 205
    paddle::framework::MixVector<int64_t> mixv_in2_rows(&in2_rows);
    mixv_in2_rows.Extend(in1_rows.begin(), in1_rows.end());
Q
QI JUN 已提交
206 207

    auto in1_place = input1.place();
208 209
    PADDLE_ENFORCE_EQ(platform::is_cpu_place(in1_place),
                      true,
210
                      platform::errors::InvalidArgument(
211
                          "The running environment is not on the CPU place."));
Q
QI JUN 已提交
212
    auto in2_place = input2->place();
213 214
    PADDLE_ENFORCE_EQ(platform::is_cpu_place(in2_place),
                      true,
215
                      platform::errors::InvalidArgument(
216
                          "The running environment is not on the CPU place."));
Q
QI JUN 已提交
217 218 219

    auto* in1_data = in1_value.data<T>();
    auto* in2_data = in2_value->data<T>();
220 221 222 223
    memory::Copy(in2_place,
                 in2_data + input2_offset,
                 in1_place,
                 in1_data,
Q
QI JUN 已提交
224 225 226 227
                 in1_value.numel() * sizeof(T));
  }
};

Q
QI JUN 已提交
228 229 230 231
template struct SelectedRowsAddTo<platform::CPUDeviceContext, float>;
template struct SelectedRowsAddTo<platform::CPUDeviceContext, double>;
template struct SelectedRowsAddTo<platform::CPUDeviceContext, int>;
template struct SelectedRowsAddTo<platform::CPUDeviceContext, int64_t>;
Q
QI JUN 已提交
232

M
minqiyang 已提交
233 234 235
template <typename T>
struct SelectedRowsSumTo<platform::CPUDeviceContext, T> {
  void operator()(const platform::CPUDeviceContext& context,
236
                  const std::vector<phi::SelectedRows*>& input1,
M
minqiyang 已提交
237
                  const std::vector<int64_t>& input2_offsets,
238
                  phi::SelectedRows* input2) {
M
minqiyang 已提交
239 240 241 242 243 244
    // Ensure all selected rows have the same height
    size_t size = 0u;
    for (auto iter = input1.begin(); iter != input1.end(); ++iter) {
      auto& in_rows = (*iter)->rows();
      size += in_rows.end() - in_rows.begin();
      auto in1_height = (*iter)->height();
245 246
      PADDLE_ENFORCE_EQ(in1_height,
                        input2->height(),
247 248
                        platform::errors::InvalidArgument(
                            "The two inputs height must be equal."
249
                            "But received first input height = [%d], second "
250
                            "input height = [%d]",
251 252
                            in1_height,
                            input2->height()));
M
minqiyang 已提交
253 254 255 256 257 258 259 260 261 262 263 264
    }
    // concat rows
    std::vector<int64_t> in2_rows;
    in2_rows.reserve(in2_rows.size() + size);
    for (auto iter = input1.begin(); iter != input1.end(); ++iter) {
      const framework::Vector<int64_t>& in_rows = (*iter)->rows();
      in2_rows.insert(in2_rows.end(), in_rows.begin(), in_rows.end());
    }
    input2->set_rows(in2_rows);

    auto* in2_value = input2->mutable_value();
    auto* in2_data = in2_value->data<T>();
265
    auto blas = phi::funcs::GetBlas<platform::CPUDeviceContext, T>(context);
M
minqiyang 已提交
266 267 268 269 270 271 272 273 274 275 276 277 278
    size_t offset = 0u;
    for (size_t i = 0u; i != input1.size(); ++i) {
      auto& in_value = input1[i]->value();
      const auto* in_data = in_value.data<T>();
      offset += input2_offsets[i];
      blas.VCOPY(in_value.numel(), in_data, in2_data + offset);
    }
  }
};

template struct SelectedRowsSumTo<platform::CPUDeviceContext, float>;
template struct SelectedRowsSumTo<platform::CPUDeviceContext, double>;

Q
QI JUN 已提交
279
template <typename T>
Q
QI JUN 已提交
280 281
struct SelectedRowsAddToTensor<platform::CPUDeviceContext, T> {
  void operator()(const platform::CPUDeviceContext& context,
282 283
                  const phi::SelectedRows& input1,
                  framework::Tensor* input2) {
Q
Qiao Longfei 已提交
284
    if (UNLIKELY(input1.rows().size() == 0)) {
285 286 287
      LOG(WARNING) << "input selected rows is empty!";
      return;
    }
Q
QI JUN 已提交
288
    auto in1_height = input1.height();
289
    const auto& in2_dims = input2->dims();
290
    PADDLE_ENFORCE_EQ(
291 292
        in1_height,
        in2_dims[0],
293
        platform::errors::InvalidArgument("The two inputs height must be equal."
294
                                          "But received first input height = "
295
                                          "[%d], second input height = [%d]",
296 297
                                          in1_height,
                                          in2_dims[0]));
Q
QI JUN 已提交
298 299 300 301 302

    auto& in1_value = input1.value();
    auto& in1_rows = input1.rows();

    int64_t in1_row_numel = in1_value.numel() / in1_rows.size();
303
    PADDLE_ENFORCE_EQ(
304 305
        in1_row_numel,
        input2->numel() / in1_height,
306 307
        platform::errors::InvalidArgument(
            "The two inputs width must be equal."
308
            "But received first input width = [%d], second input width = [%d]",
309 310
            in1_row_numel,
            input2->numel() / in1_height));
Q
QI JUN 已提交
311 312 313 314 315 316 317 318 319 320 321 322 323

    auto* in1_data = in1_value.data<T>();
    auto* input2_data = input2->data<T>();

    for (size_t i = 0; i < in1_rows.size(); i++) {
      for (int64_t j = 0; j < in1_row_numel; j++) {
        input2_data[in1_rows[i] * in1_row_numel + j] +=
            in1_data[i * in1_row_numel + j];
      }
    }
  }
};

H
hong 已提交
324 325 326
template <typename T>
struct SelectedRowsAddToTensor<phi::CPUContext, T> {
  void operator()(const phi::CPUContext& context,
327 328
                  const phi::SelectedRows& input1,
                  framework::Tensor* input2) {
H
hong 已提交
329 330 331 332 333
    if (UNLIKELY(input1.rows().size() == 0)) {
      LOG(WARNING) << "input selected rows is empty!";
      return;
    }
    auto in1_height = input1.height();
334
    const auto& in2_dims = input2->dims();
H
hong 已提交
335
    PADDLE_ENFORCE_EQ(
336 337
        in1_height,
        in2_dims[0],
H
hong 已提交
338
        platform::errors::InvalidArgument("The two inputs height must be equal."
339
                                          "But received first input height = "
H
hong 已提交
340
                                          "[%d], second input height = [%d]",
341 342
                                          in1_height,
                                          in2_dims[0]));
H
hong 已提交
343 344 345 346 347 348

    auto& in1_value = input1.value();
    auto& in1_rows = input1.rows();

    int64_t in1_row_numel = in1_value.numel() / in1_rows.size();
    PADDLE_ENFORCE_EQ(
349 350
        in1_row_numel,
        input2->numel() / in1_height,
H
hong 已提交
351 352
        platform::errors::InvalidArgument(
            "The two inputs width must be equal."
353
            "But received first input width = [%d], second input width = [%d]",
354 355
            in1_row_numel,
            input2->numel() / in1_height));
H
hong 已提交
356 357 358 359 360 361 362 363 364 365 366 367 368

    auto* in1_data = in1_value.data<T>();
    auto* input2_data = input2->data<T>();

    for (size_t i = 0; i < in1_rows.size(); i++) {
      for (int64_t j = 0; j < in1_row_numel; j++) {
        input2_data[in1_rows[i] * in1_row_numel + j] +=
            in1_data[i * in1_row_numel + j];
      }
    }
  }
};

Q
QI JUN 已提交
369 370 371 372
template struct SelectedRowsAddToTensor<platform::CPUDeviceContext, float>;
template struct SelectedRowsAddToTensor<platform::CPUDeviceContext, double>;
template struct SelectedRowsAddToTensor<platform::CPUDeviceContext, int>;
template struct SelectedRowsAddToTensor<platform::CPUDeviceContext, int64_t>;
373 374
template struct SelectedRowsAddToTensor<platform::CPUDeviceContext,
                                        platform::bfloat16>;
375

H
hong 已提交
376 377 378 379 380
template struct SelectedRowsAddToTensor<phi::CPUContext, float>;
template struct SelectedRowsAddToTensor<phi::CPUContext, double>;
template struct SelectedRowsAddToTensor<phi::CPUContext, int>;
template struct SelectedRowsAddToTensor<phi::CPUContext, int64_t>;
template struct SelectedRowsAddToTensor<phi::CPUContext, platform::bfloat16>;
T
typhoonzero 已提交
381 382 383 384 385 386 387 388
// This is a separated namespace for manipulate SelectedRows typed
// data. Like merge duplicated rows, adding two SelectedRows etc.
//
// Another group of functors is called "scatter updates", which means
// use SelectedRows to update a dense tensor with different Ops, like
// add or mul.
namespace scatter {

389
template <typename T, typename DeviceContext>
390
typename std::enable_if<!std::is_integral<T>::value>::type elementwise_add_to(
391 392 393
    phi::funcs::BlasT<DeviceContext, T>* blas,
    size_t data_len,
    const T* in,
394
    T* out) {
395
  blas->AXPY(data_len, T(1.f), in, out);
Q
Qiao Longfei 已提交
396 397
}

398
template <typename T, typename DeviceContext>
399
typename std::enable_if<std::is_integral<T>::value>::type elementwise_add_to(
400 401 402
    phi::funcs::BlasT<DeviceContext, T>* blas,
    size_t data_len,
    const T* in,
403
    T* out) {
T
Tao Luo 已提交
404
  for (size_t i = 0; i < data_len; i++) {
Q
Qiao Longfei 已提交
405 406
    out[i] += in[i];
  }
T
typhoonzero 已提交
407 408
}

409
template <typename T, typename DeviceContext>
410
typename std::enable_if<std::is_same<T, platform::bfloat16>::value>::type
411
add_sparse_inputs(const std::vector<const phi::SelectedRows*>& inputs,
412
                  const std::unordered_map<int64_t, size_t>& rows_to_id,
413 414
                  int64_t input_width,
                  const DeviceContext& context,
415
                  T* out_data) {
416
#ifndef PADDLE_WITH_MKLDNN
417
  auto blas = phi::funcs::GetBlas<DeviceContext, T>(context);
418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435
#endif
  for (auto* input : inputs) {
    if (input->rows().size() == 0) {
      continue;
    }
    auto* input_data = input->value().data<T>();
    auto& input_rows = input->rows();

#ifdef PADDLE_WITH_MKLDNN
    OneDNNAXPYHandler<T> axpy_handler(input_width, T(1.f));
    for (size_t i = 0; i < input_rows.size(); i++) {
      size_t out_i = rows_to_id.at(input_rows[i]);
      axpy_handler(&input_data[i * input_width],
                   &out_data[out_i * input_width]);
    }
#else
    for (size_t i = 0; i < input_rows.size(); i++) {
      size_t out_i = rows_to_id.at(input_rows[i]);
436 437 438 439
      elementwise_add_to<T, DeviceContext>(&blas,
                                           static_cast<size_t>(input_width),
                                           &input_data[i * input_width],
                                           &out_data[out_i * input_width]);
440 441 442 443 444
    }
#endif
  }
}

445
template <typename T, typename DeviceContext>
446
typename std::enable_if<!std::is_same<T, platform::bfloat16>::value>::type
447
add_sparse_inputs(const std::vector<const phi::SelectedRows*>& inputs,
448
                  const std::unordered_map<int64_t, size_t>& rows_to_id,
449 450
                  int64_t input_width,
                  const DeviceContext& context,
451
                  T* out_data) {
452
  VLOG(4) << "[CPU] add_sparse_inputs <" << typeid(T).name();
453
  auto blas = phi::funcs::GetBlas<DeviceContext, T>(context);
454 455 456 457 458 459 460 461 462
  for (auto* input : inputs) {
    if (input->rows().size() == 0) {
      continue;
    }
    auto* input_data = input->value().data<T>();
    auto& input_rows = input->rows();

    for (size_t i = 0; i < input_rows.size(); i++) {
      size_t out_i = rows_to_id.at(input_rows[i]);
463 464 465 466
      elementwise_add_to<T, DeviceContext>(&blas,
                                           static_cast<size_t>(input_width),
                                           &input_data[i * input_width],
                                           &out_data[out_i * input_width]);
467 468 469 470
    }
  }
}

471 472 473
template <typename DeviceContext, typename T>
struct MergeAddImpl {
  phi::SelectedRows operator()(const DeviceContext& context,
474 475 476
                               const phi::SelectedRows& input,
                               const bool sorted_result = false) {
    phi::SelectedRows out;
477
    (*this)(context, input, &out, sorted_result);
S
sneaxiy 已提交
478 479 480
    return out;
  }

481 482 483 484
  void operator()(const DeviceContext& context,
                  const phi::SelectedRows& input,
                  phi::SelectedRows* output,
                  const bool sorted_result = false) {
485
    std::vector<const phi::SelectedRows*> inputs;
486
    inputs.push_back(&input);
487
    (*this)(context, inputs, output, sorted_result);
488
  }
T
typhoonzero 已提交
489

490
  void operator()(const DeviceContext& context,
491
                  const std::vector<const phi::SelectedRows*>& inputs,
492 493
                  phi::SelectedRows* output,
                  const bool sorted_result = false) {
Q
Qiao Longfei 已提交
494
    if (inputs.size() == 0) {
M
minqiyang 已提交
495
      VLOG(3) << "no input! return";
Q
Qiao Longfei 已提交
496 497
      return;
    }
498
    const phi::SelectedRows* has_value_input = nullptr;
Q
Qiao Longfei 已提交
499
    for (auto* in : inputs) {
Q
Qiao Longfei 已提交
500
      if (in->rows().size() > 0) {
Q
Qiao Longfei 已提交
501 502 503 504 505
        has_value_input = in;
        break;
      }
    }
    if (has_value_input == nullptr) {
M
minqiyang 已提交
506
      VLOG(3) << "no input has value! just return" << std::endl;
Q
Qiao Longfei 已提交
507 508 509 510
      return;
    }
    auto input_width = has_value_input->value().dims()[1];
    auto input_height = has_value_input->height();
511
    phi::SelectedRows& out = *output;
512
    std::set<int64_t> merged_row_set;
513
    size_t row_num = 0;
514
    for (auto* input : inputs) {
Q
Qiao Longfei 已提交
515
      if (input->rows().size() == 0) {
Q
Qiao Longfei 已提交
516 517
        continue;
      }
518 519
      PADDLE_ENFORCE_EQ(input_width,
                        input->value().dims()[1],
520 521 522
                        platform::errors::InvalidArgument(
                            "All inputs should have same "
                            "dimension except for the first one."));
523 524
      PADDLE_ENFORCE_EQ(input_height,
                        input->height(),
525 526
                        platform::errors::InvalidArgument(
                            "All inputs should have same height."));
527
      row_num += input->rows().size();
528 529
      merged_row_set.insert(input->rows().begin(), input->rows().end());
    }
530

531
    out.set_height(input_height);
T
wip  
typhoonzero 已提交
532
    out.mutable_value()->mutable_data<T>(
533
        phi::make_ddim(
534
            {static_cast<int64_t>(merged_row_set.size()), input_width}),
T
typhoonzero 已提交
535
        context.GetPlace());
536
    auto* out_data = out.mutable_value()->data<T>();
T
typhoonzero 已提交
537

538 539 540 541 542 543
    if (merged_row_set.size() == row_num && !sorted_result) {
      // no duplicated ids, just concat the result together
      std::vector<int64_t> merge_rows;
      merge_rows.reserve(row_num);
      // concat rows
      for (auto* in : inputs) {
544 545
        merge_rows.insert(
            merge_rows.end(), in->rows().begin(), in->rows().end());
546 547 548 549 550 551 552
      }
      out.set_rows(merge_rows);
      auto in_place = inputs[0]->place();
      auto out_place = out.place();
      int64_t copied_numel = 0;
      for (auto* in : inputs) {
        auto* in_data = in->value().data<T>();
553
        auto in_numel = in->rows().size() * input_width;
554 555 556 557
        memory::Copy(out_place,
                     out_data + copied_numel,
                     in_place,
                     in_data,
558 559 560 561 562 563
                     in_numel * sizeof(T));
        copied_numel += in_numel;
      }
    } else {
      std::vector<int64_t> merge_rows(merged_row_set.begin(),
                                      merged_row_set.end());
T
typhoonzero 已提交
564

565 566 567
      if (sorted_result) {
        std::sort(merge_rows.begin(), merge_rows.end());
      }
T
typhoonzero 已提交
568

569 570
      out.set_rows(merge_rows);

571
      phi::funcs::SetConstant<DeviceContext, T> constant_functor;
572
      constant_functor(context, out.mutable_value(), static_cast<T>(0.f));
573 574 575 576

      std::unordered_map<int64_t, size_t> rows_to_id;
      for (size_t i = 0; i < merge_rows.size(); ++i) {
        rows_to_id[merge_rows[i]] = i;
Q
Qiao Longfei 已提交
577
      }
578

579 580
      add_sparse_inputs<T, DeviceContext>(
          inputs, rows_to_id, input_width, context, out_data);
T
typhoonzero 已提交
581
    }
T
wip  
typhoonzero 已提交
582 583 584
  }
};

585 586 587 588 589 590 591
template <typename T>
struct MergeAdd<platform::CPUDeviceContext, T> {
  // unary functor, merge by adding duplicated rows in
  // the input SelectedRows object.
  phi::SelectedRows operator()(const platform::CPUDeviceContext& context,
                               const phi::SelectedRows& input,
                               const bool sorted_result) {
592 593
    return MergeAddImpl<platform::CPUDeviceContext, T>()(
        context, input, sorted_result);
594 595 596
  }

  void operator()(const platform::CPUDeviceContext& context,
597 598
                  const phi::SelectedRows& input,
                  phi::SelectedRows* output,
599
                  const bool sorted_result) {
600 601
    MergeAddImpl<platform::CPUDeviceContext, T>()(
        context, input, output, sorted_result);
602 603 604 605
  }

  void operator()(const platform::CPUDeviceContext& context,
                  const std::vector<const phi::SelectedRows*>& inputs,
606 607 608 609
                  phi::SelectedRows* output,
                  const bool sorted_result) {
    MergeAddImpl<platform::CPUDeviceContext, T>()(
        context, inputs, output, sorted_result);
610 611 612 613 614 615 616 617 618 619 620 621 622 623
  }
};

template <typename T>
struct MergeAdd<phi::CPUContext, T> {
  // unary functor, merge by adding duplicated rows in
  // the input SelectedRows object.
  phi::SelectedRows operator()(const phi::CPUContext& context,
                               const phi::SelectedRows& input,
                               const bool sorted_result) {
    return MergeAddImpl<phi::CPUContext, T>()(context, input, sorted_result);
  }

  void operator()(const phi::CPUContext& context,
624 625
                  const phi::SelectedRows& input,
                  phi::SelectedRows* output,
626 627 628 629 630 631
                  const bool sorted_result) {
    MergeAddImpl<phi::CPUContext, T>()(context, input, output, sorted_result);
  }

  void operator()(const phi::CPUContext& context,
                  const std::vector<const phi::SelectedRows*>& inputs,
632 633
                  phi::SelectedRows* output,
                  const bool sorted_result) {
634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651
    MergeAddImpl<phi::CPUContext, T>()(context, inputs, output, sorted_result);
  }
};

#define TEMPLATE_SPECIALIZED_FOR_MERGEADD_CPU(dtype)               \
  template struct MergeAddImpl<platform::CPUDeviceContext, dtype>; \
  template struct MergeAddImpl<phi::CPUContext, dtype>;            \
  template struct MergeAdd<platform::CPUDeviceContext, dtype>;     \
  template struct MergeAdd<phi::CPUContext, dtype>;

TEMPLATE_SPECIALIZED_FOR_MERGEADD_CPU(float)
TEMPLATE_SPECIALIZED_FOR_MERGEADD_CPU(double)
TEMPLATE_SPECIALIZED_FOR_MERGEADD_CPU(int)
TEMPLATE_SPECIALIZED_FOR_MERGEADD_CPU(int64_t)
TEMPLATE_SPECIALIZED_FOR_MERGEADD_CPU(platform::bfloat16)
TEMPLATE_SPECIALIZED_FOR_MERGEADD_CPU(platform::complex<float>)
TEMPLATE_SPECIALIZED_FOR_MERGEADD_CPU(platform::complex<double>)

652 653 654
#ifdef PADDLE_WITH_XPU
template <typename T>
struct MergeAdd<platform::XPUDeviceContext, T> {
655 656 657 658
  phi::SelectedRows operator()(const platform::XPUDeviceContext& context,
                               const phi::SelectedRows& input,
                               const bool sorted_result = false) {
    phi::SelectedRows out;
659 660 661 662 663
    (*this)(context, input, &out, sorted_result);
    return out;
  }

  void operator()(const platform::XPUDeviceContext& context,
664 665
                  const phi::SelectedRows& input,
                  phi::SelectedRows* output,
666 667 668 669 670 671
                  const bool sorted_result = false) {
    framework::Vector<int64_t> input_rows(input.rows());
    if (input_rows.size() == 0) {
      return;
    }

672
    phi::SelectedRows& out = *output;
673 674 675 676 677 678 679
    std::set<int64_t> row_set(input_rows.begin(), input_rows.end());
    std::vector<int64_t> merge_rows(row_set.begin(), row_set.end());
    auto input_width = input.value().dims()[1];

    out.set_rows(merge_rows);
    out.set_height(input.height());
    out.mutable_value()->mutable_data<T>(
680
        phi::make_ddim({static_cast<int64_t>(merge_rows.size()), input_width}),
681 682 683 684 685 686 687
        context.GetPlace());

    std::unordered_map<int64_t, size_t> rows_to_id;
    for (size_t i = 0; i < merge_rows.size(); ++i) {
      rows_to_id[merge_rows[i]] = i;
    }

688 689 690 691
    auto* y_data = out.mutable_value()->data<T>();
    auto* x_data = input.value().data<T>();
    int xm = input_rows.size();
    int ym = merge_rows.size();
692
    int n = input_width;
693 694 695 696

    xpu::ctx_guard RAII_GUARD(context.x_context());
    int64_t* x_rows_data = RAII_GUARD.alloc_l3_or_gm<int64_t>(xm);
    int64_t* y_rows_data = RAII_GUARD.alloc_l3_or_gm<int64_t>(ym);
697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714
    memory::Copy(context.GetPlace(),
                 y_rows_data,
                 platform::CPUPlace(),
                 merge_rows.data(),
                 ym * sizeof(int64_t));
    memory::Copy(context.GetPlace(),
                 x_rows_data,
                 platform::CPUPlace(),
                 input_rows.data(),
                 xm * sizeof(int64_t));
    int r = xpu::merge_dup_rows<T, int64_t>(context.x_context(),
                                            x_data,
                                            y_data,
                                            x_rows_data,
                                            y_rows_data,
                                            xm,
                                            n,
                                            ym);
715
    PADDLE_ENFORCE_XDNN_SUCCESS(r, "merge_dup_rows");
716 717 718
  }

  void operator()(const platform::XPUDeviceContext& context,
719
                  const std::vector<const phi::SelectedRows*>& inputs,
720 721
                  phi::SelectedRows* output,
                  const bool sorted_result = false) {
722 723 724 725
    if (inputs.size() == 0) {
      VLOG(3) << "no input! return";
      return;
    }
726
    const phi::SelectedRows* has_value_input = nullptr;
727 728 729 730 731 732 733 734 735 736 737 738
    for (auto* in : inputs) {
      if (in->rows().size() > 0) {
        has_value_input = in;
        break;
      }
    }
    if (has_value_input == nullptr) {
      VLOG(3) << "no input has value! just return" << std::endl;
      return;
    }
    auto input_width = has_value_input->value().dims()[1];
    auto input_height = has_value_input->height();
739
    phi::SelectedRows& out = *output;
740 741 742 743 744 745
    std::set<int64_t> merged_row_set;
    size_t row_num = 0;
    for (auto* input : inputs) {
      if (input->rows().size() == 0) {
        continue;
      }
746 747
      PADDLE_ENFORCE_EQ(input_width,
                        input->value().dims()[1],
748 749 750
                        platform::errors::InvalidArgument(
                            "All inputs should have same "
                            "dimension except for the first one."));
751 752
      PADDLE_ENFORCE_EQ(input_height,
                        input->height(),
753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768
                        platform::errors::InvalidArgument(
                            "All inputs should have same height."));
      row_num += input->rows().size();
      merged_row_set.insert(input->rows().begin(), input->rows().end());
    }

    std::vector<int64_t> merge_rows(merged_row_set.begin(),
                                    merged_row_set.end());

    if (sorted_result) {
      std::sort(merge_rows.begin(), merge_rows.end());
    }

    out.set_rows(merge_rows);
    out.set_height(input_height);
    out.mutable_value()->mutable_data<T>(
769
        phi::make_ddim(
770 771 772
            {static_cast<int64_t>(merged_row_set.size()), input_width}),
        context.GetPlace());

773
    float* y_data = reinterpret_cast<float*>(out.mutable_value()->data<T>());
774 775 776 777 778 779 780 781 782 783 784 785

    std::unordered_map<int64_t, size_t> rows_to_id;
    for (size_t i = 0; i < merge_rows.size(); ++i) {
      rows_to_id[merge_rows[i]] = i;
    }

    for (auto* input : inputs) {
      if (input->rows().size() == 0) {
        continue;
      }
      auto& input_rows = input->rows();

786 787 788
      auto* x_data = input->value().data<T>();
      int xm = input_rows.size();
      int ym = merge_rows.size();
789
      int n = input_width;
790 791 792 793

      xpu::ctx_guard RAII_GUARD(context.x_context());
      int64_t* x_rows_data = RAII_GUARD.alloc_l3_or_gm<int64_t>(xm);
      int64_t* y_rows_data = RAII_GUARD.alloc_l3_or_gm<int64_t>(ym);
794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811
      memory::Copy(context.GetPlace(),
                   y_rows_data,
                   platform::CPUPlace(),
                   merge_rows.data(),
                   ym * sizeof(int64_t));
      memory::Copy(context.GetPlace(),
                   x_rows_data,
                   platform::CPUPlace(),
                   input_rows.data(),
                   xm * sizeof(int64_t));
      int r = xpu::merge_dup_rows<T, int64_t>(context.x_context(),
                                              x_data,
                                              y_data,
                                              x_rows_data,
                                              y_rows_data,
                                              xm,
                                              n,
                                              ym);
812
      PADDLE_ENFORCE_XDNN_SUCCESS(r, "merge_dup_rows");
813 814 815 816 817
    }
  }
};

#endif
818 819
template <typename T>
struct MergeAverage<platform::CPUDeviceContext, T> {
820 821 822
  phi::SelectedRows operator()(const platform::CPUDeviceContext& context,
                               const phi::SelectedRows& input) {
    phi::SelectedRows out;
823 824 825 826 827
    (*this)(context, input, &out);
    return out;
  }

  void operator()(const platform::CPUDeviceContext& context,
828 829
                  const phi::SelectedRows& input,
                  phi::SelectedRows* output) {
830
    std::vector<const phi::SelectedRows*> inputs;
831 832 833 834 835
    inputs.push_back(&input);
    (*this)(context, inputs, output);
  }

  void operator()(const platform::CPUDeviceContext& context,
836 837
                  const std::vector<const phi::SelectedRows*>& inputs,
                  phi::SelectedRows* output) {
838 839 840 841
    if (inputs.size() == 0) {
      VLOG(3) << "no input! return";
      return;
    }
842
    const phi::SelectedRows* has_value_input = nullptr;
843 844 845 846 847 848 849 850 851 852 853 854
    for (auto* in : inputs) {
      if (in->rows().size() > 0) {
        has_value_input = in;
        break;
      }
    }
    if (has_value_input == nullptr) {
      VLOG(3) << "no input has value! just return" << std::endl;
      return;
    }
    auto input_width = has_value_input->value().dims()[1];
    auto input_height = has_value_input->height();
855
    phi::SelectedRows& out = *output;
856 857 858 859 860 861
    std::set<int64_t> merged_row_set;
    size_t row_num = 0;
    for (auto* input : inputs) {
      if (input->rows().size() == 0) {
        continue;
      }
862 863
      PADDLE_ENFORCE_EQ(input_width,
                        input->value().dims()[1],
864 865 866
                        platform::errors::InvalidArgument(
                            "All inputs should have same "
                            "dimension except for the first one."));
867 868
      PADDLE_ENFORCE_EQ(input_height,
                        input->height(),
869 870
                        platform::errors::InvalidArgument(
                            "All input should have same height."));
871 872 873 874 875 876
      row_num += input->rows().size();
      merged_row_set.insert(input->rows().begin(), input->rows().end());
    }

    out.set_height(input_height);
    out.mutable_value()->mutable_data<T>(
877
        phi::make_ddim(
878 879 880 881 882 883 884 885 886 887
            {static_cast<int64_t>(merged_row_set.size()), input_width}),
        context.GetPlace());
    auto* out_data = out.mutable_value()->data<T>();

    std::vector<int64_t> merge_rows(merged_row_set.begin(),
                                    merged_row_set.end());
    std::sort(merge_rows.begin(), merge_rows.end());

    out.set_rows(merge_rows);

888
    phi::funcs::SetConstant<platform::CPUDeviceContext, T> constant_functor;
889 890 891 892 893 894 895
    constant_functor(context, out.mutable_value(), 0.0);

    std::unordered_map<int64_t, size_t> rows_to_id;
    for (size_t i = 0; i < merge_rows.size(); ++i) {
      rows_to_id[merge_rows[i]] = i;
    }

896
    auto blas = phi::funcs::GetBlas<platform::CPUDeviceContext, T>(context);
897 898 899 900 901 902 903 904 905
    for (auto* input : inputs) {
      if (input->rows().size() == 0) {
        continue;
      }
      auto* input_data = input->value().data<T>();
      auto& input_rows = input->rows();

      for (size_t i = 0; i < input_rows.size(); i++) {
        size_t out_i = rows_to_id[input_rows[i]];
906 907
        elementwise_add_to<T>(&blas,
                              static_cast<size_t>(input_width),
908 909
                              &input_data[i * input_width],
                              &out_data[out_i * input_width]);
910 911 912 913 914 915 916 917 918 919 920 921
      }
    }
    size_t input_width_cast = static_cast<size_t>(input_width);
    T count = static_cast<T>(inputs.size());
    for (size_t i = 0; i < merge_rows.size(); i++) {
      for (size_t j = 0; j < input_width_cast; j++) {
        out_data[i * input_width + j] = out_data[i * input_width + j] / count;
      }
    }
  }
};

922 923 924 925
#ifdef PADDLE_WITH_XPU
template struct MergeAdd<platform::XPUDeviceContext, float>;
#endif

926 927 928 929 930
template struct MergeAverage<platform::CPUDeviceContext, int>;
template struct MergeAverage<platform::CPUDeviceContext, int64_t>;
template struct MergeAverage<platform::CPUDeviceContext, float>;
template struct MergeAverage<platform::CPUDeviceContext, double>;

T
wip  
typhoonzero 已提交
931 932
template <typename T>
struct UpdateToTensor<platform::CPUDeviceContext, T> {
T
typhoonzero 已提交
933
  void operator()(const platform::CPUDeviceContext& context,
934 935
                  const ScatterOps& op,
                  const phi::SelectedRows& input1,
T
typhoonzero 已提交
936
                  framework::Tensor* input2) {
T
wip  
typhoonzero 已提交
937
    auto in1_height = input1.height();
938
    const auto& in2_dims = input2->dims();
939
    PADDLE_ENFORCE_EQ(
940 941
        in1_height,
        in2_dims[0],
942
        platform::errors::InvalidArgument("The two inputs height must be equal."
943
                                          "But received first input height = "
944
                                          "[%d], second input height = [%d]",
945 946
                                          in1_height,
                                          in2_dims[0]));
T
wip  
typhoonzero 已提交
947 948 949 950 951

    auto& in1_value = input1.value();
    auto& in1_rows = input1.rows();

    int64_t in1_row_numel = in1_value.numel() / in1_rows.size();
952
    PADDLE_ENFORCE_EQ(
953 954
        in1_row_numel,
        input2->numel() / in1_height,
955 956
        platform::errors::InvalidArgument(
            "The two inputs width must be equal."
957
            "But received first input width = [%d], second input width = [%d]",
958 959
            in1_row_numel,
            input2->numel() / in1_height));
T
wip  
typhoonzero 已提交
960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003

    auto* in1_data = in1_value.data<T>();
    auto* input2_data = input2->data<T>();

    // FIXME(typhoonzero): use macro fix the below messy code.
    switch (op) {
      case ScatterOps::ASSIGN:
        INLINE_FOR2(in1_rows.size(), in1_row_numel)
        input2_data[in1_rows[i] * in1_row_numel + j] =
            in1_data[i * in1_row_numel + j];
        break;
      case ScatterOps::ADD:
        INLINE_FOR2(in1_rows.size(), in1_row_numel)
        input2_data[in1_rows[i] * in1_row_numel + j] +=
            in1_data[i * in1_row_numel + j];
        break;
      case ScatterOps::SUB:
        INLINE_FOR2(in1_rows.size(), in1_row_numel)
        input2_data[in1_rows[i] * in1_row_numel + j] -=
            in1_data[i * in1_row_numel + j];
        break;
      case ScatterOps::SUBBY:
        INLINE_FOR2(in1_rows.size(), in1_row_numel)
        input2_data[in1_rows[i] * in1_row_numel + j] =
            in1_data[i * in1_row_numel + j] -
            input2_data[in1_rows[i] * in1_row_numel + j];
        break;
      case ScatterOps::MUL:
        INLINE_FOR2(in1_rows.size(), in1_row_numel)
        input2_data[in1_rows[i] * in1_row_numel + j] *=
            in1_data[i * in1_row_numel + j];
        break;
      case ScatterOps::DIV:
        INLINE_FOR2(in1_rows.size(), in1_row_numel)
        input2_data[in1_rows[i] * in1_row_numel + j] /=
            in1_data[i * in1_row_numel + j];
        break;
      case ScatterOps::DIVBY:
        INLINE_FOR2(in1_rows.size(), in1_row_numel)
        input2_data[in1_rows[i] * in1_row_numel + j] =
            in1_data[i * in1_row_numel + j] /
            input2_data[in1_rows[i] * in1_row_numel + j];
        break;
    }
T
typhoonzero 已提交
1004 1005 1006 1007
  }
};

}  // namespace scatter
1008 1009 1010
}  // namespace math
}  // namespace operators
}  // namespace paddle