已合并
test(futures): add test case patch for futures #33279
dinglaiping创建于 4月8日
test(futures): add test case patch for futures #33279
已合并
从已删除 :add-testcasepatch-for-futures-2.10.0合入到Ascend/pytorchv2.10.0
共 3 个文件变更+239-0
| @@ -0,0 +1,66 @@ | |||
| 1 | +#!/bin/bash | ||
| 2 | + | ||
| 3 | +# ====================== 【重要】配置项 ====================== | ||
| 4 | +# 源码根目录相对于patch目录的路径 对于我们的项目来说是根目录相对于test_upstream的路径,也就是.. | ||
| 5 | +RELATIVE_TO_ROOT=.. | ||
| 6 | +# ========================================================== | ||
| 7 | + | ||
| 8 | +# 自动获取当前脚本所在目录 | ||
| 9 | +SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd) | ||
| 10 | +# 自动计算源码根目录 | ||
| 11 | +ROOT_DIR=$(cd "$SCRIPT_DIR/$RELATIVE_TO_ROOT" &>/dev/null && pwd) | ||
| 12 | +PATCH_DIR="$SCRIPT_DIR" | ||
| 13 | + | ||
| 14 | +# 检查根目录是否合法 | ||
| 15 | +if [ ! -d "$ROOT_DIR" ]; then | ||
| 16 | + echo "错误:无法定位源码根目录!" | ||
| 17 | + echo "请检查 RELATIVE_TO_ROOT 配置" | ||
| 18 | + exit 1 | ||
| 19 | +fi | ||
| 20 | + | ||
| 21 | +echo "================================================" | ||
| 22 | +echo " 自动批量应用 patch" | ||
| 23 | +echo "================================================" | ||
| 24 | +echo "源码根目录:$ROOT_DIR" | ||
| 25 | +echo "Patch 目录:$PATCH_DIR" | ||
| 26 | +echo "================================================" | ||
| 27 | + | ||
| 28 | +# 进入源码根目录 | ||
| 29 | +cd "$ROOT_DIR" || exit 1 | ||
| 30 | + | ||
| 31 | +# 递归查找所有 patch 文件并排序 | ||
| 32 | +PATCH_FILES=$(find "$PATCH_DIR" -type f \( -name "*.patch" -o -name "*.diff" \) | sort) | ||
| 33 | + | ||
| 34 | +if [ -z "$PATCH_FILES" ]; then | ||
| 35 | + echo "未找到任何 .patch / .diff 文件" | ||
| 36 | + exit 0 | ||
| 37 | +fi | ||
| 38 | + | ||
| 39 | +count=0 | ||
| 40 | +success=0 | ||
| 41 | +fail=0 | ||
| 42 | + | ||
| 43 | +# 逐个应用 | ||
| 44 | +for patch in $PATCH_FILES; do | ||
| 45 | + count=$((count+1)) | ||
| 46 | + echo -e "\n[$count] 应用:$patch" | ||
| 47 | + | ||
| 48 | + patch -p1 --no-backup-if-mismatch -f < "$patch" | ||
| 49 | + | ||
| 50 | + if [ $? -eq 0 ]; then | ||
| 51 | + echo "成功" | ||
| 52 | + success=$((success+1)) | ||
| 53 | + else | ||
| 54 | + echo "失败!停止执行" | ||
| 55 | + fail=$((fail+1)) | ||
| 56 | + exit 1 | ||
| 57 | + fi | ||
| 58 | +done | ||
| 59 | + | ||
| 60 | +echo -e "\n================================================" | ||
| 61 | +echo " 全部完成" | ||
| 62 | +echo "================================================" | ||
| 63 | +echo "总计:$count 个" | ||
| 64 | +echo "成功:$success 个" | ||
| 65 | +echo "失败:$fail 个" | ||
| 66 | +echo "================================================" | ||
| @@ -0,0 +1,42 @@ | |||
| 1 | +# Patch 批量应用脚本使用说明 | ||
| 2 | + | ||
| 3 | +## 目录结构 | ||
| 4 | + | ||
| 5 | +1. 核心仓库地址 | ||
| 6 | + | ||
| 7 | +- 官方 PyTorch 仓库(v2.10.0 版本):https://github.com/pytorch/pytorch/tree/v2.10.0,需拉取该仓库并切换至 tags/v2.10.0 标签。 | ||
| 8 | +- 补丁仓库(Ascend/pytorch):https://gitcode.com/Ascend/pytorch,仅需提取该仓库中的 patch 目录。 | ||
| 9 | + | ||
| 10 | +2. 核心目录结构 | ||
| 11 | + | ||
| 12 | +```coldFusion | ||
| 13 | + pytorch/ # PyTorch 源码根目录 | ||
| 14 | + ├─ ...(其他 PyTorch 原生文件/目录) | ||
| 15 | + └─ test_upstream/ # 补丁目录 | ||
| 16 | + ├─ apply_patches.sh # 批量应用脚本 | ||
| 17 | + ├─ *.patch # 补丁文件(支持子目录嵌套) | ||
| 18 | + ├─ ...(其他补丁子目录) | ||
| 19 | +``` | ||
| 20 | + | ||
| 21 | +## 环境要求 | ||
| 22 | + | ||
| 23 | +仅需安装git即可 | ||
| 24 | + | ||
| 25 | +## 使用方法 | ||
| 26 | + | ||
| 27 | +1. 将本仓库的test_upstream文件夹整体复制到本地的PyTorch官方仓库中 | ||
| 28 | + | ||
| 29 | +2. 运行脚本文件 | ||
| 30 | + | ||
| 31 | +```bash | ||
| 32 | +cd test_upstream | ||
| 33 | +./apply_patches.sh | ||
| 34 | +``` | ||
| 35 | + | ||
| 36 | +脚本执行说明:自动定位 PyTorch 根目录,递归扫描所有 .patch文件,按文件名排序强制应用,冲突部分生成 .rej 文件. | ||
| 37 | + | ||
| 38 | +## 注意事项 | ||
| 39 | + | ||
| 40 | +- 所有补丁仅适配 PyTorch tags/v2.10.0,其他版本可能导致应用失败,务必提前校验版本。 | ||
| 41 | +- test_upstream 目录需整体复制至 PyTorch 根目录。 | ||
| 42 | +- 生成 .rej 冲突文件时,需手动解决冲突后重新执行脚本。 | ||
| @@ -0,0 +1,131 @@ | |||
| 1 | +diff --git a/test/test_futures.py b/test/test_futures.py | ||
| 2 | +index dd1e79f..caf52f9 100644 | ||
| 3 | +--- a/test/test_futures.py | ||
| 4 | ++++ b/test/test_futures.py | ||
| 5 | + import unittest | ||
| 6 | + from torch.futures import Future | ||
| 7 | + from torch.testing._internal.common_utils import IS_WINDOWS, TestCase, TemporaryFileName, run_tests | ||
| 8 | + from typing import TypeVar | ||
| 9 | ++import torch_npu | ||
| 10 | + | ||
| 11 | + T = TypeVar("T") | ||
| 12 | + | ||
| 13 | + class TestFuture(TestCase): | ||
| 14 | + f = Future[torch.Tensor]() | ||
| 15 | + self.assertFalse(f.done()) | ||
| 16 | + | ||
| 17 | +- f.set_result(torch.ones(2, 2)) | ||
| 18 | ++ f.set_result(torch.ones(2, 2).npu()) | ||
| 19 | + self.assertTrue(f.done()) | ||
| 20 | + | ||
| 21 | + def test_done_exception(self) -> None: | ||
| 22 | + class TestFuture(TestCase): | ||
| 23 | + | ||
| 24 | + f1 = Future[torch.Tensor]() | ||
| 25 | + self.assertFalse(f1.done()) | ||
| 26 | +- f1.set_result(torch.ones(2, 2)) | ||
| 27 | ++ f1.set_result(torch.ones(2, 2).npu()) | ||
| 28 | + self.assertTrue(f1.done()) | ||
| 29 | + | ||
| 30 | + f2 = f1.then(raise_exception) | ||
| 31 | + class TestFuture(TestCase): | ||
| 32 | + | ||
| 33 | + def test_wait(self) -> None: | ||
| 34 | + f = Future[torch.Tensor]() | ||
| 35 | +- f.set_result(torch.ones(2, 2)) | ||
| 36 | ++ f.set_result(torch.ones(2, 2).npu()) | ||
| 37 | + | ||
| 38 | +- self.assertEqual(f.wait(), torch.ones(2, 2)) | ||
| 39 | ++ self.assertEqual(f.wait(), torch.ones(2, 2).npu()) | ||
| 40 | + | ||
| 41 | + def test_wait_multi_thread(self) -> None: | ||
| 42 | + | ||
| 43 | + class TestFuture(TestCase): | ||
| 44 | + | ||
| 45 | + f = Future[torch.Tensor]() | ||
| 46 | + | ||
| 47 | +- t = threading.Thread(target=slow_set_future, args=(f, torch.ones(2, 2))) | ||
| 48 | ++ t = threading.Thread(target=slow_set_future, args=(f, torch.ones(2, 2).npu())) | ||
| 49 | + t.start() | ||
| 50 | + | ||
| 51 | +- self.assertEqual(f.wait(), torch.ones(2, 2)) | ||
| 52 | ++ self.assertEqual(f.wait(), torch.ones(2, 2).npu()) | ||
| 53 | + t.join() | ||
| 54 | + | ||
| 55 | + def test_mark_future_twice(self) -> None: | ||
| 56 | + class TestFuture(TestCase): | ||
| 57 | + fut = Future[torch.Tensor]() | ||
| 58 | + then_fut = fut.then(lambda x: x.wait() + 1) | ||
| 59 | + | ||
| 60 | +- fut.set_result(torch.ones(2, 2)) | ||
| 61 | +- self.assertEqual(fut.wait(), torch.ones(2, 2)) | ||
| 62 | +- self.assertEqual(then_fut.wait(), torch.ones(2, 2) + 1) | ||
| 63 | ++ fut.set_result(torch.ones(2, 2).npu()) | ||
| 64 | ++ self.assertEqual(fut.wait(), torch.ones(2, 2).npu()) | ||
| 65 | ++ self.assertEqual(then_fut.wait(), torch.ones(2, 2).npu() + 1) | ||
| 66 | + | ||
| 67 | + def test_chained_then(self): | ||
| 68 | + fut = Future[torch.Tensor]() | ||
| 69 | + class TestFuture(TestCase): | ||
| 70 | + last_fut = last_fut.then(add_one) | ||
| 71 | + futs.append(last_fut) | ||
| 72 | + | ||
| 73 | +- fut.set_result(torch.ones(2, 2)) | ||
| 74 | ++ fut.set_result(torch.ones(2, 2).npu()) | ||
| 75 | + | ||
| 76 | + for i in range(len(futs)): | ||
| 77 | +- self.assertEqual(futs[i].wait(), torch.ones(2, 2) + i + 1) | ||
| 78 | ++ self.assertEqual(futs[i].wait(), torch.ones(2, 2).npu() + i + 1) | ||
| 79 | + | ||
| 80 | + def _test_then_error(self, cb, errMsg): | ||
| 81 | + fut = Future[int]() | ||
| 82 | + class TestFuture(TestCase): | ||
| 83 | + fut.add_done_callback(callback) | ||
| 84 | + | ||
| 85 | + self.assertFalse(callback_result) | ||
| 86 | +- fut.set_result(torch.ones(2, 2)) | ||
| 87 | +- self.assertEqual(fut.wait(), torch.ones(2, 2)) | ||
| 88 | ++ fut.set_result(torch.ones(2, 2).npu()) | ||
| 89 | ++ self.assertEqual(fut.wait(), torch.ones(2, 2).npu()) | ||
| 90 | + self.assertTrue(callback_result) | ||
| 91 | + | ||
| 92 | + def test_add_done_callback_maintains_callback_order(self): | ||
| 93 | + class TestFuture(TestCase): | ||
| 94 | + fut.add_done_callback(callback_set1) | ||
| 95 | + fut.add_done_callback(callback_set2) | ||
| 96 | + | ||
| 97 | +- fut.set_result(torch.ones(2, 2)) | ||
| 98 | +- self.assertEqual(fut.wait(), torch.ones(2, 2)) | ||
| 99 | ++ fut.set_result(torch.ones(2, 2).npu()) | ||
| 100 | ++ self.assertEqual(fut.wait(), torch.ones(2, 2).npu()) | ||
| 101 | + # set2 called last, callback_result = 2 | ||
| 102 | + self.assertEqual(callback_result, 2) | ||
| 103 | + | ||
| 104 | + class TestFuture(TestCase): | ||
| 105 | + then_fut = fut.then(callback_then) | ||
| 106 | + fut.add_done_callback(callback_set2) | ||
| 107 | + | ||
| 108 | +- fut.set_result(torch.ones(2, 2)) | ||
| 109 | +- self.assertEqual(fut.wait(), torch.ones(2, 2)) | ||
| 110 | ++ fut.set_result(torch.ones(2, 2).npu()) | ||
| 111 | ++ self.assertEqual(fut.wait(), torch.ones(2, 2).npu()) | ||
| 112 | + # then_fut's callback is called with callback_result = 1 | ||
| 113 | +- self.assertEqual(then_fut.wait(), torch.ones(2, 2) + 1) | ||
| 114 | ++ self.assertEqual(then_fut.wait(), torch.ones(2, 2).npu() + 1) | ||
| 115 | + # set2 called last, callback_result = 2 | ||
| 116 | + self.assertEqual(callback_result, 2) | ||
| 117 | + | ||
| 118 | + class TestFuture(TestCase): | ||
| 119 | + fut = Future[torch.Tensor]() | ||
| 120 | + then_fut = fut.then(raise_value_error) | ||
| 121 | + fut.add_done_callback(raise_value_error) | ||
| 122 | +- fut.set_result(torch.ones(2, 2)) | ||
| 123 | ++ fut.set_result(torch.ones(2, 2).npu()) | ||
| 124 | + | ||
| 125 | + # error from add_done_callback's callback is swallowed | ||
| 126 | + # error from then's callback is not | ||
| 127 | +- self.assertEqual(fut.wait(), torch.ones(2, 2)) | ||
| 128 | ++ self.assertEqual(fut.wait(), torch.ones(2, 2).npu()) | ||
| 129 | + with self.assertRaisesRegex(RuntimeError, "Expected error"): | ||
| 130 | + then_fut.wait() | ||
| 131 | + | ||