selected_rows_functor.cc 33.9 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>
L
Leo Chen 已提交
28 29
struct SelectedRowsAdd<phi::CPUContext, T> {
  void operator()(const phi::CPUContext& 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));
  }
};

L
Leo Chen 已提交
109 110
template struct SelectedRowsAdd<phi::CPUContext, float>;
template struct SelectedRowsAdd<phi::CPUContext, double>;
111 112

template <typename T>
L
Leo Chen 已提交
113 114
struct SelectedRowsAddTensor<phi::CPUContext, T> {
  void operator()(const phi::CPUContext& 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

L
Leo Chen 已提交
159
    phi::funcs::SetConstant<phi::CPUContext, 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
  }
};

L
Leo Chen 已提交
178 179
template struct SelectedRowsAddTensor<phi::CPUContext, float>;
template struct SelectedRowsAddTensor<phi::CPUContext, double>;
Q
QI JUN 已提交
180 181

template <typename T>
L
Leo Chen 已提交
182 183
struct SelectedRowsAddTo<phi::CPUContext, T> {
  void operator()(const phi::CPUContext& 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));
  }
};

L
Leo Chen 已提交
228 229 230 231
template struct SelectedRowsAddTo<phi::CPUContext, float>;
template struct SelectedRowsAddTo<phi::CPUContext, double>;
template struct SelectedRowsAddTo<phi::CPUContext, int>;
template struct SelectedRowsAddTo<phi::CPUContext, int64_t>;
Q
QI JUN 已提交
232

M
minqiyang 已提交
233
template <typename T>
L
Leo Chen 已提交
234 235
struct SelectedRowsSumTo<phi::CPUContext, T> {
  void operator()(const phi::CPUContext& 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>();
L
Leo Chen 已提交
265
    auto blas = phi::funcs::GetBlas<phi::CPUContext, T>(context);
M
minqiyang 已提交
266 267 268 269 270 271 272 273 274 275
    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);
    }
  }
};

L
Leo Chen 已提交
276 277
template struct SelectedRowsSumTo<phi::CPUContext, float>;
template struct SelectedRowsSumTo<phi::CPUContext, double>;
M
minqiyang 已提交
278

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

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

    int64_t in1_row_numel = in1_value.numel() / in1_rows.size();
    PADDLE_ENFORCE_EQ(
304 305
        in1_row_numel,
        input2->numel() / in1_height,
H
hong 已提交
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));
H
hong 已提交
311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328

    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];
      }
    }
  }
};

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 已提交
329 330 331 332 333 334 335 336
// 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 {

337
template <typename T, typename DeviceContext>
338
typename std::enable_if<!std::is_integral<T>::value>::type elementwise_add_to(
339 340 341
    phi::funcs::BlasT<DeviceContext, T>* blas,
    size_t data_len,
    const T* in,
342
    T* out) {
343
  blas->AXPY(data_len, T(1.f), in, out);
Q
Qiao Longfei 已提交
344 345
}

346
template <typename T, typename DeviceContext>
347
typename std::enable_if<std::is_integral<T>::value>::type elementwise_add_to(
348 349 350
    phi::funcs::BlasT<DeviceContext, T>* blas,
    size_t data_len,
    const T* in,
351
    T* out) {
T
Tao Luo 已提交
352
  for (size_t i = 0; i < data_len; i++) {
Q
Qiao Longfei 已提交
353 354
    out[i] += in[i];
  }
T
typhoonzero 已提交
355 356
}

357
template <typename T, typename DeviceContext>
358
typename std::enable_if<std::is_same<T, platform::bfloat16>::value>::type
359
add_sparse_inputs(const std::vector<const phi::SelectedRows*>& inputs,
360
                  const std::unordered_map<int64_t, size_t>& rows_to_id,
361 362
                  int64_t input_width,
                  const DeviceContext& context,
363
                  T* out_data) {
364
#ifndef PADDLE_WITH_MKLDNN
365
  auto blas = phi::funcs::GetBlas<DeviceContext, T>(context);
366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383
#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]);
384 385 386 387
      elementwise_add_to<T, DeviceContext>(&blas,
                                           static_cast<size_t>(input_width),
                                           &input_data[i * input_width],
                                           &out_data[out_i * input_width]);
388 389 390 391 392
    }
#endif
  }
}

393
template <typename T, typename DeviceContext>
394
typename std::enable_if<!std::is_same<T, platform::bfloat16>::value>::type
395
add_sparse_inputs(const std::vector<const phi::SelectedRows*>& inputs,
396
                  const std::unordered_map<int64_t, size_t>& rows_to_id,
397 398
                  int64_t input_width,
                  const DeviceContext& context,
399
                  T* out_data) {
400
  VLOG(4) << "[CPU] add_sparse_inputs <" << typeid(T).name();
401
  auto blas = phi::funcs::GetBlas<DeviceContext, T>(context);
402 403 404 405 406 407 408 409 410
  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]);
411 412 413 414
      elementwise_add_to<T, DeviceContext>(&blas,
                                           static_cast<size_t>(input_width),
                                           &input_data[i * input_width],
                                           &out_data[out_i * input_width]);
415 416 417 418
    }
  }
}

419 420 421
template <typename DeviceContext, typename T>
struct MergeAddImpl {
  phi::SelectedRows operator()(const DeviceContext& context,
422 423 424
                               const phi::SelectedRows& input,
                               const bool sorted_result = false) {
    phi::SelectedRows out;
425
    (*this)(context, input, &out, sorted_result);
S
sneaxiy 已提交
426 427 428
    return out;
  }

429 430 431 432
  void operator()(const DeviceContext& context,
                  const phi::SelectedRows& input,
                  phi::SelectedRows* output,
                  const bool sorted_result = false) {
433
    std::vector<const phi::SelectedRows*> inputs;
434
    inputs.push_back(&input);
435
    (*this)(context, inputs, output, sorted_result);
436
  }
T
typhoonzero 已提交
437

438
  void operator()(const DeviceContext& context,
439
                  const std::vector<const phi::SelectedRows*>& inputs,
440 441
                  phi::SelectedRows* output,
                  const bool sorted_result = false) {
Q
Qiao Longfei 已提交
442
    if (inputs.size() == 0) {
M
minqiyang 已提交
443
      VLOG(3) << "no input! return";
Q
Qiao Longfei 已提交
444 445
      return;
    }
446
    const phi::SelectedRows* has_value_input = nullptr;
Q
Qiao Longfei 已提交
447
    for (auto* in : inputs) {
Q
Qiao Longfei 已提交
448
      if (in->rows().size() > 0) {
Q
Qiao Longfei 已提交
449 450 451 452 453
        has_value_input = in;
        break;
      }
    }
    if (has_value_input == nullptr) {
M
minqiyang 已提交
454
      VLOG(3) << "no input has value! just return" << std::endl;
Q
Qiao Longfei 已提交
455 456 457 458
      return;
    }
    auto input_width = has_value_input->value().dims()[1];
    auto input_height = has_value_input->height();
459
    phi::SelectedRows& out = *output;
460
    std::set<int64_t> merged_row_set;
461
    size_t row_num = 0;
462
    for (auto* input : inputs) {
Q
Qiao Longfei 已提交
463
      if (input->rows().size() == 0) {
Q
Qiao Longfei 已提交
464 465
        continue;
      }
466 467
      PADDLE_ENFORCE_EQ(input_width,
                        input->value().dims()[1],
468 469 470
                        platform::errors::InvalidArgument(
                            "All inputs should have same "
                            "dimension except for the first one."));
471 472
      PADDLE_ENFORCE_EQ(input_height,
                        input->height(),
473 474
                        platform::errors::InvalidArgument(
                            "All inputs should have same height."));
475
      row_num += input->rows().size();
476 477
      merged_row_set.insert(input->rows().begin(), input->rows().end());
    }
478

479
    out.set_height(input_height);
T
wip  
typhoonzero 已提交
480
    out.mutable_value()->mutable_data<T>(
481
        phi::make_ddim(
482
            {static_cast<int64_t>(merged_row_set.size()), input_width}),
T
typhoonzero 已提交
483
        context.GetPlace());
484
    auto* out_data = out.mutable_value()->data<T>();
T
typhoonzero 已提交
485

486 487 488 489 490 491
    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) {
492 493
        merge_rows.insert(
            merge_rows.end(), in->rows().begin(), in->rows().end());
494 495 496 497 498 499 500
      }
      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>();
501
        auto in_numel = in->rows().size() * input_width;
502 503 504 505
        memory::Copy(out_place,
                     out_data + copied_numel,
                     in_place,
                     in_data,
506 507 508 509 510 511
                     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 已提交
512

513 514 515
      if (sorted_result) {
        std::sort(merge_rows.begin(), merge_rows.end());
      }
T
typhoonzero 已提交
516

517 518
      out.set_rows(merge_rows);

519
      phi::funcs::SetConstant<DeviceContext, T> constant_functor;
520
      constant_functor(context, out.mutable_value(), static_cast<T>(0.f));
521 522 523 524

      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 已提交
525
      }
526

527 528
      add_sparse_inputs<T, DeviceContext>(
          inputs, rows_to_id, input_width, context, out_data);
T
typhoonzero 已提交
529
    }
T
wip  
typhoonzero 已提交
530 531 532
  }
};

533 534 535 536 537 538 539 540 541 542 543
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,
544 545
                  const phi::SelectedRows& input,
                  phi::SelectedRows* output,
546 547 548 549 550 551
                  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,
552 553
                  phi::SelectedRows* output,
                  const bool sorted_result) {
554 555 556 557
    MergeAddImpl<phi::CPUContext, T>()(context, inputs, output, sorted_result);
  }
};

L
Leo Chen 已提交
558 559
#define TEMPLATE_SPECIALIZED_FOR_MERGEADD_CPU(dtype)    \
  template struct MergeAddImpl<phi::CPUContext, dtype>; \
560 561 562 563 564 565 566 567 568 569
  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>)

570 571 572
#ifdef PADDLE_WITH_XPU
template <typename T>
struct MergeAdd<platform::XPUDeviceContext, T> {
573 574 575 576
  phi::SelectedRows operator()(const platform::XPUDeviceContext& context,
                               const phi::SelectedRows& input,
                               const bool sorted_result = false) {
    phi::SelectedRows out;
577 578 579 580 581
    (*this)(context, input, &out, sorted_result);
    return out;
  }

  void operator()(const platform::XPUDeviceContext& context,
582 583
                  const phi::SelectedRows& input,
                  phi::SelectedRows* output,
584 585 586 587 588 589
                  const bool sorted_result = false) {
    framework::Vector<int64_t> input_rows(input.rows());
    if (input_rows.size() == 0) {
      return;
    }

590
    phi::SelectedRows& out = *output;
591 592 593 594 595 596 597
    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>(
598
        phi::make_ddim({static_cast<int64_t>(merge_rows.size()), input_width}),
599 600 601 602 603 604 605
        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;
    }

606 607 608 609
    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();
610
    int n = input_width;
611 612 613 614

    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);
615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632
    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);
633
    PADDLE_ENFORCE_XDNN_SUCCESS(r, "merge_dup_rows");
634 635 636
  }

  void operator()(const platform::XPUDeviceContext& context,
637
                  const std::vector<const phi::SelectedRows*>& inputs,
638 639
                  phi::SelectedRows* output,
                  const bool sorted_result = false) {
640 641 642 643
    if (inputs.size() == 0) {
      VLOG(3) << "no input! return";
      return;
    }
644
    const phi::SelectedRows* has_value_input = nullptr;
645 646 647 648 649 650 651 652 653 654 655 656
    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();
657
    phi::SelectedRows& out = *output;
658 659 660 661 662 663
    std::set<int64_t> merged_row_set;
    size_t row_num = 0;
    for (auto* input : inputs) {
      if (input->rows().size() == 0) {
        continue;
      }
664 665
      PADDLE_ENFORCE_EQ(input_width,
                        input->value().dims()[1],
666 667 668
                        platform::errors::InvalidArgument(
                            "All inputs should have same "
                            "dimension except for the first one."));
669 670
      PADDLE_ENFORCE_EQ(input_height,
                        input->height(),
671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686
                        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>(
687
        phi::make_ddim(
688 689 690
            {static_cast<int64_t>(merged_row_set.size()), input_width}),
        context.GetPlace());

691
    float* y_data = reinterpret_cast<float*>(out.mutable_value()->data<T>());
692 693 694 695 696 697 698 699 700 701 702 703

    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();

704 705 706
      auto* x_data = input->value().data<T>();
      int xm = input_rows.size();
      int ym = merge_rows.size();
707
      int n = input_width;
708 709 710 711

      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);
712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729
      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);
730
      PADDLE_ENFORCE_XDNN_SUCCESS(r, "merge_dup_rows");
731 732 733 734 735
    }
  }
};

#endif
736
template <typename T>
L
Leo Chen 已提交
737 738
struct MergeAverage<phi::CPUContext, T> {
  phi::SelectedRows operator()(const phi::CPUContext& context,
739 740
                               const phi::SelectedRows& input) {
    phi::SelectedRows out;
741 742 743 744
    (*this)(context, input, &out);
    return out;
  }

L
Leo Chen 已提交
745
  void operator()(const phi::CPUContext& context,
746 747
                  const phi::SelectedRows& input,
                  phi::SelectedRows* output) {
748
    std::vector<const phi::SelectedRows*> inputs;
749 750 751 752
    inputs.push_back(&input);
    (*this)(context, inputs, output);
  }

L
Leo Chen 已提交
753
  void operator()(const phi::CPUContext& context,
754 755
                  const std::vector<const phi::SelectedRows*>& inputs,
                  phi::SelectedRows* output) {
756 757 758 759
    if (inputs.size() == 0) {
      VLOG(3) << "no input! return";
      return;
    }
760
    const phi::SelectedRows* has_value_input = nullptr;
761 762 763 764 765 766 767 768 769 770 771 772
    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();
773
    phi::SelectedRows& out = *output;
774 775 776 777 778 779
    std::set<int64_t> merged_row_set;
    size_t row_num = 0;
    for (auto* input : inputs) {
      if (input->rows().size() == 0) {
        continue;
      }
780 781
      PADDLE_ENFORCE_EQ(input_width,
                        input->value().dims()[1],
782 783 784
                        platform::errors::InvalidArgument(
                            "All inputs should have same "
                            "dimension except for the first one."));
785 786
      PADDLE_ENFORCE_EQ(input_height,
                        input->height(),
787 788
                        platform::errors::InvalidArgument(
                            "All input should have same height."));
789 790 791 792 793 794
      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>(
795
        phi::make_ddim(
796 797 798 799 800 801 802 803 804 805
            {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);

L
Leo Chen 已提交
806
    phi::funcs::SetConstant<phi::CPUContext, T> constant_functor;
807 808 809 810 811 812 813
    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;
    }

L
Leo Chen 已提交
814
    auto blas = phi::funcs::GetBlas<phi::CPUContext, T>(context);
815 816 817 818 819 820 821 822 823
    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]];
824 825
        elementwise_add_to<T>(&blas,
                              static_cast<size_t>(input_width),
826 827
                              &input_data[i * input_width],
                              &out_data[out_i * input_width]);
828 829 830 831 832 833 834 835 836 837 838 839
      }
    }
    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;
      }
    }
  }
};

840 841 842 843
#ifdef PADDLE_WITH_XPU
template struct MergeAdd<platform::XPUDeviceContext, float>;
#endif

L
Leo Chen 已提交
844 845 846 847
template struct MergeAverage<phi::CPUContext, int>;
template struct MergeAverage<phi::CPUContext, int64_t>;
template struct MergeAverage<phi::CPUContext, float>;
template struct MergeAverage<phi::CPUContext, double>;
848

T
wip  
typhoonzero 已提交
849
template <typename T>
L
Leo Chen 已提交
850 851
struct UpdateToTensor<phi::CPUContext, T> {
  void operator()(const phi::CPUContext& context,
852 853
                  const ScatterOps& op,
                  const phi::SelectedRows& input1,
T
typhoonzero 已提交
854
                  framework::Tensor* input2) {
T
wip  
typhoonzero 已提交
855
    auto in1_height = input1.height();
856
    const auto& in2_dims = input2->dims();
857
    PADDLE_ENFORCE_EQ(
858 859
        in1_height,
        in2_dims[0],
860
        platform::errors::InvalidArgument("The two inputs height must be equal."
861
                                          "But received first input height = "
862
                                          "[%d], second input height = [%d]",
863 864
                                          in1_height,
                                          in2_dims[0]));
T
wip  
typhoonzero 已提交
865 866 867 868 869

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

    int64_t in1_row_numel = in1_value.numel() / in1_rows.size();
870
    PADDLE_ENFORCE_EQ(
871 872
        in1_row_numel,
        input2->numel() / in1_height,
873 874
        platform::errors::InvalidArgument(
            "The two inputs width must be equal."
875
            "But received first input width = [%d], second input width = [%d]",
876 877
            in1_row_numel,
            input2->numel() / in1_height));
T
wip  
typhoonzero 已提交
878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921

    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 已提交
922 923 924 925
  }
};

}  // namespace scatter
926 927 928
}  // namespace math
}  // namespace operators
}  // namespace paddle