Repository: pytorch
Issue: Different tensor strides can result in surprisingly large discrepancies in Conv2d outputs
Labels: module: convolution,triaged,module: tf32

### 🐛 Describe the bug

I ran into a counterintuitive behavior that might be a result of the interplay between strides, Conv2d, and tf32. The code to reproduce the behavior is given below:

```python
import random

import numpy
import torch.backends.cuda
import torch.backends.cudnn
from torch.nn import Conv2d

if __name__ == "__main__":
    device = torch.device('cuda:0')

    seed = 123456
    torch.manual_seed(seed)
    torch.cuda.manual_seed(seed)
    torch.cuda.manual_seed_all(seed)
    numpy.random.seed(seed)
    random.seed(seed)
    torch.manual_seed(seed)
    torch.use_deterministic_algorithms(True)
    torch.set_deterministic_debug_mode(True)
    torch.backends.cudnn.benchmark = False
    torch.backends.cudnn.deterministic = True

    print("torch version =", torch.__version__)
    print("device name =", torch.cuda.get_device_name(device))

    in_a = torch.rand(4, 3, 512, 512, device=device)
    in_b = torch.clone(in_a.permute(0, 2, 3, 1), memory_format=torch.contiguous_format).permute(0, 3, 1, 2)

    print("in_a.stride() =", in_a.stride())
    print("in_b.stride() =", in_b.stride())

    print("max(|in_a - in_b|) =", (in_a - in_b).abs().max().item())

    proj = Conv2d(3, 128, kernel_size=3, stride=1, padding=1)
    proj.to(device)
    proj.train(False)

    out_a = proj.forward(in_a)
    out_b = proj.forward(in_b)
    print("max(|out_a-out_b|) =", (out_a - out_b).abs().max().item(), "(with tf32)")

    torch.backends.cudnn.allow_tf32 = False

    out_a = proj.forward(in_a)
    out_b = proj.forward(in_b)
    print("max(|out_a-out_b|) =", (out_a - out_b).abs().max().item(), "(no tf32)")
```

Here is the output on my local machine.

```
torch version = 2.3.0+cu121
device name = NVIDIA RTX A6000
in_a.stride() = (786432, 262144, 512, 1)
in_b.stride() = (786432, 1, 1536, 3)
max(|in_a - in_b|) = 0.0
max(|out_a-out_b|) = 0.0005768537521362305 (with tf32)
max(|out_a-out_b|) = 4.76837158203125e-07 (no tf32)
```

Ideally, all differences should be zero. I learned though that Conv2d would produce different results if the strides of the input tensors are different according to [this previous issue](https://github.com/pytorch/pytorch/issues/88147). However, I was expecting the difference to be negligible, so 4.7e-07 is acceptable, but something in the order of 5.8e-4 is surprisingly large.

Interestingly, when the `padding_mode` of the Conv2d layer is set to anything other than `"zeros"`, all differences are zeros. For example, if I change the layer to

```python
proj = Conv2d(3, 128, kernel_size=3, stride=1, padding=1, padding_mode='circular')
```

The output is:

```
torch version = 2.3.0+cu121
device name = NVIDIA RTX A6000
in_a.stride() = (786432, 262144, 512, 1)
in_b.stride() = (786432, 1, 1536, 3)
max(|in_a - in_b|) = 0.0
max(|out_a-out_b|) = 0.0 (with tf32)
max(|out_a-out_b|) = 0.0 (no tf32)
```

As a result, the unintuitive and large difference can be removed in two ways.

1. Make sure that the `zeros` padding mode's implementation is consistent with other padding modes, or
2. disable tf32 for convolutions by default.

It would be great if the issue can be addressed.

### Versions

PyTorch version: 2.3.0+cu121
Is debug build: False
CUDA used to build PyTorch: 12.1
ROCM used to build PyTorch: N/A

OS: Ubuntu 22.04.3 LTS (x86_64)
GCC version: (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0
Clang version: Could not collect
CMake version: Could not collect
Libc version: glibc-2.35

Python version: 3.10.11 (main, Mar  7 2024, 16:52:06) [GCC 11.4.0] (64-bit runtime)
Python platform: Linux-5.15.146.1-microsoft-standard-WSL2-x86_64-with-glibc2.35
Is CUDA available: True
CUDA runtime version: Could not collect
CUDA_MODULE_LOADING set to: LAZY
GPU models and configuration:
GPU 0: NVIDIA RTX A6000
GPU 1: NVIDIA RTX A6000

Nvidia driver version: 545.84
cuDNN version: Could not collect
HIP runtime version: N/A
MIOpen runtime version: N/A
Is XNNPACK available: True

CPU:
Architecture:                       x86_64
CPU op-mode(s):                     32-bit, 64-bit
Address sizes:                      46 bits physical, 57 bits virtual
Byte Order:                         Little Endian
CPU(s):                             24
On-line CPU(s) list:                0-23
Vendor ID:                          GenuineIntel
Model name:                         Intel(R) Xeon(R) Silver 4310 CPU @ 2.10GHz
CPU family:                         6
Model:                              106
Thread(s) per core:                 2
Core(s) per socket:                 12
Socket(s):                          1
Stepping:                           6
BogoMIPS:                           4190.15
Flags:                              fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon rep_good nopl xtopology tsc_reliable nonstop_tsc cpuid pni pclmulqdq vmx ssse3 fma cx16 pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single ssbd ibrs ibpb stibp ibrs_enhanced tpr_shadow vnmi ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves avx512vbmi umip avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg avx512_vpopcntdq rdpid fsrm md_clear flush_l1d arch_capabilities
Virtualization:                     VT-x
Hypervisor vendor:                  Microsoft
Virtualization type:                full
L1d cache:                          576 KiB (12 instances)
L1i cache:                          384 KiB (12 instances)
L2 cache:                           15 MiB (12 instances)
L3 cache:                           18 MiB (1 instance)
Vulnerability Gather data sampling: Unknown: Dependent on hypervisor status
Vulnerability Itlb multihit:        Not affected
Vulnerability L1tf:                 Not affected
Vulnerability Mds:                  Not affected
Vulnerability Meltdown:             Not affected
Vulnerability Mmio stale data:      Mitigation; Clear CPU buffers; SMT Host state unknown
Vulnerability Retbleed:             Mitigation; Enhanced IBRS
Vulnerability Spec rstack overflow: Not affected
Vulnerability Spec store bypass:    Mitigation; Speculative Store Bypass disabled via prctl and seccomp
Vulnerability Spectre v1:           Mitigation; usercopy/swapgs barriers and __user pointer sanitization
Vulnerability Spectre v2:           Mitigation; Enhanced IBRS, IBPB conditional, RSB filling, PBRSB-eIBRS SW sequence
Vulnerability Srbds:                Not affected
Vulnerability Tsx async abort:      Not affected

Versions of relevant libraries:
[pip3] numpy==1.26.4
[pip3] torch==2.3.0
[pip3] torchaudio==2.3.0
[pip3] torchvision==0.18.0
[pip3] triton==2.3.0
[conda] Could not collect

cc @zasdfgbnm @ptrblck