```python
import torch
import torch.nn as nn
import triton
import triton.language as tl


@triton.jit
def _dilated_conv_kernel(
    X, W, Bias, Y,
    BATCH: tl.constexpr,
    CIN: tl.constexpr,
    COUT: tl.constexpr,
    H: tl.constexpr,
    WIDTH: tl.constexpr,
    KH: tl.constexpr,
    KW: tl.constexpr,
    OH: tl.constexpr,
    OW: tl.constexpr,
    SH: tl.constexpr,
    SW: tl.constexpr,
    PH: tl.constexpr,
    PW: tl.constexpr,
    DH: tl.constexpr,
    DW: tl.constexpr,
    HAS_BIAS: tl.constexpr,
    BM: tl.constexpr,
    BN: tl.constexpr,
    BK: tl.constexpr,
):
    spatial: tl.constexpr = OH * OW
    reduction: tl.constexpr = CIN * KH * KW

    rows = tl.program_id(0) * BM + tl.arange(0, BM)
    cols = tl.program_id(1) * BN + tl.arange(0, BN)
    rk = tl.arange(0, BK)

    batch = rows // spatial
    position = rows % spatial
    base_h = (position // OW) * SH - PH
    base_w = (position % OW) * SW - PW

    accumulator = tl.full((BM, BN), 0, tl.float32)

    for tile in range(tl.cdiv(reduction, BK)):
        k = tile * BK + rk
        channel = k // (KH * KW)
        kernel_h = (k // KW) % KH
        kernel_w = k % KW

        ih = base_h[:, None] + kernel_h[None, :] * DH
        iw = base_w[:, None] + kernel_w[None, :] * DW

        input_offsets = (
            (batch[:, None] * CIN + channel[None, :]) * H + ih
        ) * WIDTH + iw

        input_mask = (
            (rows[:, None] < BATCH * spatial)
            & (k[None, :] < reduction)
            & (ih >= 0)
            & (ih < H)
            & (iw >= 0)
            & (iw < WIDTH)
        )

        a = tl.load(X + input_offsets, mask=input_mask, other=0.0)
        b = tl.load(
            W + cols[None, :] * reduction + k[:, None],
            mask=(k[:, None] < reduction) & (cols[None, :] < COUT),
            other=0.0,
        )

        accumulator = tl.dot(
            a, b, accumulator, input_precision="tf32x3"
        )

    if HAS_BIAS:
        bias = tl.load(Bias + cols, mask=cols < COUT, other=0.0)
        accumulator += bias[None, :]

    output_offsets = (
        (batch[:, None] * COUT + cols[None, :]) * spatial
        + position[:, None]
    )
    tl.store(
        Y + output_offsets,
        accumulator,
        mask=(rows[:, None] < BATCH * spatial) & (cols[None, :] < COUT),
    )


class ModelNew(nn.Module):
    def __init__(
        self,
        in_channels: int,
        out_channels: int,
        kernel_size: tuple,
        stride: int = 1,
        padding: tuple = (0, 0),
        dilation: tuple = (1, 1),
        bias: bool = False,
    ):
        super().__init__()
        self.conv2d = nn.Conv2d(
            in_channels,
            out_channels,
            kernel_size,
            stride=stride,
            padding=padding,
            dilation=dilation,
            bias=bias,
        )

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        conv = self.conv2d

        if (
            not x.is_cuda
            or x.ndim != 4
            or x.dtype != torch.float32
            or conv.weight.dtype != torch.float32
            or x.device != conv.weight.device
            or torch.is_autocast_enabled()
            or isinstance(conv.padding, str)
        ):
            return conv(x)

        if torch.cuda.get_device_capability(x.device)[0] < 8:
            return conv(x)

        batch, cin, height, width = x.shape
        cout, weight_cin, kh, kw = conv.weight.shape
        if cin != weight_cin:
            return conv(x)

        sh, sw = conv.stride
        ph, pw = conv.padding
        dh, dw = conv.dilation

        oh = (height + 2 * ph - dh * (kh - 1) - 1) // sh + 1
        ow = (width + 2 * pw - dw * (kw - 1) - 1) // sw + 1
        if oh <= 0 or ow <= 0:
            return conv(x)

        x = x.contiguous()
        weight = conv.weight.contiguous()
        bias = conv.bias

        output = torch.empty(
            (batch, cout, oh, ow), device=x.device, dtype=x.dtype
        )
        if output.numel() == 0:
            return output

        bm, bn, bk = 128, 64, 32
        grid = (
            triton.cdiv(batch * oh * ow, bm),
            triton.cdiv(cout, bn),
        )

        with torch.cuda.device(x.device):
            _dilated_conv_kernel[grid](
                x,
                weight,
                bias if bias is not None else weight,
                output,
                BATCH=batch,
                CIN=cin,
                COUT=cout,
                H=height,
                WIDTH=width,
                KH=kh,
                KW=kw,
                OH=oh,
                OW=ow,
                SH=sh,
                SW=sw,
                PH=ph,
                PW=pw,
                DH=dh,
                DW=dw,
                HAS_BIAS=bias is not None,
                BM=bm,
                BN=bn,
                BK=bk,
                num_warps=4,
                num_stages=2,
            )

        return output
```