src/bst_op.cc (481 lines of code) (raw):
#include "tensorflow/core/framework/op.h"
#include "tensorflow/core/framework/op_kernel.h"
#include "tensorflow/core/framework/shape_inference.h"
#include "tensorflow/core/framework/types.h"
#include "tensorflow/core/lib/core/status.h"
#include "tensorflow/core/platform/stream_executor.h"
#include "tensorflow/stream_executor/cuda/cuda_stream.h"
#include "gpu_types.h"
using namespace tensorflow;
using shape_inference::DimensionHandle;
using shape_inference::InferenceContext;
using shape_inference::ShapeHandle;
using perftools::gputools::cuda::CUDAStream;
Status UnchangedShape(shape_inference::InferenceContext* ctx);
////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////// BlocksparseTransformerOp //////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////
Status nt_shape(InferenceContext* ctx)
{
int heads, blocks, blk_size;
TF_RETURN_IF_ERROR(ctx->GetAttr("heads", &heads ));
TF_RETURN_IF_ERROR(ctx->GetAttr("blocks", &blocks ));
TF_RETURN_IF_ERROR(ctx->GetAttr("blk_size", &blk_size));
ShapeHandle x = ctx->input(0);
if (ctx->RankKnown(x))
{
// (minbatch, heads, blocks, block_size, block_size)
ctx->set_output(0, ctx->MakeShape({
ctx->Dim(x, 0),
ctx->MakeDim(heads),
ctx->MakeDim(blocks),
ctx->MakeDim(blk_size),
ctx->MakeDim(blk_size)
}));
}
else
ctx->set_output(0, ctx->UnknownShape());
return Status::OK();
}
Status xn_shape(InferenceContext* ctx)
{
int ctx_blks_c, blk_size;
TF_RETURN_IF_ERROR(ctx->GetAttr("ctx_blks_c", &ctx_blks_c));
TF_RETURN_IF_ERROR(ctx->GetAttr("blk_size", &blk_size));
ShapeHandle b = ctx->input(1);
if (ctx->RankKnown(b))
{
// (batch, ctx_size, state_size)
ctx->set_output(0, ctx->MakeShape({
ctx->Dim(b, 0),
ctx->MakeDim(ctx_blks_c * blk_size),
ctx->Dim(b, 2)
}));
}
else
ctx->set_output(0, ctx->UnknownShape());
return Status::OK();
}
REGISTER_OP("BlocksparseTransformerNT")
.Input("a: T")
.Input("b: T")
.Input("nt_lut: int32")
.Input("nn_lut: int32")
.Input("tn_lut: int32")
.Output("c: CT")
.Attr("T: {half, float}")
.Attr("CT: {half, bfloat16}")
.Attr("heads: int")
.Attr("blocks: int")
.Attr("blk_size: int")
.Attr("ctx_blks_a: int")
.Attr("ctx_blks_b: int")
.Attr("ctx_blks_c: int = 0")
.Attr("nn_max: int")
.Attr("tn_max: int")
.Attr("bench: int = 0")
.SetShapeFn(nt_shape)
.Doc(R"doc(
Multiply the dense matrix "a" by the dense matrix "b.T" and produce blocksparse output "c".
)doc");
REGISTER_OP("BlocksparseTransformerNN")
.Input("a: AT")
.Input("b: T")
.Input("nt_lut: int32")
.Input("nn_lut: int32")
.Input("tn_lut: int32")
.Output("c: T")
.Attr("T: {half, float}")
.Attr("AT: {half, bfloat16}")
.Attr("heads: int")
.Attr("blocks: int")
.Attr("blk_size: int")
.Attr("ctx_blks_a: int = 0")
.Attr("ctx_blks_b: int")
.Attr("ctx_blks_c: int")
.Attr("nn_max: int")
.Attr("tn_max: int")
.Attr("bench: int = 0")
.SetShapeFn(xn_shape)
.Doc(R"doc(
Multiply the blocksparse matrix "a" by the dense matrix "b" and produce dense output "c".
)doc");
REGISTER_OP("BlocksparseTransformerTN")
.Input("a: AT")
.Input("b: T")
.Input("nt_lut: int32")
.Input("nn_lut: int32")
.Input("tn_lut: int32")
.Output("c: T")
.Attr("T: {half, float}")
.Attr("AT: {half, bfloat16}")
.Attr("heads: int")
.Attr("blocks: int")
.Attr("blk_size: int")
.Attr("ctx_blks_a: int = 0")
.Attr("ctx_blks_b: int")
.Attr("ctx_blks_c: int")
.Attr("nn_max: int")
.Attr("tn_max: int")
.Attr("bench: int = 0")
.SetShapeFn(xn_shape)
.Doc(R"doc(
Multiply the blocksparse matrix "a.T" by the dense matrix "b" and produce dense output "c".
)doc");
template <typename CT, typename CV2, typename CV4>
bool bst_hgemm_nt(CUstream stream, const uint2* lut, const ehalf* a, const ehalf* b, CT* c, uint block_size, uint blocks, uint batch_dim, uint ctx_blks_a, uint ctx_blks_b, uint head_dim, uint state_dim, uint lut_heads, uint lut_dim);
bool bst_hgemm_xn(CUstream stream, const uint2* lut, const ehalf* a, const ehalf* b, ehalf* c, uint block_size, uint blocks, uint batch_dim, uint ctx_blks_b, uint ctx_blks_c, uint head_dim, uint state_dim, uint lut_heads, uint lut_dim, uint op, uint magic, uint shift, uint max_blks);
bool bst_sgemm_nt(CUstream stream, const uint2* lut, const float* a, const float* b, bhalf* c, uint block_size, uint blocks, uint batch_dim, uint ctx_blks_a, uint ctx_blks_b, uint head_dim, uint state_dim, uint lut_heads, uint lut_dim);
bool bst_sgemm_xn(CUstream stream, const uint2* lut, const bhalf* a, const float* b, float* c, uint block_size, uint blocks, uint batch_dim, uint ctx_blks_b, uint ctx_blks_c, uint head_dim, uint state_dim, uint lut_heads, uint lut_dim, uint op, uint magic, uint shift, uint max_blks);
template <uint OP>
class BlocksparseTransformerOp : public OpKernel {
public:
explicit BlocksparseTransformerOp(OpKernelConstruction* ctx) : OpKernel(ctx), major_(0), magic_(0), shift_(0), head_state_(0)
{
OP_REQUIRES_OK(ctx, ctx->GetAttr("heads", &heads_ ));
OP_REQUIRES_OK(ctx, ctx->GetAttr("blocks", &blocks_ ));
OP_REQUIRES_OK(ctx, ctx->GetAttr("blk_size", &blk_size_ ));
OP_REQUIRES_OK(ctx, ctx->GetAttr("ctx_blks_a", &ctx_blks_a_ ));
OP_REQUIRES_OK(ctx, ctx->GetAttr("ctx_blks_b", &ctx_blks_b_ ));
OP_REQUIRES_OK(ctx, ctx->GetAttr("ctx_blks_c", &ctx_blks_c_ ));
OP_REQUIRES_OK(ctx, ctx->GetAttr("nn_max", &nn_max_ ));
OP_REQUIRES_OK(ctx, ctx->GetAttr("tn_max", &tn_max_ ));
OP_REQUIRES_OK(ctx, ctx->GetAttr("bench", &bench_ ));
repeat_ = bench_ ? bench_ : 1;
flops_ = (float)(blocks_ * blk_size_ * blk_size_);
}
void Compute(OpKernelContext* ctx) override
{
if (major_ == 0)
{
GetCountSMsVersion(&major_, NULL);
if (bench_)
{
const char* op; // = ? "NT" : OP == NN_OP ? "NN" : "TN";
int ctx_blks_q, ctx_blks_k;
if (OP == NT_OP) { ctx_blks_q = ctx_blks_a_; ctx_blks_k = ctx_blks_b_; op = "NT"; }
else if (OP == NN_OP) { ctx_blks_q = ctx_blks_c_; ctx_blks_k = ctx_blks_b_; op = "NN"; }
else { ctx_blks_q = ctx_blks_b_; ctx_blks_k = ctx_blks_c_; op = "TN"; }
sprintf(bench_string_, "op:%s bsize:%02dx%02d blocks:%6d ctx:%5dq%5dk", op, blk_size_, blk_size_, blocks_, ctx_blks_q, ctx_blks_k);
}
}
if (OP == NT_OP)
this->Compute_NT(ctx);
else
this->Compute_XN(ctx, OP, OP == NN_OP ? nn_max_ : tn_max_);
}
void Compute_NT(OpKernelContext* ctx)
{
const Tensor& a = ctx->input(0);
const Tensor& b = ctx->input(1);
const Tensor& lut = ctx->input(2);
OP_REQUIRES(ctx, a.dims() == 3 && b.dims() == 3, errors::InvalidArgument("Mismatched Shapes: a,b"));
OP_REQUIRES(ctx, lut.dims() == 3, errors::InvalidArgument("bad lut"));
uint lut_heads = lut.dim_size(0);
uint lut_dim = lut.dim_size(1);
uint batch_dim = a.dim_size(0);
uint state_dim = a.dim_size(2);
if (head_state_ == 0)
{
OP_REQUIRES(ctx,
b.dim_size(0) == batch_dim &&
b.dim_size(2) == state_dim, errors::InvalidArgument("Mismatched Shapes"));
OP_REQUIRES(ctx, a.dim_size(1) == ctx_blks_a_ * blk_size_, errors::InvalidArgument("Bad A context length"));
OP_REQUIRES(ctx, b.dim_size(1) == ctx_blks_b_ * blk_size_, errors::InvalidArgument("Bad B context length"));
head_state_ = state_dim / heads_;
OP_REQUIRES(ctx, state_dim % heads_ == 0, errors::InvalidArgument("state_dim not evenly divisible by number of heads"));
OP_REQUIRES(ctx, (head_state_ & 7) == 0, errors::InvalidArgument("Head state dim must be multiple of 8, and ideally a multiple of 64"));
OP_REQUIRES(ctx, lut_heads == heads_ || lut_heads == 1, errors::InvalidArgument("Bad head dim"));
}
Tensor* c;
TensorShape shapeC({batch_dim, heads_, blocks_, blk_size_, blk_size_});
OP_REQUIRES(ctx, shapeC.num_elements() < (1ull << 32), errors::InvalidArgument("Attention space too large. Only 32 bit address offsets currently supported (easily fixed if needed.)"));
OP_REQUIRES_OK(ctx, ctx->allocate_output(0, shapeC, &c));
const uint2* l_ptr = (const uint2*)lut.flat<int32>().data();
CUstream stream = ((CUDAStream*)ctx->op_device_context()->stream()->implementation())->cuda_stream();
Benchmark* bench = nullptr;
if (bench_) bench = new Benchmark(stream, bench_string_, 0, flops_ * (float)(batch_dim * state_dim), repeat_);
if (a.dtype() == DT_HALF)
{
OP_REQUIRES(ctx, major_ >= 7, errors::InvalidArgument("Tensorcore GPU required"));
const ehalf* a_ptr = (const ehalf*)a.tensor_data().data();
const ehalf* b_ptr = (const ehalf*)b.tensor_data().data();
for (int r = 0; r < repeat_; r++)
if (c->dtype() == DT_HALF)
bst_hgemm_nt<ehalf,ehalf2,ehalf4>(stream, l_ptr, a_ptr, b_ptr, (ehalf*)c->tensor_data().data(), blk_size_, blocks_, batch_dim, ctx_blks_a_, ctx_blks_b_, heads_, head_state_, lut_heads, lut_dim);
else
bst_hgemm_nt<bhalf,bhalf2,bhalf4>(stream, l_ptr, a_ptr, b_ptr, (bhalf*)c->tensor_data().data(), blk_size_, blocks_, batch_dim, ctx_blks_a_, ctx_blks_b_, heads_, head_state_, lut_heads, lut_dim);
}
else
{
const float* a_ptr = (const float*)a.tensor_data().data();
const float* b_ptr = (const float*)b.tensor_data().data();
bhalf* c_ptr = (bhalf*)c->tensor_data().data();
OP_REQUIRES(ctx, blk_size_ == 32, errors::InvalidArgument("Only blocksize=32 supported for fp32 pathway."));
for (int r = 0; r < repeat_; r++)
bst_sgemm_nt(stream, l_ptr, a_ptr, b_ptr, c_ptr, blk_size_, blocks_, batch_dim, ctx_blks_a_, ctx_blks_b_, heads_, head_state_, lut_heads, lut_dim);
}
if (bench) delete bench;
}
void Compute_XN(OpKernelContext* ctx, uint op, uint max_lut)
{
const Tensor& a = ctx->input(0);
const Tensor& b = ctx->input(1);
const Tensor& lut = ctx->input(2 + op);
OP_REQUIRES(ctx, a.dims() == 5 && b.dims() == 3, errors::InvalidArgument("Mismatched Shapes: a,b"));
OP_REQUIRES(ctx, lut.dims() == 3, errors::InvalidArgument("Bad lut"));
uint lut_heads = lut.dim_size(0);
uint lut_dim = lut.dim_size(1);
uint batch_dim = b.dim_size(0);
uint state_dim = b.dim_size(2);
if (head_state_ == 0)
{
OP_REQUIRES(ctx,
a.dim_size(0) == batch_dim &&
a.dim_size(1) == heads_ &&
a.dim_size(2) == blocks_ &&
a.dim_size(3) == blk_size_ &&
a.dim_size(4) == blk_size_, errors::InvalidArgument("Mismatched A shape"));
head_state_ = state_dim / heads_;
OP_REQUIRES(ctx, state_dim % heads_ == 0, errors::InvalidArgument("state_dim not evenly divisible by number of heads"));
OP_REQUIRES(ctx, (head_state_ & 7) == 0, errors::InvalidArgument("Head state dim must be multiple of 8, and ideally a multiple of 64"));
OP_REQUIRES(ctx, b.dim_size(1) == ctx_blks_b_ * blk_size_, errors::InvalidArgument("Bad B context length"));
OP_REQUIRES(ctx, lut_heads == heads_ || lut_heads == 1, errors::InvalidArgument("Bad head dim"));
uint div = CEIL_DIV(head_state_, 64);
magicu64(div, magic_, shift_);
OP_REQUIRES(ctx, magic_ > 0, errors::InvalidArgument("Bad magic for div: ", div));
}
Tensor* c;
TensorShape shapeC({batch_dim, ctx_blks_c_ * blk_size_, state_dim});
OP_REQUIRES_OK(ctx, ctx->allocate_output(0, shapeC, &c));
const uint2* l_ptr = (const uint2*)lut.flat<int32>().data();
CUstream stream = ((CUDAStream*)ctx->op_device_context()->stream()->implementation())->cuda_stream();
Benchmark* bench = nullptr;
if (bench_) bench = new Benchmark(stream, bench_string_, 0, flops_ * (float)(batch_dim * state_dim), repeat_);
if (a.dtype() == DT_HALF)
{
OP_REQUIRES(ctx, major_ >= 7, errors::InvalidArgument("Tensorcore GPU required"));
const ehalf* a_ptr = (const ehalf*)a.tensor_data().data();
const ehalf* b_ptr = (const ehalf*)b.tensor_data().data();
ehalf* c_ptr = ( ehalf*)c->tensor_data().data();
for (int r = 0; r < repeat_; r++)
bst_hgemm_xn(stream, l_ptr, a_ptr, b_ptr, c_ptr, blk_size_, blocks_, batch_dim, ctx_blks_b_, ctx_blks_c_, heads_, head_state_, lut_heads, lut_dim, op, magic_, shift_, max_lut);
}
else
{
const bhalf* a_ptr = (const bhalf*)a.tensor_data().data();
const float* b_ptr = (const float*)b.tensor_data().data();
float* c_ptr = ( float*)c->tensor_data().data();
OP_REQUIRES(ctx, blk_size_ == 32, errors::InvalidArgument("Only blocksize=32 supported for fp32 pathway."));
for (int r = 0; r < repeat_; r++)
bst_sgemm_xn(stream, l_ptr, a_ptr, b_ptr, c_ptr, blk_size_, blocks_, batch_dim, ctx_blks_b_, ctx_blks_c_, heads_, head_state_, lut_heads, lut_dim, op, magic_, shift_, max_lut);
}
if (bench) delete bench;
}
int major_, heads_, blocks_, blk_size_, ctx_blks_a_, ctx_blks_b_, ctx_blks_c_, nn_max_, tn_max_, bench_, repeat_, flops_;
uint magic_, shift_, head_state_;
char bench_string_[256];
};
REGISTER_KERNEL_BUILDER(Name("BlocksparseTransformerNT").Device(DEVICE_GPU),BlocksparseTransformerOp<NT_OP>);
REGISTER_KERNEL_BUILDER(Name("BlocksparseTransformerNN").Device(DEVICE_GPU),BlocksparseTransformerOp<NN_OP>);
REGISTER_KERNEL_BUILDER(Name("BlocksparseTransformerTN").Device(DEVICE_GPU),BlocksparseTransformerOp<TN_OP>);
template <typename T, typename V>
bool BlocksparseMaskedSoftmax(CUstream stream,
const uint2* lut,
const char* mask,
const bhalf* x,
T* y,
uint block_size, uint blocks,
uint batch_dim, uint head_dim, uint ctx_blks,
uint lut_heads, uint lut_dim, uint max_lut,
uint mask_heads, float scale);
REGISTER_OP("BlocksparseMaskedSoftmax")
.Input("x: bfloat16")
.Input("scale: float")
.Input("lut: int32")
.Input("mask: MT")
.Output("y: T")
.Attr("T: {half, bfloat16}")
.Attr("MT: { uint8, uint16, uint32, uint64 }")
.Attr("blocks: int")
.Attr("blk_size: int")
.Attr("ctx_blks: int")
.Attr("lut_max: int")
.SetShapeFn(UnchangedShape)
.Doc(R"doc(
Blocksparse softmax with mask
)doc");
REGISTER_OP("BlocksparseSoftmax")
.Input("x: bfloat16")
.Input("scale: float")
.Input("lut: int32")
.Output("y: T")
.Attr("T: {half, bfloat16}")
.Attr("blocks: int")
.Attr("blk_size: int")
.Attr("ctx_blks: int")
.Attr("lut_max: int")
.SetShapeFn(UnchangedShape)
.Doc(R"doc(
Blocksparse softmax without mask
)doc");
class BlocksparseMaskedSoftmaxOp : public OpKernel {
public:
explicit BlocksparseMaskedSoftmaxOp(OpKernelConstruction* ctx) : OpKernel(ctx)
{
OP_REQUIRES_OK(ctx, ctx->GetAttr("blocks", &blocks_ ));
OP_REQUIRES_OK(ctx, ctx->GetAttr("blk_size", &blk_size_ ));
OP_REQUIRES_OK(ctx, ctx->GetAttr("ctx_blks", &ctx_blks_ ));
OP_REQUIRES_OK(ctx, ctx->GetAttr("lut_max", &lut_max_ ));
OP_REQUIRES(ctx, lut_max_ * blk_size_ <= 32*1024, errors::InvalidArgument("max sparse sofmax dim: 32K"));
}
void Compute(OpKernelContext* ctx) override {
const Tensor& x = ctx->input(0);
const Tensor& s = ctx->input(1);
const Tensor& lut = ctx->input(2);
OP_REQUIRES(ctx, x.dims() == 5, errors::InvalidArgument("expecting 5 dims: (batch, head, block, blk_size, blk_size)"));
OP_REQUIRES(ctx, lut.dims() == 3, errors::InvalidArgument("expecting 3 lut dims (head, entry, data)"));
uint lut_heads = lut.dim_size(0);
uint lut_dim = lut.dim_size(1);
uint batch_dim = x.dim_size(0);
uint head_dim = x.dim_size(1);
uint mask_heads = 1;
const char* m_ptr = NULL;
if (ctx->num_inputs() > 3)
{
const Tensor& mask = ctx->input(3);
OP_REQUIRES(ctx, mask.dims() == 3, errors::InvalidArgument("expecting 3 mask dims (head, blk_size, block)"));
mask_heads = mask.dim_size(0);
m_ptr = mask.tensor_data().data();
}
OP_REQUIRES(ctx, lut_heads == head_dim || lut_heads == 1, errors::InvalidArgument("Bad lut head dim"));
OP_REQUIRES(ctx, mask_heads == head_dim || mask_heads == 1, errors::InvalidArgument("Bad mask head dim"));
Tensor* y = NULL;
OP_REQUIRES_OK(ctx, ctx->allocate_output(0, x.shape(), &y));
const uint2* l_ptr = (const uint2*)lut.flat<int32>().data();
const bhalf* x_ptr = (const bhalf*)x.tensor_data().data();
float scale = s.scalar<float>()();
CUstream stream = ((CUDAStream*)ctx->op_device_context()->stream()->implementation())->cuda_stream();
if (y->dtype() == DT_HALF)
BlocksparseMaskedSoftmax<ehalf,ehalf2>(stream, l_ptr, m_ptr, x_ptr, (ehalf*)y->tensor_data().data(), blk_size_, blocks_, batch_dim, head_dim, ctx_blks_, lut_heads, lut_dim, lut_max_, mask_heads, scale);
else
BlocksparseMaskedSoftmax<bhalf,bhalf2>(stream, l_ptr, m_ptr, x_ptr, (bhalf*)y->tensor_data().data(), blk_size_, blocks_, batch_dim, head_dim, ctx_blks_, lut_heads, lut_dim, lut_max_, mask_heads, scale);
}
int blocks_, blk_size_, ctx_blks_, lut_max_;
};
REGISTER_KERNEL_BUILDER(Name("BlocksparseMaskedSoftmax").Device(DEVICE_GPU).HostMemory("scale"),BlocksparseMaskedSoftmaxOp);
REGISTER_KERNEL_BUILDER(Name("BlocksparseSoftmax" ).Device(DEVICE_GPU).HostMemory("scale"),BlocksparseMaskedSoftmaxOp);
template <typename T, typename V>
bool BlocksparseMaskedSoftmaxGrad(CUstream stream,
const uint2* lut,
const T* dy,
const T* y,
T* dx,
uint block_size, uint blocks,
uint batch_dim, uint head_dim, uint ctx_blks,
uint lut_heads, uint lut_dim, uint max_lut,
float scale);
REGISTER_OP("BlocksparseSoftmaxGrad")
.Input("dy: T")
.Input("y: T")
.Input("scale: float")
.Input("lut: int32")
.Output("dx: T")
.Attr("T: {half, bfloat16}")
.Attr("blocks: int")
.Attr("blk_size: int")
.Attr("ctx_blks: int")
.Attr("lut_max: int")
.SetShapeFn(UnchangedShape)
.Doc(R"doc(
Blocksparse softmax grad
)doc");
class BlocksparseMaskedSoftmaxGradOp : public OpKernel {
public:
explicit BlocksparseMaskedSoftmaxGradOp(OpKernelConstruction* ctx) : OpKernel(ctx)
{
OP_REQUIRES_OK(ctx, ctx->GetAttr("blocks", &blocks_ ));
OP_REQUIRES_OK(ctx, ctx->GetAttr("blk_size", &blk_size_ ));
OP_REQUIRES_OK(ctx, ctx->GetAttr("ctx_blks", &ctx_blks_ ));
OP_REQUIRES_OK(ctx, ctx->GetAttr("lut_max", &lut_max_ ));
}
void Compute(OpKernelContext* ctx) override {
const Tensor& dy = ctx->input(0);
const Tensor& y = ctx->input(1);
const Tensor& s = ctx->input(2);
const Tensor& lut = ctx->input(3);
OP_REQUIRES(ctx, dy.dims() == 5, errors::InvalidArgument("expecting 5 dims: (batch, head, block, blk_size, blk_size)"));
OP_REQUIRES(ctx, lut.dims() == 3, errors::InvalidArgument("expecting 3 lut dims (head, entry, data)"));
uint lut_heads = lut.dim_size(0);
uint lut_dim = lut.dim_size(1);
uint batch_dim = dy.dim_size(0);
uint head_dim = dy.dim_size(1);
OP_REQUIRES(ctx, lut_heads == head_dim || lut_heads == 1, errors::InvalidArgument("Bad lut head dim"));
Tensor* dx = NULL;
OP_REQUIRES_OK(ctx, ctx->allocate_output(0, dy.shape(), &dx));
const uint2* l_ptr = (const uint2*)lut.flat<int32>().data();
float scale = s.scalar<float>()();
CUstream stream = ((CUDAStream*)ctx->op_device_context()->stream()->implementation())->cuda_stream();
if (dy.dtype() == DT_HALF)
{
const ehalf* dy_ptr = (const ehalf*)dy.tensor_data().data();
const ehalf* y_ptr = (const ehalf*)y.tensor_data().data();
ehalf* dx_ptr = ( ehalf*)dx->tensor_data().data();
BlocksparseMaskedSoftmaxGrad<ehalf,ehalf2>(stream, l_ptr, dy_ptr, y_ptr, dx_ptr, blk_size_, blocks_, batch_dim, head_dim, ctx_blks_, lut_heads, lut_dim, lut_max_, scale);
}
else
{
const bhalf* dy_ptr = (const bhalf*)dy.tensor_data().data();
const bhalf* y_ptr = (const bhalf*)y.tensor_data().data();
bhalf* dx_ptr = ( bhalf*)dx->tensor_data().data();
BlocksparseMaskedSoftmaxGrad<bhalf,bhalf2>(stream, l_ptr, dy_ptr, y_ptr, dx_ptr, blk_size_, blocks_, batch_dim, head_dim, ctx_blks_, lut_heads, lut_dim, lut_max_, scale);
}
}
int blocks_, blk_size_, ctx_blks_, lut_max_;
};
REGISTER_KERNEL_BUILDER(Name("BlocksparseSoftmaxGrad").Device(DEVICE_GPU).HostMemory("scale"),BlocksparseMaskedSoftmaxGradOp);
bool BstPartialAutoregressiveMask(CUstream stream,
const int2* lut, const char* maskI, char* maskO,
uint block_size, uint blocks, uint lut_heads, uint lut_dim, int autoregress_at_k);
REGISTER_OP("BstPartialAutoregressiveMask")
.Input("mask: MT")
.Input("lut: int32")
.Input("autoregress_at_k: KT")
.Output("mask_out: MT")
.Attr("MT: { uint8, uint16, uint32, uint64 }")
.Attr("KT: { int32, int64 }")
.Attr("blocks: int")
.Attr("blk_size: int")
.Attr("ctx_blks_k: int")
.SetShapeFn(UnchangedShape)
.Doc(R"doc(
Dynamically apply autoregressive property to softmax mask from autoregress_at_k on
)doc");
class PartialAutoregressiveMaskOp : public OpKernel {
public:
explicit PartialAutoregressiveMaskOp(OpKernelConstruction* ctx) : OpKernel(ctx)
{
int ctx_blks_k;
OP_REQUIRES_OK(ctx, ctx->GetAttr("blocks", &blocks_ ));
OP_REQUIRES_OK(ctx, ctx->GetAttr("blk_size", &blk_size_ ));
OP_REQUIRES_OK(ctx, ctx->GetAttr("ctx_blks_k", &ctx_blks_k));
ctx_keys_ = ctx_blks_k * blk_size_;
}
void Compute(OpKernelContext* ctx) override {
const Tensor& mask = ctx->input(0);
const Tensor& lut = ctx->input(1);
const Tensor& k = ctx->input(2);
int key = k.dtype() == DT_INT64 ? (int)k.scalar<int64>()() : k.scalar<int32>()();
OP_REQUIRES(ctx, key >= 0 && key < ctx_keys_, errors::InvalidArgument("autoregress_at_key out of range"));
OP_REQUIRES(ctx, lut.dims() == 3, errors::InvalidArgument("expecting 3 lut dims (head, entry, data)"));
OP_REQUIRES(ctx, mask.dims() == 3, errors::InvalidArgument("expecting 3 mask dims (head, blk_size, block)"));
uint mask_heads = mask.dim_size(0);
uint lut_heads = lut.dim_size(0);
uint lut_dim = lut.dim_size(1);
OP_REQUIRES(ctx, mask_heads == lut_heads, errors::InvalidArgument("Bad lut/mask head dim"));
Tensor* out = NULL;
OP_REQUIRES_OK(ctx, ctx->allocate_output(0, mask.shape(), &out));
const int2* l_ptr = (const int2*)lut.flat<int32>().data();
const char* m_ptr = mask.tensor_data().data();
char* o_ptr = (char*)out->tensor_data().data();
CUstream stream = ((CUDAStream*)ctx->op_device_context()->stream()->implementation())->cuda_stream();
BstPartialAutoregressiveMask(stream, l_ptr, m_ptr, o_ptr, blk_size_, blocks_, lut_heads, lut_dim, key);
}
int blocks_, blk_size_, ctx_keys_;
};
REGISTER_KERNEL_BUILDER(Name("BstPartialAutoregressiveMask").Device(DEVICE_GPU).HostMemory("autoregress_at_k"),PartialAutoregressiveMaskOp);