blob: 981e7aba9f9f03930063dc2871deab1087d67ac9 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/*!
* Copyright (c) 2016 by Contributors
* \file elemwise_binary_scalar_op.cu
* \brief GPU Implementation of unary function.
*/
#include <cub/cub.cuh>
#include "./elemwise_binary_op.h"
#include "./elemwise_binary_op-inl.h"
#include "./indexing_op.h"
namespace mxnet {
namespace op {
template<typename OP>
struct RspElemwiseKernel {
template<typename DType, typename IType>
static MSHADOW_XINLINE void Map(int i, DType* out, const IType* lookup_table,
const DType* data, const IType* indices,
const nnvm::dim_t nz_rows, const nnvm::dim_t num_cols) {
if (i < nz_rows * num_cols) {
const nnvm::dim_t row = i / num_cols;
const nnvm::dim_t col = i % num_cols;
const nnvm::dim_t out_row = lookup_table[indices[row]] - 1;
const nnvm::dim_t out_idx = out_row * num_cols + col;
out[out_idx] = OP::Map(out[out_idx], data[i]);
}
}
};
template<typename OP>
void ElemwiseBinaryOp::RspRspOp(mshadow::Stream<gpu> *s,
const nnvm::NodeAttrs &attrs,
const OpContext &ctx,
const NDArray &lhs,
const NDArray &rhs,
const OpReqType req,
const NDArray &output,
const bool lhs_may_be_dense,
const bool rhs_may_be_dense,
const bool allow_inplace,
const bool scatter) {
using namespace mshadow;
using namespace mxnet_op;
using namespace mshadow::expr;
using namespace rowsparse;
if (req == kNullOp) return;
CHECK(!scatter) << "scatter is not supported in RspRspOp on GPU yet...";
CHECK(lhs.storage_type() == kRowSparseStorage && rhs.storage_type() == kRowSparseStorage);
CHECK(output.storage_type() == kRowSparseStorage);
CHECK(req != kAddTo);
const nnvm::dim_t num_rows = output.shape()[0];
MSHADOW_TYPE_SWITCH(lhs.data().type_flag_, DType, {
MSHADOW_IDX_TYPE_SWITCH(lhs.aux_data(kIdx).type_flag_, IType, {
if (lhs.storage_initialized() && rhs.storage_initialized()) {
const nnvm::dim_t lhs_nz_rows = lhs.storage_shape()[0];
const nnvm::dim_t rhs_nz_rows = rhs.storage_shape()[0];
const nnvm::dim_t num_cols = lhs.data().Size() / lhs_nz_rows;
// Optimize for the case where one of the rsps is actually dense
if ((lhs_nz_rows == num_rows || rhs_nz_rows == num_rows) && req == kWriteInplace) {
const NDArray& dns = (output.IsSame(lhs)) ? lhs : rhs;
const NDArray& rsp = (output.IsSame(lhs)) ? rhs : lhs;
const bool reverse = !(lhs_nz_rows == num_rows);
ElemwiseBinaryOp::DnsRspDnsOp<gpu, OP>(s, attrs, ctx, dns, rsp, req, output, reverse);
return;
}
CHECK(req == kWriteTo) << "Should be kWriteTo but got " << req;
const TBlob& lhs_indices = lhs.aux_data(kIdx);
const TBlob& rhs_indices = rhs.aux_data(kIdx);
size_t common_row_table_bytes = num_rows * sizeof(IType);
IType* common_row_table = NULL;
void* temp_storage_ptr = NULL;
size_t temp_storage_bytes = 0;
cub::DeviceScan::InclusiveSum(temp_storage_ptr,
temp_storage_bytes,
common_row_table,
common_row_table,
num_rows,
mshadow::Stream<gpu>::GetStream(s));
size_t workspace_bytes = common_row_table_bytes + temp_storage_bytes;
Tensor<gpu, 1, char> workspace =
ctx.requested[0].get_space_typed<gpu, 1, char>(Shape1(workspace_bytes), s);
common_row_table = reinterpret_cast<IType*>(workspace.dptr_);
temp_storage_ptr = workspace.dptr_ + common_row_table_bytes;
mxnet_op::Kernel<set_zero, gpu>::Launch(s, num_rows, common_row_table);
Kernel<MarkRspRowFlgKernel, gpu>::Launch(
s, lhs_nz_rows, common_row_table, lhs_indices.dptr<IType>(), lhs_nz_rows);
Kernel<MarkRspRowFlgKernel, gpu>::Launch(
s, rhs_nz_rows, common_row_table, rhs_indices.dptr<IType>(), rhs_nz_rows);
cub::DeviceScan::InclusiveSum(temp_storage_ptr,
temp_storage_bytes,
common_row_table,
common_row_table,
num_rows,
mshadow::Stream<gpu>::GetStream(s));
nnvm::dim_t nnr_out = 0;
CUDA_CALL(cudaMemcpy(&nnr_out, &common_row_table[num_rows-1], sizeof(nnvm::dim_t),
cudaMemcpyDeviceToHost));
output.CheckAndAlloc({mshadow::Shape1(nnr_out)});
Kernel<FillRspRowIdxKernel, gpu>::Launch(
s, num_rows, output.aux_data(kIdx).dptr<IType>(), common_row_table, num_rows);
Kernel<set_zero, gpu>::Launch(s, nnr_out * num_cols, output.data().dptr<DType>());
Kernel<RspElemwiseKernel<mshadow_op::plus>, gpu>::Launch(
s, lhs_nz_rows * num_cols, output.data().dptr<DType>(), common_row_table,
lhs.data().dptr<DType>(), lhs_indices.dptr<IType>(), lhs_nz_rows, num_cols);
Kernel<RspElemwiseKernel<OP>, gpu>::Launch(
s, rhs_nz_rows * num_cols, output.data().dptr<DType>(), common_row_table,
rhs.data().dptr<DType>(), rhs_indices.dptr<IType>(), rhs_nz_rows, num_cols);
} else {
if (lhs.storage_initialized()) {
if (req == kWriteTo) {
output.CheckAndAlloc({lhs.aux_shape(kIdx)});
Copy(output.data().FlatTo1D<gpu, DType>(),
lhs.data().FlatTo1D<gpu, DType>(), s);
Copy(output.aux_data(kIdx).FlatTo1D<gpu, IType>(),
lhs.aux_data(kIdx).FlatTo1D<gpu, IType>(), s);
} else if (req == kWriteInplace && rhs.IsSame(output)) {
LOG(FATAL) << "Inplace on an empty rhs is not supported";
}
} else if (rhs.storage_initialized()) {
if (req == kWriteTo) {
output.CheckAndAlloc({rhs.aux_shape(kIdx)});
} else if (req == kWriteInplace && lhs.IsSame(output)) {
LOG(FATAL) << "Inplace on an empty lhs is not supported";
}
if (std::is_same<OP, mshadow_op::minus>::value) {
Kernel<op_with_req<mshadow_op::negation, kWriteTo>, gpu>::Launch(
s, rhs.data().Size(), output.data().dptr<DType>(), rhs.data().dptr<DType>());
} else if (req == kWriteTo) {
Copy(output.data().FlatTo1D<gpu, DType>(),
rhs.data().FlatTo1D<gpu, DType>(), s);
}
if (req == kWriteTo) {
Copy(output.aux_data(kIdx).FlatTo1D<gpu, IType>(),
rhs.aux_data(kIdx).FlatTo1D<gpu, IType>(), s);
}
} else {
FillZerosRspImpl(s, output);
}
}
});
});
}
/*! \brief DNS -op- CSR binary operator for non-canonical NDArray */
template<typename OP>
void ElemwiseBinaryOp::DnsCsrDnsOp(mshadow::Stream<gpu> *s,
const nnvm::NodeAttrs &attrs,
const OpContext &ctx,
const NDArray &dns,
const NDArray &csr,
const OpReqType req,
const NDArray &output,
const bool reverse) {
using namespace mshadow;
using namespace mxnet_op;
CHECK_EQ(dns.storage_type(), kDefaultStorage);
CHECK_EQ(csr.storage_type(), kCSRStorage);
CHECK(req != kAddTo);
CHECK(req != kNullOp);
const bool supported_op = std::is_same<OP, mshadow_op::minus>::value ||
std::is_same<OP, mshadow_op::plus>::value;
CHECK(supported_op == true);
const nnvm::dim_t num_csr_rows = csr.shape()[0];
const nnvm::dim_t num_csr_cols = csr.shape()[1];
TBlob csr_data = csr.data();
TBlob csr_indices = csr.aux_data(csr::kIdx);
TBlob csr_indptr = csr.aux_data(csr::kIndPtr);
MSHADOW_SGL_DBL_TYPE_SWITCH(csr_data.type_flag_, DType, {
MSHADOW_IDX_TYPE_SWITCH(csr_indices.type_flag_, IType, {
MSHADOW_IDX_TYPE_SWITCH(csr_indptr.type_flag_, CType, {
MXNET_ASSIGN_REQ_SWITCH(req, Req, {
if (reverse && std::is_same<OP, mshadow_op::minus>::value) {
mxnet_op::Kernel<mxnet_op::op_with_req<mshadow_op::negation, Req>, gpu>::Launch(
s, output.data().Size(), output.data().dptr<DType>(), dns.data().dptr<DType>());
if (!csr.storage_initialized()) { return; }
mxnet_op::Kernel<ElemwiseDnsCsrDnsWarpKernel<Req, mshadow_op::plus>, gpu>::Launch(
s, kWarpSize * num_csr_rows, output.data().dptr<DType>(),
output.data().dptr<DType>(), csr_data.dptr<DType>(), csr_indices.dptr<IType>(),
csr_indptr.dptr<CType>(), num_csr_rows, num_csr_cols);
} else {
if (req == kWriteTo) {
mxnet_op::Kernel<mxnet_op::op_with_req<mshadow_op::identity, Req>, gpu>::Launch(
s, output.data().Size(), output.data().dptr<DType>(), dns.data().dptr<DType>());
}
if (!csr.storage_initialized()) { return; }
mxnet_op::Kernel<ElemwiseDnsCsrDnsWarpKernel<Req, OP>, gpu>::Launch(
s, kWarpSize * num_csr_rows, output.data().dptr<DType>(),
output.data().dptr<DType>(), csr_data.dptr<DType>(), csr_indices.dptr<IType>(),
csr_indptr.dptr<CType>(), num_csr_rows, num_csr_cols);
}
});
});
});
});
}
NNVM_REGISTER_OP(elemwise_add)
.set_attr<FCompute>("FCompute<gpu>", ElemwiseBinaryOp::ComputeWithHalf2<gpu, op::mshadow_op::plus>)
.set_attr<FComputeEx>("FComputeEx<gpu>", ElemwiseBinaryOp::ComputeEx<gpu, op::mshadow_op::plus>);
NNVM_REGISTER_OP(_grad_add)
.set_attr<FCompute>("FCompute<gpu>", ElemwiseBinaryOp::ComputeWithHalf2<gpu, op::mshadow_op::plus>);
NNVM_REGISTER_OP(_backward_add)
.set_attr<FCompute>("FCompute<gpu>",
ElemwiseBinaryOp::BackwardUseNoneWithHalf2<gpu, mshadow_op::identity,
mshadow_op::identity>);
NNVM_REGISTER_OP(elemwise_sub)
.set_attr<FCompute>("FCompute<gpu>", ElemwiseBinaryOp::ComputeWithHalf2<
gpu, op::mshadow_op::minus>)
.set_attr<FComputeEx>("FComputeEx<gpu>", ElemwiseBinaryOp::ComputeEx<gpu, op::mshadow_op::minus>);
NNVM_REGISTER_OP(_backward_sub)
.set_attr<FCompute>("FCompute<gpu>",
ElemwiseBinaryOp::BackwardUseNoneWithHalf2<gpu, mshadow_op::identity,
mshadow_op::negation>);
NNVM_REGISTER_OP(elemwise_mul)
.set_attr<FCompute>("FCompute<gpu>", ElemwiseBinaryOp::ComputeWithHalf2<gpu, op::mshadow_op::mul>)
.set_attr<FComputeEx>("FComputeEx<gpu>",
ElemwiseBinaryOp::ComputeDnsLRValueEx<gpu, op::mshadow_op::mul, true, true>);
NNVM_REGISTER_OP(_backward_mul)
.set_attr<FCompute>("FCompute<gpu>",
ElemwiseBinaryOp::BackwardUseInWithHalf2<gpu, mshadow_op::right,
mshadow_op::left>);
NNVM_REGISTER_OP(elemwise_div)
.set_attr<FCompute>("FCompute<gpu>",
ElemwiseBinaryOp::ElemwiseBinaryOp::ComputeWithHalf2<gpu, op::mshadow_op::div>);
NNVM_REGISTER_OP(_backward_div)
.set_attr<FCompute>("FCompute<gpu>",
ElemwiseBinaryOp::BackwardUseInWithHalf2<gpu, mshadow_op::div_grad,
mshadow_op::div_rgrad>);
NNVM_REGISTER_OP(_mod)
.set_attr<FCompute>("FCompute<gpu>", ElemwiseBinaryOp::ComputeWithHalf2<gpu, mshadow_op::mod>);
NNVM_REGISTER_OP(_backward_mod)
.set_attr<FCompute>("FCompute<gpu>",
ElemwiseBinaryOp::BackwardUseInWithHalf2<gpu, mshadow_op::mod_grad, mshadow_op::mod_rgrad>);
} // namespace op
} // namespace mxnet