18 KiB
18 KiB
In [ ]:
!#nvidia-smiIn [ ]:
!#pip install nvidia-cutlassIn [ ]:
import torch
import random
import cutlass
# This controls whether the C++ GEMM declaration will be printed at each step.
# Set to `false` to omit this information.
print_module = True
# Input tensor: [N, H, W, C] under the channel-last layout
N, H, W, C = [32, 28, 28, 64]
# Weight tensor: [K, R, S, C] under the channel-last layout
K, R, S = [128, 3, 3]
# Stride, and padding
stride = (2, 2)
padding = (1, 1)
dilation = (1, 1)
# Compute the output size [N, P, Q, K]
N, P, Q, K = cutlass.Conv2d.output_size((N, H, W, C), (K, R, S, C), padding, stride, dilation)
dtype = torch.float16
type_A = torch.float16
type_B = torch.float16
type_C = torch.float16
type_D = torch.float16
torch.manual_seed(1234)
input = torch.ceil(
torch.empty(size=(N, C, H, W), dtype=type_A, device="cuda").uniform_(-4.5, 3.5)
).to(memory_format=torch.channels_last)
weight = torch.ceil(
torch.empty(size=(K, C, R, S), dtype=type_B, device="cuda").uniform_(-4.5, 3.5)
).to(memory_format=torch.channels_last)
tensor_C = torch.ceil(
torch.empty(size=(N, K, P, Q), dtype=type_B, device="cuda").uniform_(-4.5, 3.5)
).to(memory_format=torch.channels_last)
output = torch.zeros_like(tensor_C)
alpha = 1.0
beta = 0.0In [ ]:
# Specifying `element_accumulator` is not required if it is the same as `element`
plan = cutlass.Conv2dFprop(element=dtype, element_accumulator=torch.float32)
plan.run(input, weight, tensor_C, output, stride, padding, dilation, alpha, beta, print_module=print_module)In [ ]:
output_torch = alpha * torch.ops.aten.conv2d(
input, weight, stride=stride, padding=padding, dilation=dilation
) + beta * tensor_C
assert torch.equal(output_torch, output)In [ ]:
grad_output = torch.ceil(
torch.empty(size=(N, K, P, Q), dtype=type_A, device="cuda").uniform_(-4.5, 3.5)
).to(memory_format=torch.channels_last)
grad_input = torch.zeros_like(input)
grad_weight = torch.zeros_like(weight)
tensor_C_dgrad = torch.ceil(
torch.empty(size=(N, C, H, W), dtype=type_A, device="cuda").uniform_(-4.5, 3.5)
).to(memory_format=torch.channels_last)
tensor_C_wgrad = torch.ceil(
torch.empty(size=(K, C, R, S), dtype=type_B, device="cuda").uniform_(-4.5, 3.5)
).to(memory_format=torch.channels_last)In [ ]:
plan_dgrad = cutlass.Conv2dDgrad(element=dtype, element_accumulator=torch.float32)
plan_dgrad.run(grad_output, weight, tensor_C_dgrad, grad_input, stride, padding, dilation, alpha, beta, print_module=print_module)
grad_input_torch = alpha * torch.nn.grad.conv2d_input(
(N, C, H, W),
weight, grad_output,
stride=stride, padding=padding
) + beta * tensor_C_dgrad
assert torch.equal(grad_input_torch, grad_input)In [ ]:
plan_wgrad = cutlass.Conv2dWgrad(element=dtype, element_accumulator=torch.float32)
plan_wgrad.run(grad_output, input, tensor_C_wgrad, grad_weight, stride, padding, dilation, alpha, beta, print_module=print_module)
grad_weight_torch = alpha * torch.nn.grad.conv2d_weight(
input, (K, C, R, S), grad_output,
stride=stride, padding=padding
) + beta * tensor_C_wgrad
assert torch.equal(grad_weight_torch, grad_weight)In [ ]:
plan.opclass = "tensor_op"
tiles = plan.tile_descriptions()
print(f'{len(tiles)} tile descriptions returned')
num_print = 10
print(f'First {num_print} tile descriptions are:')
for td in tiles[:num_print]:
print(td)In [ ]:
random.seed(42)
idx = random.randint(0, len(tiles)-1)
td = tiles[idx]
print(f'Tile description {idx} is: {td}')
plan.tile_description = td
plan.run(input, weight, tensor_C, output, stride, padding, dilation, alpha, beta, print_module=print_module)
assert torch.equal(output_torch, output)In [ ]:
if plan.cc == 70:
plan.tile_description = {
"threadblock_shape": [64, 256, 32],
"warp_count": [1, 4, 1],
"stages": 2,
"instruction_shape": [8, 8, 4], # optional,
"cluster_shape": [1, 1, 1] # optional, only [1, 1, 1] is supported currently
}
elif plan.cc == 75:
plan.tile_description = {
"threadblock_shape": [128, 64, 32],
"warp_count": [2, 1, 1],
"stages": 2,
"instruction_shape": [16, 8, 8], # optional,
"cluster_shape": [1, 1, 1] # optional, only [1, 1, 1] is supported currently
}
elif plan.cc == 80:
plan.tile_description = {
"threadblock_shape": [128, 128, 64],
"warp_count": [2, 2, 1],
"stages": 4,
"instruction_shape": [16, 8, 16], # optional,
"cluster_shape": [1, 1, 1] # optional, only [1, 1, 1] is supported currently
}
elif plan.cc == 86:
plan.tile_description = {
"threadblock_shape": [128, 64, 64],
"warp_count": [2, 2, 1],
"stages": 3,
"instruction_shape": [16, 8, 16],
"cluster_shape": [1, 1, 1]
}
plan.run(input, weight, tensor_C, output, stride, padding, dilation, alpha, beta, print_module=print_module)
assert torch.equal(output_torch, output)In [ ]:
plan.iterator_algorithm = "analytic"
plan.run(input, weight, tensor_C, output, stride, padding, dilation, alpha, beta, print_module=print_module)
assert torch.equal(output_torch, output)In [ ]:
plan.swizzling_stride = 4
plan.run(input, weight, tensor_C, output, stride, padding, dilation, alpha, beta, print_module=print_module)
assert torch.equal(output_torch, output)In [ ]:
# Parallel Split-K with 5 slices
grad_weight_parallel = torch.zeros_like(grad_weight)
plan_wgrad.run(
grad_output, input, tensor_C_wgrad, grad_weight_parallel,
stride, padding, dilation, alpha, beta, print_module=print_module, split_k=("parallel", 5))
assert torch.equal(grad_weight_torch, grad_weight_parallel)
# Serial Split-K with 3 slices
grad_weight_serial = torch.zeros_like(grad_weight)
plan_wgrad.run(
grad_output, input, tensor_C_wgrad, grad_weight_serial,
stride, padding, dilation, alpha, beta, print_module=print_module, split_k=("serial", 3))
assert torch.equal(grad_weight_torch, grad_weight_serial)