Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Re-organize SLL ops, pt 7 #3650

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 0 additions & 20 deletions fbgemm_gpu/fbgemm_gpu/sll/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,7 @@
)

from fbgemm_gpu.sll.triton_sll import ( # noqa F401
array_jagged_bmm_jagged_out,
dense_jagged_cat_jagged_out,
jagged2_to_padded_dense,
# jagged_dense_bmm,
jagged_dense_elementwise_mul_jagged_out,
jagged_jagged_bmm_jagged_out,
triton_jagged_self_substraction_jagged_out,
)

Expand Down Expand Up @@ -269,28 +264,13 @@

# pyre-ignore[5]
sll_gpu_registrations = {
"sll_dense_jagged_cat_jagged_out": {
"CUDA": dense_jagged_cat_jagged_out,
},
"sll_jagged_self_substraction_jagged_out": {
"CUDA": triton_jagged_self_substraction_jagged_out,
},
"sll_jagged2_to_padded_dense": {
"CUDA": jagged2_to_padded_dense,
"AutogradCUDA": jagged2_to_padded_dense,
},
"sll_jagged_dense_elementwise_mul_jagged_out": {
"CUDA": jagged_dense_elementwise_mul_jagged_out,
"AutogradCUDA": jagged_dense_elementwise_mul_jagged_out,
},
"sll_array_jagged_bmm_jagged_out": {
"CUDA": array_jagged_bmm_jagged_out,
"AutogradCUDA": array_jagged_bmm_jagged_out,
},
"sll_jagged_jagged_bmm_jagged_out": {
"CUDA": jagged_jagged_bmm_jagged_out,
"AutogradCUDA": jagged_jagged_bmm_jagged_out,
},
}

for op_name, dispatches in sll_cpu_registrations.items():
Expand Down
33 changes: 33 additions & 0 deletions fbgemm_gpu/fbgemm_gpu/sll/triton/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,31 @@

# pyre-strict

from fbgemm_gpu.sll.triton.triton_dense_jagged_cat_jagged_out import (
dense_jagged_cat_jagged_out,
)

from fbgemm_gpu.sll.triton.triton_jagged2_to_padded_dense import ( # noqa F401
jagged2_to_padded_dense,
Jagged2ToPaddedDense, # noqa F401
)

from fbgemm_gpu.sll.triton.triton_jagged_bmm import ( # noqa F401
jagged_dense_bmm,
jagged_jagged_bmm,
JaggedDenseBmm, # noqa F401
JaggedJaggedBmm, # noqa F401
)

from fbgemm_gpu.sll.triton.triton_jagged_bmm_jagged_out import ( # noqa F401
array_jagged_bmm_jagged_out,
ArrayJaggedBmmNopadding, # noqa F401
jagged_jagged_bmm_jagged_out,
JaggedJaggedBmmNoPadding, # noqa F401
triton_array_jagged_bmm_jagged_out, # noqa F401
triton_jagged_jagged_bmm_jagged_out, # noqa F401
)

from fbgemm_gpu.sll.triton.triton_jagged_dense_elementwise_add import ( # noqa F401
jagged_dense_elementwise_add,
JaggedDenseAdd, # noqa F401
Expand Down Expand Up @@ -43,6 +61,9 @@

# pyre-ignore[5]
op_registrations = {
"sll_dense_jagged_cat_jagged_out": {
"CUDA": dense_jagged_cat_jagged_out,
},
"sll_jagged_dense_bmm": {
"CUDA": jagged_dense_bmm,
"AutogradCUDA": jagged_dense_bmm,
Expand All @@ -51,6 +72,18 @@
"CUDA": jagged_jagged_bmm,
"AutogradCUDA": jagged_jagged_bmm,
},
"sll_jagged2_to_padded_dense": {
"CUDA": jagged2_to_padded_dense,
"AutogradCUDA": jagged2_to_padded_dense,
},
"sll_array_jagged_bmm_jagged_out": {
"CUDA": array_jagged_bmm_jagged_out,
"AutogradCUDA": array_jagged_bmm_jagged_out,
},
"sll_jagged_jagged_bmm_jagged_out": {
"CUDA": jagged_jagged_bmm_jagged_out,
"AutogradCUDA": jagged_jagged_bmm_jagged_out,
},
"sll_jagged_softmax": {
"CUDA": jagged_softmax,
"AutogradCUDA": jagged_softmax,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
# All rights reserved.
#
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree.

# pyre-unsafe

import torch
import triton
import triton.language as tl


@triton.jit
def dense_jagged_cat_jagged_out_kernel(
a_ptr, # dense
b_ptr, # jagged
c_ptr, # jagged
b_offsets_ptr,
c_offsets_ptr,
max_seq_len,
BLOCK_SIZE: tl.constexpr,
):
pid_batch = tl.program_id(0)
b_start = tl.load(b_offsets_ptr + pid_batch)
b_end = tl.load(b_offsets_ptr + pid_batch + 1)
c_start = b_start + pid_batch
N = b_end - b_start
N = tl.minimum(N, max_seq_len)

a = tl.load(a_ptr + pid_batch)
tl.store(c_ptr + c_start, a)

offs_k = tl.arange(0, BLOCK_SIZE)
for k in range(0, N, BLOCK_SIZE):
b_offset = k + offs_k
b_ptrs = b_ptr + b_start + b_offset
b = tl.load(b_ptrs, mask=b_offset < N, other=0.0)
tl.store(c_ptr + c_start + 1 + b_offset, b, mask=b_offset < N)
tl.store(c_offsets_ptr + pid_batch, b_start + pid_batch)


def dense_jagged_cat_jagged_out(
a: torch.Tensor,
b: torch.Tensor,
b_offsets: torch.Tensor,
max_seq_len: int,
):
assert a.is_contiguous()
assert b.is_contiguous()
assert b_offsets.is_contiguous()
B = a.size(0)
BLOCK_SIZE = 128
c = torch.zeros(b.size(0) + a.size(0), dtype=a.dtype, device=a.device)
c_offsets = torch.empty(
b_offsets.size(0), dtype=b_offsets.dtype, device=b_offsets.device
) # B + 1

dense_jagged_cat_jagged_out_kernel[(B,)](
a,
b,
c,
b_offsets,
c_offsets,
max_seq_len,
# pyre-fixme[6]: For 7th argument expected `constexpr` but got `int`.
BLOCK_SIZE,
)

c_offsets[-1] = b_offsets[-1] + B

return c, c_offsets
222 changes: 222 additions & 0 deletions fbgemm_gpu/fbgemm_gpu/sll/triton/triton_jagged2_to_padded_dense.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
# All rights reserved.
#
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree.

# pyre-unsafe

from typing import Tuple

import torch
import triton
import triton.language as tl

from .common import expect_contiguous


@triton.jit
def jagged2_to_padded_dense_kernel(
x_ptr,
lengths_ptr,
offsets_ptr,
output_dense_ptr,
stride_b,
stride_m,
stride_n,
max_length,
BLOCK_M: tl.constexpr,
BLOCK_N: tl.constexpr,
):
pid_batch = tl.program_id(2)
pid_m = tl.program_id(0)
pid_n = tl.program_id(1)

begin = tl.load(offsets_ptr + pid_batch)
seqlen = tl.load(lengths_ptr + pid_batch)

seqlen = tl.minimum(seqlen, max_length)
if seqlen == 0:
return

offs_m = pid_m * BLOCK_M + tl.arange(0, BLOCK_M)
offs_n = pid_n * BLOCK_N + tl.arange(0, BLOCK_N)

x_ptrs = x_ptr + begin + offs_m[:, None] * seqlen + offs_n[None, :]
x = tl.load(x_ptrs, mask=((offs_m[:, None] < seqlen) & (offs_n[None, :] < seqlen)))

out_ptrs = (
output_dense_ptr
+ pid_batch * stride_b
+ offs_m[:, None] * stride_m
+ offs_n[None, :] * stride_n
)
tl.store(
out_ptrs, x, mask=((offs_m[:, None] < seqlen) & (offs_n[None, :] < seqlen))
)


@triton.jit
def padded_dense_to_jagged2_kernel(
x_ptr,
lengths_ptr,
offsets_ptr,
output_jagged_ptr,
stride_b,
stride_m,
stride_n,
max_length,
BLOCK_M: tl.constexpr,
BLOCK_N: tl.constexpr,
):
pid_batch = tl.program_id(2)
pid_m = tl.program_id(0)
pid_n = tl.program_id(1)

begin = tl.load(offsets_ptr + pid_batch)
# end = tl.load(offsets_ptr + pid_batch + 1)
seqlen = tl.load(lengths_ptr + pid_batch)

seqlen = tl.minimum(seqlen, max_length)

if seqlen == 0:
return

offs_m = pid_m * BLOCK_M + tl.arange(0, BLOCK_M)
offs_n = pid_n * BLOCK_N + tl.arange(0, BLOCK_N)

x_ptrs = (
x_ptr
+ pid_batch * stride_b
+ offs_m[:, None] * stride_m
+ offs_n[None, :] * stride_n
)
x = tl.load(x_ptrs, mask=((offs_m[:, None] < seqlen) & (offs_n[None, :] < seqlen)))
out_ptrs = output_jagged_ptr + begin + offs_m[:, None] * seqlen + offs_n[None, :]
tl.store(
out_ptrs, x, mask=((offs_m[:, None] < seqlen) & (offs_n[None, :] < seqlen))
)


def jagged2_to_padded_dense_fwd(
values: torch.Tensor,
lengths: torch.Tensor,
offsets: torch.Tensor,
max_length: int,
padding_value: float,
) -> torch.Tensor:
B = offsets.size(0) - 1

output_dense = torch.full(
(B, max_length, max_length),
padding_value,
dtype=values.dtype,
device=values.device,
)
BLOCK_M = 32
BLOCK_N = 32
num_blocks_m = triton.cdiv(max_length, BLOCK_M)
num_blocks_n = triton.cdiv(max_length, BLOCK_N)
grid = (num_blocks_m, num_blocks_n, B)

jagged2_to_padded_dense_kernel[grid](
values,
lengths,
offsets,
output_dense,
output_dense.stride(0),
output_dense.stride(1),
output_dense.stride(2),
max_length,
# pyre-fixme[6]: Incompatible parameter type [6]: expected `constexpr` but got `int`.
BLOCK_M,
# pyre-fixme[6]: Incompatible parameter type [6]: expected `constexpr` but got `int`.
BLOCK_N,
)

return output_dense


def padded_dense_to_jagged2_fwd(
values: torch.Tensor,
lengths: torch.Tensor,
offsets: torch.Tensor,
max_length: int,
) -> torch.Tensor:
B = values.size(0)
output_jagged = torch.empty(
int(offsets[-1]), dtype=values.dtype, device=values.device
)
BLOCK_M = 32
BLOCK_N = 32
num_blocks_m = triton.cdiv(max_length, BLOCK_M)
num_blocks_n = triton.cdiv(max_length, BLOCK_N)
grid = (num_blocks_m, num_blocks_n, B)

padded_dense_to_jagged2_kernel[grid](
values,
lengths,
offsets,
output_jagged,
values.stride(0),
values.stride(1),
values.stride(2),
max_length,
# pyre-fixme[6]: Incompatible parameter type [6]: expected `constexpr` but got `int`.
BLOCK_M,
# pyre-fixme[6]: Incompatible parameter type [6]: expected `constexpr` but got `int`.
BLOCK_N,
)

return output_jagged


class Jagged2ToPaddedDense(torch.autograd.Function):
@staticmethod
# pyre-fixme
def forward(
ctx,
values: torch.Tensor,
offsets: torch.Tensor,
max_length: int,
padding_value: float,
) -> torch.Tensor:
lengths_square = offsets[1:] - offsets[0:-1:1]
lengths = torch.sqrt(lengths_square).to(torch.int32)

ctx.max_length = max_length
ctx.save_for_backward(lengths, offsets)

output = jagged2_to_padded_dense_fwd(
values, lengths, offsets, max_length, padding_value
)
return output

@staticmethod
# pyre-fixme
def backward(
ctx, grad_output: torch.Tensor
) -> Tuple[torch.Tensor, None, None, None]:
max_length = ctx.max_length
(lengths, offsets) = ctx.saved_tensors
grad_in = padded_dense_to_jagged2_fwd(grad_output, lengths, offsets, max_length)
return (grad_in, None, None, None)


def jagged2_to_padded_dense(
values: torch.Tensor,
offsets: torch.Tensor,
max_length: int,
padding_value: float = 0.0,
) -> torch.Tensor:
"""
values: jagged tensor with size [sum(Ni * Ni)]
offsets: offsets for jagged tensor, with size [B + 1]
max_length: maximum sequence length in the batch
padding_value: value to use for padding
return padded dense tensor of size [B, N, N]
"""
values = expect_contiguous(values)
offsets = expect_contiguous(offsets)

return Jagged2ToPaddedDense.apply(values, offsets, max_length, padding_value)
Loading
Loading