已合并
fix: Fall back to the aclop path for reshaped Nx1 column vectors #42471
21xiu创建于 7月23日
fix: Fall back to the aclop path for reshaped Nx1 column vectors #42471
已合并
共 2 个文件变更+34-0
| @@ -228,6 +228,28 @@ class TestNpuFormatCastAclnn(TestCase): | |||
| 228 | with self.assertRaises(RuntimeError): | 228 | with self.assertRaises(RuntimeError): |
| 229 | torch_npu.npu_format_cast(nz_t, ACL_FORMAT_ND) | 229 | torch_npu.npu_format_cast(nz_t, ACL_FORMAT_ND) |
| 230 | 230 | ||
| 231 | + # ------------------------------------------------------------------ # | ||
| 232 | + # Group 7: View guard — 2D column-vector (Nx1) fallback to aclop | ||
| 233 | + # ------------------------------------------------------------------ # | ||
| 234 | + # When a 2D contiguous tensor is a view (storage shape != current shape) | ||
| 235 | + # and the target is FRACTAL_NZ, the aclnn path is skipped to avoid | ||
| 236 | + # downstream precision issues. The aclop fallback is exercised here. | ||
| 237 | + # Example: storage [1, N] viewed as [N, 1] (Nx1 column vector). | ||
| 238 | + | ||
| 239 | + | ||
| 240 | + def test_2d_view_nx1_nd_to_nz_format_id(self): | ||
| 241 | + """View [1, N] -> [N, 1]: format cast to NZ still succeeds.""" | ||
| 242 | + # Create storage [1, N], then view as [N, 1] | ||
| 243 | + N = 16 | ||
| 244 | + t = torch.rand(1, N).half().npu() | ||
| 245 | + t_view = t.t() # contiguous, Nx1 column vector | ||
| 246 | + self.assertTrue(t_view.is_contiguous()) | ||
| 247 | + | ||
| 248 | + out = torch_npu.npu_format_cast(t_view, ACL_FORMAT_FRACTAL_NZ) | ||
| 249 | + self.assertEqual(torch_npu.get_npu_format(out), ACL_FORMAT_FRACTAL_NZ) | ||
| 250 | + self.assertEqual(out.shape, (N, 1)) | ||
| 251 | + self.assertEqual(out.storage()[1], t_view.storage()[1]) | ||
| 252 | + | ||
| 231 | 253 | ||
| 232 | class TestNpuFormatCastDtypeParam(TestCase): | 254 | class TestNpuFormatCastDtypeParam(TestCase): |
| 233 | """ | 255 | """ |
| @@ -123,6 +123,18 @@ std::tuple<bool, int64_t, c10::SmallVector<int64_t, SIZE>> MaybeUseAclnnNpuForma | |||
| 123 | if (!FormatHelper::IsBaseFormatType(src) && !src.is_contiguous()) { | 123 | if (!FormatHelper::IsBaseFormatType(src) && !src.is_contiguous()) { |
| 124 | return std::make_tuple(false, dstFormat, outputShape); | 124 | return std::make_tuple(false, dstFormat, outputShape); |
| 125 | } | 125 | } |
| 126 | + // Guard: when casting a 2D contiguous tensor from ND to FRACTAL_NZ, if the | ||
| 127 | + // tensor's current shape differs from its storage shape (base_sizes_), it means | ||
| 128 | + // it is a view (e.g. transpose). Downstream ops may encounter precision | ||
| 129 | + // issues, so the aclnn path is not supported for this case. | ||
| 130 | + // Fall back to the aclop path to stay safe. | ||
| 131 | + // Example: storage [1, N] viewed as [N, 1] (Nx1 column vector). | ||
| 132 | + auto src_desc = torch_npu::NPUBridge::GetNpuStorageImpl(src)->npu_desc_; | ||
| 133 | + if (src_desc.npu_format_ == ACL_FORMAT_ND && acl_format == ACL_FORMAT_FRACTAL_NZ && | ||
| 134 | + src.is_contiguous() && src.sizes().size() == 2 && src_desc.base_sizes_.size() == 2 && src.sizes()[1] == 1 && | ||
| 135 | + (src_desc.base_sizes_[0] != src.sizes()[0] || src_desc.base_sizes_[1] != src.sizes()[1])) { | ||
| 136 | + return std::make_tuple(false, dstFormat, outputShape); | ||
| 137 | + } | ||
| 126 | if (IsAclnnFormatCastSupported() && aclnnNpuFormatCastExist) { | 138 | if (IsAclnnFormatCastSupported() && aclnnNpuFormatCastExist) { |
| 127 | auto acl_src = ConvertType(srcWrapper); | 139 | auto acl_src = ConvertType(srcWrapper); |
| 128 | auto api_ret = GetFormat(acl_src, acl_format, customizeAcltype, &dstStorageShape, | 140 | auto api_ret = GetFormat(acl_src, acl_format, customizeAcltype, &dstStorageShape, |