已合并
fix: lintrunner --all-files --take NEWLINE -a #35871
Jingwei Huang创建于 5月17日
fix: lintrunner --all-files --take NEWLINE -a #35871
已合并
共 89 个文件变更+16495-16512
| @@ -1,397 +1,396 @@ | |||
| 1 | cmake_minimum_required(VERSION 3.18 FATAL_ERROR) | 1 | cmake_minimum_required(VERSION 3.18 FATAL_ERROR) |
| 2 | 2 | ||
| 3 | find_program(CCACHE ccache) | 3 | find_program(CCACHE ccache) |
| 4 | if(${CCACHE} STREQUAL "CCACHE-NOTFOUND") | 4 | if(${CCACHE} STREQUAL "CCACHE-NOTFOUND") |
| 5 | message(STATUS "Compile without ccache") | 5 | message(STATUS "Compile without ccache") |
| 6 | else() | 6 | else() |
| 7 | set(CMAKE_C_COMPILER_LAUNCHER ${CCACHE} CACHE PATH "cache Compiler") | 7 | set(CMAKE_C_COMPILER_LAUNCHER ${CCACHE} CACHE PATH "cache Compiler") |
| 8 | set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE} CACHE PATH "cache Compiler") | 8 | set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE} CACHE PATH "cache Compiler") |
| 9 | message(STATUS "CMAKE_C_COMPILER_LAUNCHER:${CMAKE_C_COMPILER_LAUNCHER}") | 9 | message(STATUS "CMAKE_C_COMPILER_LAUNCHER:${CMAKE_C_COMPILER_LAUNCHER}") |
| 10 | message(STATUS "CMAKE_CXX_COMPILER_LAUNCHER:${CMAKE_CXX_COMPILER_LAUNCHER}") | 10 | message(STATUS "CMAKE_CXX_COMPILER_LAUNCHER:${CMAKE_CXX_COMPILER_LAUNCHER}") |
| 11 | endif() | 11 | endif() |
| 12 | 12 | ||
| 13 | project(TORCHNPU CXX C) | 13 | project(TORCHNPU CXX C) |
| 14 | add_compile_options(-fmacro-prefix-map=${CMAKE_SOURCE_DIR}/=) | 14 | add_compile_options(-fmacro-prefix-map=${CMAKE_SOURCE_DIR}/=) |
| 15 | 15 | ||
| 16 | # GCC 11.2 (used in v2.7.1) does not support -fuse-ld=mold | 16 | # GCC 11.2 (used in v2.7.1) does not support -fuse-ld=mold |
| 17 | find_program(MOLD_LINKER mold) | 17 | find_program(MOLD_LINKER mold) |
| 18 | if(MOLD_LINKER) | 18 | if(MOLD_LINKER) |
| 19 | set(MOLD_WRAPPER_DIR "${CMAKE_BINARY_DIR}/mold-wrapper") | 19 | set(MOLD_WRAPPER_DIR "${CMAKE_BINARY_DIR}/mold-wrapper") |
| 20 | file(MAKE_DIRECTORY "${MOLD_WRAPPER_DIR}") | 20 | file(MAKE_DIRECTORY "${MOLD_WRAPPER_DIR}") |
| 21 | if(NOT EXISTS "${MOLD_WRAPPER_DIR}/ld") | 21 | if(NOT EXISTS "${MOLD_WRAPPER_DIR}/ld") |
| 22 | file(CREATE_LINK "${MOLD_LINKER}" "${MOLD_WRAPPER_DIR}/ld" SYMBOLIC) | 22 | file(CREATE_LINK "${MOLD_LINKER}" "${MOLD_WRAPPER_DIR}/ld" SYMBOLIC) |
| 23 | endif() | 23 | endif() |
| 24 | add_link_options("-B${MOLD_WRAPPER_DIR}") | 24 | add_link_options("-B${MOLD_WRAPPER_DIR}") |
| 25 | message(STATUS "Using mold linker via -B wrapper: ${MOLD_LINKER}") | 25 | message(STATUS "Using mold linker via -B wrapper: ${MOLD_LINKER}") |
| 26 | else() | 26 | else() |
| 27 | message(STATUS "mold linker not found, using default linker") | 27 | message(STATUS "mold linker not found, using default linker") |
| 28 | endif() | 28 | endif() |
| 29 | 29 | ||
| 30 | set(LINUX TRUE) | 30 | set(LINUX TRUE) |
| 31 | set(CMAKE_INSTALL_MESSAGE NEVER) | 31 | set(CMAKE_INSTALL_MESSAGE NEVER) |
| 32 | # set(CMAKE_VERBOSE_MAKEFILE ON) | 32 | # set(CMAKE_VERBOSE_MAKEFILE ON) |
| 33 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) | 33 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) |
| 34 | 34 | ||
| 35 | if(DEFINED TORCH_VERSION) | 35 | if(DEFINED TORCH_VERSION) |
| 36 | add_definitions(-DPYTORCH_NPU_VERSION="${TORCH_VERSION}") | 36 | add_definitions(-DPYTORCH_NPU_VERSION="${TORCH_VERSION}") |
| 37 | endif() | 37 | endif() |
| 38 | 38 | ||
| 39 | set(PLUGIN_NAME torch_npu) | 39 | set(PLUGIN_NAME torch_npu) |
| 40 | 40 | ||
| 41 | set(RPATH_VALUE $ORIGIN) | 41 | set(RPATH_VALUE $ORIGIN) |
| 42 | set(CMAKE_SKIP_BUILD_RPATH FALSE) | 42 | set(CMAKE_SKIP_BUILD_RPATH FALSE) |
| 43 | set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) | 43 | set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) |
| 44 | set(CMAKE_INSTALL_RPATH "${RPATH_VALUE}/lib/:${RPATH_VALUE}/") | 44 | set(CMAKE_INSTALL_RPATH "${RPATH_VALUE}/lib/:${RPATH_VALUE}/") |
| 45 | set(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE) | 45 | set(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE) |
| 46 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${TORCHNPU_INSTALL_LIBDIR}) | 46 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${TORCHNPU_INSTALL_LIBDIR}) |
| 47 | SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g") | 47 | SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g") |
| 48 | SET(CMAKE_CXX_FLAGS_RELEASE "-O2") | 48 | SET(CMAKE_CXX_FLAGS_RELEASE "-O2") |
| 49 | SET(CMAKE_CXX_FLAGS_DEBUG "-O0 -g") | 49 | SET(CMAKE_CXX_FLAGS_DEBUG "-O0 -g") |
| 50 | 50 | ||
| 51 | option(USE_NPU "Use NPU" ON) | 51 | option(USE_NPU "Use NPU" ON) |
| 52 | 52 | ||
| 53 | # LTO&PGO optimization in compile option | 53 | # LTO&PGO optimization in compile option |
| 54 | SET(IF_APPEND FALSE) | 54 | SET(IF_APPEND FALSE) |
| 55 | if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") | 55 | if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") |
| 56 | if(MOLD_LINKER) | 56 | if(MOLD_LINKER) |
| 57 | SET(APPEND_FLAGS "-fGNU-compatibility -Wno-non-pod-varargs") | 57 | SET(APPEND_FLAGS "-fGNU-compatibility -Wno-non-pod-varargs") |
| 58 | else() | 58 | else() |
| 59 | SET(APPEND_FLAGS "-fGNU-compatibility -fuse-ld=lld -Wno-non-pod-varargs") | 59 | SET(APPEND_FLAGS "-fGNU-compatibility -fuse-ld=lld -Wno-non-pod-varargs") |
| 60 | endif() | 60 | endif() |
| 61 | if (DEFINED ENABLE_LTO) | 61 | if (DEFINED ENABLE_LTO) |
| 62 | SET(IF_APPEND TRUE) | 62 | SET(IF_APPEND TRUE) |
| 63 | SET(APPEND_FLAGS "${APPEND_FLAGS} -flto=thin") | 63 | SET(APPEND_FLAGS "${APPEND_FLAGS} -flto=thin") |
| 64 | endif() | 64 | endif() |
| 65 | if (DEFINED PGO_MODE) | 65 | if (DEFINED PGO_MODE) |
| 66 | SET(IF_APPEND TRUE) | 66 | SET(IF_APPEND TRUE) |
| 67 | if (PGO_MODE EQUAL 1) | 67 | if (PGO_MODE EQUAL 1) |
| 68 | SET(APPEND_FLAGS "${APPEND_FLAGS} -fprofile-generate") | 68 | SET(APPEND_FLAGS "${APPEND_FLAGS} -fprofile-generate") |
| 69 | elseif (PGO_MODE EQUAL 2) | 69 | elseif (PGO_MODE EQUAL 2) |
| 70 | SET(APPEND_FLAGS "${APPEND_FLAGS} -fprofile-use=${CMAKE_CURRENT_SOURCE_DIR}/default.profdata") | 70 | SET(APPEND_FLAGS "${APPEND_FLAGS} -fprofile-use=${CMAKE_CURRENT_SOURCE_DIR}/default.profdata") |
| 71 | endif() | 71 | endif() |
| 72 | endif() | 72 | endif() |
| 73 | else() | 73 | else() |
| 74 | if (DEFINED ENABLE_LTO) | 74 | if (DEFINED ENABLE_LTO) |
| 75 | message(FATAL_ERROR "Currently, LTO auto build is not supported in ${CMAKE_CXX_COMPILER_ID}") | 75 | message(FATAL_ERROR "Currently, LTO auto build is not supported in ${CMAKE_CXX_COMPILER_ID}") |
| 76 | endif() | 76 | endif() |
| 77 | if (DEFINED PGO_MODE) | 77 | if (DEFINED PGO_MODE) |
| 78 | message(FATAL_ERROR "Currently, PGO auto build is not supported in ${CMAKE_CXX_COMPILER_ID}") | 78 | message(FATAL_ERROR "Currently, PGO auto build is not supported in ${CMAKE_CXX_COMPILER_ID}") |
| 79 | endif() | 79 | endif() |
| 80 | endif() | 80 | endif() |
| 81 | if (IF_APPEND) | 81 | if (IF_APPEND) |
| 82 | SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${APPEND_FLAGS}") | 82 | SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${APPEND_FLAGS}") |
| 83 | endif() | 83 | endif() |
| 84 | 84 | ||
| 85 | # check and set CMAKE_CXX_STANDARD | 85 | # check and set CMAKE_CXX_STANDARD |
| 86 | string(FIND "${CMAKE_CXX_FLAGS}" "-std=c++" env_cxx_standard) | 86 | string(FIND "${CMAKE_CXX_FLAGS}" "-std=c++" env_cxx_standard) |
| 87 | if(env_cxx_standard GREATER -1) | 87 | if(env_cxx_standard GREATER -1) |
| 88 | message( | 88 | message( |
| 89 | WARNING "C++ standard version definition detected in environment variable." | 89 | WARNING "C++ standard version definition detected in environment variable." |
| 90 | "PyTorch requires -std=c++17. Please remove -std=c++ settings in your environment.") | 90 | "PyTorch requires -std=c++17. Please remove -std=c++ settings in your environment.") |
| 91 | endif() | 91 | endif() |
| 92 | set(CMAKE_CXX_STANDARD 17) | 92 | set(CMAKE_CXX_STANDARD 17) |
| 93 | set(CMAKE_C_STANDARD 11) | 93 | set(CMAKE_C_STANDARD 11) |
| 94 | set(CMAKE_CXX_EXTENSIONS OFF) | 94 | set(CMAKE_CXX_EXTENSIONS OFF) |
| 95 | 95 | ||
| 96 | set(TORCHNPU_ROOT "${PROJECT_SOURCE_DIR}/torch_npu/csrc") | 96 | set(TORCHNPU_ROOT "${PROJECT_SOURCE_DIR}/torch_npu/csrc") |
| 97 | set(TORCHNPU_THIRD_PARTY_ROOT "${PROJECT_SOURCE_DIR}/third_party") | 97 | set(TORCHNPU_THIRD_PARTY_ROOT "${PROJECT_SOURCE_DIR}/third_party") |
| 98 | 98 | ||
| 99 | set(Torch_DIR ${PYTORCH_INSTALL_DIR}/share/cmake/Torch) | 99 | set(Torch_DIR ${PYTORCH_INSTALL_DIR}/share/cmake/Torch) |
| 100 | FIND_PACKAGE(Torch REQUIRED) | 100 | FIND_PACKAGE(Torch REQUIRED) |
| 101 | 101 | ||
| 102 | set(LINUX TRUE) | 102 | set(LINUX TRUE) |
| 103 | set(CMAKE_INSTALL_MESSAGE NEVER) | 103 | set(CMAKE_INSTALL_MESSAGE NEVER) |
| 104 | #set(CMAKE_VERBOSE_MAKEFILE ON) | 104 | #set(CMAKE_VERBOSE_MAKEFILE ON) |
| 105 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) | 105 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) |
| 106 | 106 | ||
| 107 | # Define build type | 107 | # Define build type |
| 108 | IF(CMAKE_BUILD_TYPE MATCHES Debug) | 108 | IF(CMAKE_BUILD_TYPE MATCHES Debug) |
| 109 | message("Debug build.") | 109 | message("Debug build.") |
| 110 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_DEBUG") | 110 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_DEBUG") |
| 111 | ELSEIF(CMAKE_BUILD_TYPE MATCHES RelWithDebInfo) | 111 | ELSEIF(CMAKE_BUILD_TYPE MATCHES RelWithDebInfo) |
| 112 | message("RelWithDebInfo build") | 112 | message("RelWithDebInfo build") |
| 113 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DNDEBUG") | 113 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DNDEBUG") |
| 114 | ELSE() | 114 | ELSE() |
| 115 | message("Release build.") | 115 | message("Release build.") |
| 116 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DNDEBUG") | 116 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DNDEBUG") |
| 117 | ENDIF() | 117 | ENDIF() |
| 118 | 118 | ||
| 119 | if(USE_NPU) | 119 | if(USE_NPU) |
| 120 | string(APPEND CMAKE_CXX_FLAGS " -DUSE_NPU") | 120 | string(APPEND CMAKE_CXX_FLAGS " -DUSE_NPU") |
| 121 | endif() | 121 | endif() |
| 122 | 122 | ||
| 123 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") | 123 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") |
| 124 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-narrowing") | 124 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-narrowing") |
| 125 | # Eigen fails to build with some versions, so convert this to a warning | 125 | # Eigen fails to build with some versions, so convert this to a warning |
| 126 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") | 126 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") |
| 127 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wextra") | 127 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wextra") |
| 128 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-missing-field-initializers") | 128 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-missing-field-initializers") |
| 129 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-type-limits") | 129 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-type-limits") |
| 130 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-array-bounds") | 130 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-array-bounds") |
| 131 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unknown-pragmas") | 131 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unknown-pragmas") |
| 132 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-sign-compare") | 132 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-sign-compare") |
| 133 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-parameter") | 133 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-parameter") |
| 134 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-variable") | 134 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-variable") |
| 135 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-function") | 135 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-function") |
| 136 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-result") | 136 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-result") |
| 137 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-strict-overflow") | 137 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-strict-overflow") |
| 138 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-strict-aliasing") | 138 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-strict-aliasing") |
| 139 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-error=deprecated-declarations") | 139 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-error=deprecated-declarations") |
| 140 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-ignored-qualifiers") | 140 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-ignored-qualifiers") |
| 141 | if (CMAKE_COMPILER_IS_GNUCXX AND NOT (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.0.0)) | 141 | if (CMAKE_COMPILER_IS_GNUCXX AND NOT (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.0.0)) |
| 142 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-stringop-overflow") | 142 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-stringop-overflow") |
| 143 | endif() | 143 | endif() |
| 144 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-error=pedantic") | 144 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-error=pedantic") |
| 145 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-error=redundant-decls") | 145 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-error=redundant-decls") |
| 146 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-error=old-style-cast") | 146 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-error=old-style-cast") |
| 147 | 147 | ||
| 148 | # These flags are not available in GCC-4.8.5. Set only when using clang. | 148 | # These flags are not available in GCC-4.8.5. Set only when using clang. |
| 149 | if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") | 149 | if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") |
| 150 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-invalid-partial-specialization") | 150 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-invalid-partial-specialization") |
| 151 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-typedef-redefinition") | 151 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-typedef-redefinition") |
| 152 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unknown-warning-option") | 152 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unknown-warning-option") |
| 153 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-private-field") | 153 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-private-field") |
| 154 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-inconsistent-missing-override") | 154 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-inconsistent-missing-override") |
| 155 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-aligned-allocation-unavailable") | 155 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-aligned-allocation-unavailable") |
| 156 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-c++17-extensions") | 156 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-c++17-extensions") |
| 157 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-constexpr-not-const") | 157 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-constexpr-not-const") |
| 158 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-missing-braces") | 158 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-missing-braces") |
| 159 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Qunused-arguments") | 159 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Qunused-arguments") |
| 160 | if (${COLORIZE_OUTPUT}) | 160 | if (${COLORIZE_OUTPUT}) |
| 161 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcolor-diagnostics") | 161 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcolor-diagnostics") |
| 162 | endif() | 162 | endif() |
| 163 | endif() | 163 | endif() |
| 164 | if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.9) | 164 | if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.9) |
| 165 | if (${COLORIZE_OUTPUT}) | 165 | if (${COLORIZE_OUTPUT}) |
| 166 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdiagnostics-color=always") | 166 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdiagnostics-color=always") |
| 167 | endif() | 167 | endif() |
| 168 | endif() | 168 | endif() |
| 169 | if ((APPLE AND (NOT ("${CLANG_VERSION_STRING}" VERSION_LESS "9.0"))) | 169 | if ((APPLE AND (NOT ("${CLANG_VERSION_STRING}" VERSION_LESS "9.0"))) |
| 170 | OR (CMAKE_COMPILER_IS_GNUCXX | 170 | OR (CMAKE_COMPILER_IS_GNUCXX |
| 171 | AND (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 7.0 AND NOT APPLE))) | 171 | AND (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 7.0 AND NOT APPLE))) |
| 172 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -faligned-new") | 172 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -faligned-new") |
| 173 | endif() | 173 | endif() |
| 174 | if (WERROR) | 174 | if (WERROR) |
| 175 | check_cxx_compiler_flag("-Werror" COMPILER_SUPPORT_WERROR) | 175 | check_cxx_compiler_flag("-Werror" COMPILER_SUPPORT_WERROR) |
| 176 | if (NOT COMPILER_SUPPORT_WERROR) | 176 | if (NOT COMPILER_SUPPORT_WERROR) |
| 177 | set(WERROR FALSE) | 177 | set(WERROR FALSE) |
| 178 | else() | 178 | else() |
| 179 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror") | 179 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror") |
| 180 | endif() | 180 | endif() |
| 181 | endif(WERROR) | 181 | endif(WERROR) |
| 182 | if (NOT APPLE) | 182 | if (NOT APPLE) |
| 183 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-but-set-variable") | 183 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-but-set-variable") |
| 184 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-uninitialized") | 184 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-uninitialized") |
| 185 | endif() | 185 | endif() |
| 186 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -O0") | 186 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -O0") |
| 187 | set(CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_STATIC_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -O0") | 187 | set(CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_STATIC_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -O0") |
| 188 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-math-errno") | 188 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-math-errno") |
| 189 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-trapping-math") | 189 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-trapping-math") |
| 190 | set(CMAKE_CXX_COVERAGE $ENV{CALCULATE_CXX_COVERAGE}) | 190 | set(CMAKE_CXX_COVERAGE $ENV{CALCULATE_CXX_COVERAGE}) |
| 191 | 191 | ||
| 192 | if (CMAKE_BUILD_TYPE MATCHES Debug) | 192 | if (CMAKE_BUILD_TYPE MATCHES Debug) |
| 193 | set(CMAKE_C_FLAGS "-fstack-protector-all -Wl,-z,relro,-z,now,-z,noexecstack -fPIE -pie ${CMAKE_C_FLAGS}") | 193 | set(CMAKE_C_FLAGS "-fstack-protector-all -Wl,-z,relro,-z,now,-z,noexecstack -fPIE -pie ${CMAKE_C_FLAGS}") |
| 194 | set(CMAKE_CXX_FLAGS "-fstack-protector-all -Wl,-z,relro,-z,now,-z,noexecstack -fPIE -pie ${CMAKE_CXX_FLAGS}") | 194 | set(CMAKE_CXX_FLAGS "-fstack-protector-all -Wl,-z,relro,-z,now,-z,noexecstack -fPIE -pie ${CMAKE_CXX_FLAGS}") |
| 195 | set(CXXFLAGS "-fstack-protector-all -Wl,-z,relro,-z,now,-z,noexecstack -fPIE -pie ${CXXFLAGS}") | 195 | set(CXXFLAGS "-fstack-protector-all -Wl,-z,relro,-z,now,-z,noexecstack -fPIE -pie ${CXXFLAGS}") |
| 196 | elseif (CMAKE_CXX_COVERAGE STREQUAL "1") | 196 | elseif (CMAKE_CXX_COVERAGE STREQUAL "1") |
| 197 | set(CMAKE_C_FLAGS "-fstack-protector-all -Wl,-z,relro,-z,now,-z,noexecstack -fprofile-arcs -ftest-coverage -fPIE -pie ${CMAKE_C_FLAGS}") | 197 | set(CMAKE_C_FLAGS "-fstack-protector-all -Wl,-z,relro,-z,now,-z,noexecstack -fprofile-arcs -ftest-coverage -fPIE -pie ${CMAKE_C_FLAGS}") |
| 198 | set(CMAKE_CXX_FLAGS "-fstack-protector-all -Wl,-z,relro,-z,now,-z,noexecstack -fprofile-arcs -ftest-coverage -fPIE -pie ${CMAKE_CXX_FLAGS}") | 198 | set(CMAKE_CXX_FLAGS "-fstack-protector-all -Wl,-z,relro,-z,now,-z,noexecstack -fprofile-arcs -ftest-coverage -fPIE -pie ${CMAKE_CXX_FLAGS}") |
| 199 | set(CXXFLAGS "-fstack-protector-all -Wl,-z,relro,-z,now,-z,noexecstack -fprofile-arcs -ftest-coverage -fPIE -pie ${CXXFLAGS}") | 199 | set(CXXFLAGS "-fstack-protector-all -Wl,-z,relro,-z,now,-z,noexecstack -fprofile-arcs -ftest-coverage -fPIE -pie ${CXXFLAGS}") |
| 200 | else() | 200 | else() |
| 201 | set(CMAKE_C_FLAGS "-fstack-protector-all -Wl,-z,relro,-z,now,-z,noexecstack -fPIE -pie ${CMAKE_C_FLAGS}") | 201 | set(CMAKE_C_FLAGS "-fstack-protector-all -Wl,-z,relro,-z,now,-z,noexecstack -fPIE -pie ${CMAKE_C_FLAGS}") |
| 202 | set(CMAKE_CXX_FLAGS "-fstack-protector-all -Wl,-z,relro,-z,now,-z,noexecstack -fPIE -pie ${CMAKE_CXX_FLAGS}") | 202 | set(CMAKE_CXX_FLAGS "-fstack-protector-all -Wl,-z,relro,-z,now,-z,noexecstack -fPIE -pie ${CMAKE_CXX_FLAGS}") |
| 203 | set(CXXFLAGS "-fstack-protector-all -Wl,-z,relro,-z,now,-z,noexecstack -fPIE -pie ${CXXFLAGS}") | 203 | set(CXXFLAGS "-fstack-protector-all -Wl,-z,relro,-z,now,-z,noexecstack -fPIE -pie ${CXXFLAGS}") |
| 204 | endif() | 204 | endif() |
| 205 | 205 | ||
| 206 | if (NOT DEFINED GLIBCXX_USE_CXX11_ABI) | 206 | if (NOT DEFINED GLIBCXX_USE_CXX11_ABI) |
| 207 | set(GLIBCXX_USE_CXX11_ABI 0) | 207 | set(GLIBCXX_USE_CXX11_ABI 0) |
| 208 | endif() | 208 | endif() |
| 209 | message(STATUS "Determined _GLIBCXX_USE_CXX11_ABI=${GLIBCXX_USE_CXX11_ABI}") | 209 | message(STATUS "Determined _GLIBCXX_USE_CXX11_ABI=${GLIBCXX_USE_CXX11_ABI}") |
| 210 | set(_GLIBCXX_USE_CXX11_ABI ${GLIBCXX_USE_CXX11_ABI}) | 210 | set(_GLIBCXX_USE_CXX11_ABI ${GLIBCXX_USE_CXX11_ABI}) |
| 211 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_GLIBCXX_USE_CXX11_ABI=${GLIBCXX_USE_CXX11_ABI}") | 211 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_GLIBCXX_USE_CXX11_ABI=${GLIBCXX_USE_CXX11_ABI}") |
| 212 | if (${GLIBCXX_USE_CXX11_ABI} EQUAL 0) | 212 | if (${GLIBCXX_USE_CXX11_ABI} EQUAL 0) |
| 213 | set(CMAKE_CXX_FLAGS "-fabi-version=11 ${CMAKE_CXX_FLAGS}") | 213 | set(CMAKE_CXX_FLAGS "-fabi-version=11 ${CMAKE_CXX_FLAGS}") |
| 214 | else() | 214 | else() |
| 215 | set(CXX_STANDARD_REQUIRED ON) | 215 | set(CXX_STANDARD_REQUIRED ON) |
| 216 | if (DEFINED ABI_VERSION) | 216 | if (DEFINED ABI_VERSION) |
| 217 | set(CMAKE_CXX_FLAGS "-fabi-version=${ABI_VERSION} ${CMAKE_CXX_FLAGS}") | 217 | set(CMAKE_CXX_FLAGS "-fabi-version=${ABI_VERSION} ${CMAKE_CXX_FLAGS}") |
| 218 | endif() | 218 | endif() |
| 219 | endif() | 219 | endif() |
| 220 | 220 | ||
| 221 | 221 | ||
| 222 | if (DEFINED BUILD_LIBTORCH) | 222 | if (DEFINED BUILD_LIBTORCH) |
| 223 | add_compile_definitions(BUILD_LIBTORCH) | 223 | add_compile_definitions(BUILD_LIBTORCH) |
| 224 | endif() | 224 | endif() |
| 225 | 225 | ||
| 226 | 226 | ||
| 227 | include_directories(${PROJECT_SOURCE_DIR}) | 227 | include_directories(${PROJECT_SOURCE_DIR}) |
| 228 | include_directories(${PROJECT_SOURCE_DIR}/torch_npu/csrc/aten) | 228 | include_directories(${PROJECT_SOURCE_DIR}/torch_npu/csrc/aten) |
| 229 | include_directories(${PROJECT_SOURCE_DIR}/torch_npu/csrc/inductor) | 229 | include_directories(${PROJECT_SOURCE_DIR}/torch_npu/csrc/inductor) |
| 230 | include_directories(${PROJECT_SOURCE_DIR}/third_party/hccl/inc) | 230 | include_directories(${PROJECT_SOURCE_DIR}/third_party/hccl/inc) |
| 231 | include_directories(${PROJECT_SOURCE_DIR}/third_party/acl/inc) | 231 | include_directories(${PROJECT_SOURCE_DIR}/third_party/acl/inc) |
| 232 | include_directories(${PROJECT_SOURCE_DIR}/third_party/Tensorpipe) | 232 | include_directories(${PROJECT_SOURCE_DIR}/third_party/Tensorpipe) |
| 233 | include_directories(${PROJECT_SOURCE_DIR}/third_party/nlohmann/include) | 233 | include_directories(${PROJECT_SOURCE_DIR}/third_party/nlohmann/include) |
| 234 | 234 | ||
| 235 | # Set installed PyTorch dir | 235 | # Set installed PyTorch dir |
| 236 | if(DEFINED PYTORCH_INSTALL_DIR) | 236 | if(DEFINED PYTORCH_INSTALL_DIR) |
| 237 | include_directories(${PYTORCH_INSTALL_DIR}/include) | 237 | include_directories(${PYTORCH_INSTALL_DIR}/include) |
| 238 | include_directories(${PYTORCH_INSTALL_DIR}/include/torch/csrc/api/include) | 238 | include_directories(${PYTORCH_INSTALL_DIR}/include/torch/csrc/api/include) |
| 239 | include_directories(${PYTORCH_INSTALL_DIR}/include/torch/csrc/distributed) | 239 | include_directories(${PYTORCH_INSTALL_DIR}/include/torch/csrc/distributed) |
| 240 | else() | 240 | else() |
| 241 | message(FATAL_ERROR "Cannot find installed PyTorch directory") | 241 | message(FATAL_ERROR "Cannot find installed PyTorch directory") |
| 242 | endif() | 242 | endif() |
| 243 | 243 | ||
| 244 | # Set Python include dir | 244 | # Set Python include dir |
| 245 | if(DEFINED PYTHON_INCLUDE_DIR) | 245 | if(DEFINED PYTHON_INCLUDE_DIR) |
| 246 | include_directories(${PYTHON_INCLUDE_DIR}) | 246 | include_directories(${PYTHON_INCLUDE_DIR}) |
| 247 | else() | 247 | else() |
| 248 | message(FATAL_ERROR "Cannot find installed Python head file directory") | 248 | message(FATAL_ERROR "Cannot find installed Python head file directory") |
| 249 | endif() | 249 | endif() |
| 250 | 250 | ||
| 251 | # sources | 251 | # sources |
| 252 | set(ATEN_SRCS) | 252 | set(ATEN_SRCS) |
| 253 | set(CORE_SRCS) | 253 | set(CORE_SRCS) |
| 254 | set(FRAMEWORK_SRCS) | 254 | set(FRAMEWORK_SRCS) |
| 255 | set(LOGGING_SRCS) | 255 | set(LOGGING_SRCS) |
| 256 | set(INDUCTOR_SRCS) | 256 | set(INDUCTOR_SRCS) |
| 257 | set(DIST_SRCS) | 257 | set(DIST_SRCS) |
| 258 | 258 | ||
| 259 | if (NOT DEFINED BUILD_LIBTORCH) | 259 | if (NOT DEFINED BUILD_LIBTORCH) |
| 260 | set(FLOP_SRCS) | 260 | set(FLOP_SRCS) |
| 261 | set(NPU_SRCS) | 261 | set(NPU_SRCS) |
| 262 | set(PROF_SRCS) | 262 | set(PROF_SRCS) |
| 263 | set(IPC_SRCS) | 263 | set(IPC_SRCS) |
| 264 | set(UTILS_SRCS) | 264 | set(UTILS_SRCS) |
| 265 | set(SAN_SRCS) | 265 | set(SAN_SRCS) |
| 266 | set(AFD_SRCS) | 266 | set(AFD_SRCS) |
| 267 | endif() | 267 | endif() |
| 268 | 268 | ||
| 269 | if (DEFINED BUILD_LIBTORCH) | 269 | if (DEFINED BUILD_LIBTORCH) |
| 270 | set(NPU_CPP_LIBS_SRCS) | 270 | set(NPU_CPP_LIBS_SRCS) |
| 271 | endif() | 271 | endif() |
| 272 | 272 | ||
| 273 | add_subdirectory(${TORCHNPU_ROOT}/aten) | 273 | add_subdirectory(${TORCHNPU_ROOT}/aten) |
| 274 | add_subdirectory(${TORCHNPU_ROOT}/core) | 274 | add_subdirectory(${TORCHNPU_ROOT}/core) |
| 275 | add_subdirectory(${TORCHNPU_ROOT}/framework) | 275 | add_subdirectory(${TORCHNPU_ROOT}/framework) |
| 276 | add_subdirectory(${TORCHNPU_ROOT}/flopcount) | 276 | add_subdirectory(${TORCHNPU_ROOT}/flopcount) |
| 277 | add_subdirectory(${TORCHNPU_ROOT}/logging) | 277 | add_subdirectory(${TORCHNPU_ROOT}/logging) |
| 278 | add_subdirectory(${TORCHNPU_ROOT}/custom_dtype) | 278 | add_subdirectory(${TORCHNPU_ROOT}/custom_dtype) |
| 279 | add_subdirectory(${TORCHNPU_ROOT}/inductor) | 279 | add_subdirectory(${TORCHNPU_ROOT}/inductor) |
| 280 | add_subdirectory(${TORCHNPU_ROOT}/distributed) | 280 | add_subdirectory(${TORCHNPU_ROOT}/distributed) |
| 281 | 281 | ||
| 282 | if (NOT DEFINED BUILD_LIBTORCH) | 282 | if (NOT DEFINED BUILD_LIBTORCH) |
| 283 | add_subdirectory(${TORCHNPU_ROOT}/npu) | 283 | add_subdirectory(${TORCHNPU_ROOT}/npu) |
| 284 | add_subdirectory(${TORCHNPU_ROOT}/profiler) | 284 | add_subdirectory(${TORCHNPU_ROOT}/profiler) |
| 285 | add_subdirectory(${TORCHNPU_ROOT}/ipc) | 285 | add_subdirectory(${TORCHNPU_ROOT}/ipc) |
| 286 | add_subdirectory(${TORCHNPU_ROOT}/utils) | 286 | add_subdirectory(${TORCHNPU_ROOT}/utils) |
| 287 | add_subdirectory(${TORCHNPU_ROOT}/sanitizer) | 287 | add_subdirectory(${TORCHNPU_ROOT}/sanitizer) |
| 288 | add_subdirectory(${TORCHNPU_ROOT}/afd) | 288 | add_subdirectory(${TORCHNPU_ROOT}/afd) |
| 289 | endif() | 289 | endif() |
| 290 | 290 | ||
| 291 | if (DEFINED BUILD_LIBTORCH) | 291 | if (DEFINED BUILD_LIBTORCH) |
| 292 | add_subdirectory(${TORCHNPU_ROOT}/libs) | 292 | add_subdirectory(${TORCHNPU_ROOT}/libs) |
| 293 | endif() | 293 | endif() |
| 294 | 294 | ||
| 295 | set(OPS_PLUGIN_SRCS) | 295 | set(OPS_PLUGIN_SRCS) |
| 296 | # Add subdirectory of op-plugin | 296 | # Add subdirectory of op-plugin |
| 297 | include_directories(${PROJECT_SOURCE_DIR}/third_party/op-plugin) | 297 | include_directories(${PROJECT_SOURCE_DIR}/third_party/op-plugin) |
| 298 | add_subdirectory(${PROJECT_SOURCE_DIR}/third_party/op-plugin/op_plugin) | 298 | add_subdirectory(${PROJECT_SOURCE_DIR}/third_party/op-plugin/op_plugin) |
| 299 | 299 | ||
| 300 | if (DEFINED BUILD_TENSORPIPE) | 300 | if (DEFINED BUILD_TENSORPIPE) |
| 301 | add_definitions(-DUSE_RPC_FRAMEWORK) | 301 | add_definitions(-DUSE_RPC_FRAMEWORK) |
| 302 | set(BUILD_SHARED_LIBS ON) | 302 | set(BUILD_SHARED_LIBS ON) |
| 303 | add_subdirectory(${PROJECT_SOURCE_DIR}/third_party/Tensorpipe) | 303 | add_subdirectory(${PROJECT_SOURCE_DIR}/third_party/Tensorpipe) |
| 304 | set(BUILD_SHARED_LIBS OFF) | 304 | set(BUILD_SHARED_LIBS OFF) |
| 305 | endif() | 305 | endif() |
| 306 | 306 | ||
| 307 | if (DEFINED BUILD_LIBTORCH) | 307 | if (DEFINED BUILD_LIBTORCH) |
| 308 | set(CPP_SRCS ${ATEN_SRCS} ${INDUCTOR_SRCS} ${CORE_SRCS} ${OPS_PLUGIN_SRCS} ${DIST_SRCS} ${FLOP_SRCS} ${CUS_DTYPE_SRCS} ${FRAMEWORK_SRCS} ${LOGGING_SRCS} ${NPU_CPP_LIBS_SRCS} ) | 308 | set(CPP_SRCS ${ATEN_SRCS} ${INDUCTOR_SRCS} ${CORE_SRCS} ${OPS_PLUGIN_SRCS} ${DIST_SRCS} ${FLOP_SRCS} ${CUS_DTYPE_SRCS} ${FRAMEWORK_SRCS} ${LOGGING_SRCS} ${NPU_CPP_LIBS_SRCS} ) |
| 309 | else() | 309 | else() |
| 310 | # Compile code with pybind11 | 310 | # Compile code with pybind11 |
| 311 | set(CPP_SRCS ${ATEN_SRCS} ${INDUCTOR_SRCS} ${CORE_SRCS} ${OPS_PLUGIN_SRCS} ${DIST_SRCS} ${FLOP_SRCS} ${CUS_DTYPE_SRCS} ${LOGGING_SRCS} ${FRAMEWORK_SRCS} ${NPU_SRCS} ${PROF_SRCS} ${IPC_SRCS} ${UTILS_SRCS} ${SAN_SRCS} ${AFD_SRCS}) | 311 | set(CPP_SRCS ${ATEN_SRCS} ${INDUCTOR_SRCS} ${CORE_SRCS} ${OPS_PLUGIN_SRCS} ${DIST_SRCS} ${FLOP_SRCS} ${CUS_DTYPE_SRCS} ${LOGGING_SRCS} ${FRAMEWORK_SRCS} ${NPU_SRCS} ${PROF_SRCS} ${IPC_SRCS} ${UTILS_SRCS} ${SAN_SRCS} ${AFD_SRCS}) |
| 312 | endif() | 312 | endif() |
| 313 | 313 | ||
| 314 | add_library(${PLUGIN_NAME} SHARED ${CPP_SRCS}) | 314 | add_library(${PLUGIN_NAME} SHARED ${CPP_SRCS}) |
| 315 | include(CheckCXXCompilerFlag) | 315 | include(CheckCXXCompilerFlag) |
| 316 | check_cxx_compiler_flag("-fvisibility=hidden" COMPILER_SUPPORTS_HIDDEN_VISIBILITY) | 316 | check_cxx_compiler_flag("-fvisibility=hidden" COMPILER_SUPPORTS_HIDDEN_VISIBILITY) |
| 317 | if(${COMPILER_SUPPORTS_HIDDEN_VISIBILITY}) | 317 | if(${COMPILER_SUPPORTS_HIDDEN_VISIBILITY}) |
| 318 | target_compile_options(${PLUGIN_NAME} PRIVATE "-fvisibility=hidden") | 318 | target_compile_options(${PLUGIN_NAME} PRIVATE "-fvisibility=hidden") |
| 319 | endif() | 319 | endif() |
| 320 | 320 | ||
| 321 | target_link_options(${PLUGIN_NAME} PRIVATE "-Wl,-Bsymbolic-functions,--no-as-needed") | 321 | target_link_options(${PLUGIN_NAME} PRIVATE "-Wl,-Bsymbolic-functions,--no-as-needed") |
| 322 | 322 | ||
| 323 | if (DEFINED BUILD_TORCHAIR) | 323 | if (DEFINED BUILD_TORCHAIR) |
| 324 | add_subdirectory(${TORCHNPU_THIRD_PARTY_ROOT}/torchair) | 324 | add_subdirectory(${TORCHNPU_THIRD_PARTY_ROOT}/torchair) |
| 325 | add_dependencies(${PLUGIN_NAME} copy_torchair_pyfiles) | 325 | add_dependencies(${PLUGIN_NAME} copy_torchair_pyfiles) |
| 326 | endif() | 326 | endif() |
| 327 | 327 | ||
| 328 | add_subdirectory(${TORCHNPU_THIRD_PARTY_ROOT}/fmt EXCLUDE_FROM_ALL) | 328 | add_subdirectory(${TORCHNPU_THIRD_PARTY_ROOT}/fmt EXCLUDE_FROM_ALL) |
| 329 | 329 | ||
| 330 | add_subdirectory(${PROJECT_SOURCE_DIR}/third_party/dvm) | 330 | add_subdirectory(${PROJECT_SOURCE_DIR}/third_party/dvm) |
| 331 | add_dependencies(${PLUGIN_NAME} dvm_build) | 331 | add_dependencies(${PLUGIN_NAME} dvm_build) |
| 332 | target_link_libraries(${PLUGIN_NAME} PRIVATE ${PROJECT_SOURCE_DIR}/third_party/dvm/dvm/libdvm.a) | 332 | target_link_libraries(${PLUGIN_NAME} PRIVATE ${PROJECT_SOURCE_DIR}/third_party/dvm/dvm/libdvm.a) |
| 333 | 333 | ||
| 334 | link_directories(${PYTORCH_INSTALL_DIR}/lib) | 334 | link_directories(${PYTORCH_INSTALL_DIR}/lib) |
| 335 | link_directories(${TORCHNPU_THIRD_PARTY_ROOT}/acl/libs) | 335 | link_directories(${TORCHNPU_THIRD_PARTY_ROOT}/acl/libs) |
| 336 | 336 | ||
| 337 | target_link_libraries(${PLUGIN_NAME} PUBLIC ${TORCHNPU_THIRD_PARTY_ROOT}/acl/libs/libhccl.so) | 337 | target_link_libraries(${PLUGIN_NAME} PUBLIC ${TORCHNPU_THIRD_PARTY_ROOT}/acl/libs/libhccl.so) |
| 338 | 338 | ||
| 339 | if (NOT DEFINED BUILD_LIBTORCH) | 339 | if (NOT DEFINED BUILD_LIBTORCH) |
| 340 | target_link_libraries(${PLUGIN_NAME} PUBLIC ${PYTORCH_INSTALL_DIR}/lib/libtorch_python.so) | 340 | target_link_libraries(${PLUGIN_NAME} PUBLIC ${PYTORCH_INSTALL_DIR}/lib/libtorch_python.so) |
| 341 | endif() | 341 | endif() |
| 342 | 342 | ||
| 343 | target_link_libraries(${PLUGIN_NAME} PUBLIC ${TORCHNPU_THIRD_PARTY_ROOT}/acl/libs/libascendcl.so) | 343 | target_link_libraries(${PLUGIN_NAME} PUBLIC ${TORCHNPU_THIRD_PARTY_ROOT}/acl/libs/libascendcl.so) |
| 344 | target_link_libraries(${PLUGIN_NAME} PUBLIC ${TORCHNPU_THIRD_PARTY_ROOT}/acl/libs/libacl_op_compiler.so) | 344 | target_link_libraries(${PLUGIN_NAME} PUBLIC ${TORCHNPU_THIRD_PARTY_ROOT}/acl/libs/libacl_op_compiler.so) |
| 345 | target_link_libraries(${PLUGIN_NAME} PUBLIC ${TORCHNPU_THIRD_PARTY_ROOT}/acl/libs/libge_runner.so) | 345 | target_link_libraries(${PLUGIN_NAME} PUBLIC ${TORCHNPU_THIRD_PARTY_ROOT}/acl/libs/libge_runner.so) |
| 346 | target_link_libraries(${PLUGIN_NAME} PUBLIC ${TORCHNPU_THIRD_PARTY_ROOT}/acl/libs/libgraph.so) | 346 | target_link_libraries(${PLUGIN_NAME} PUBLIC ${TORCHNPU_THIRD_PARTY_ROOT}/acl/libs/libgraph.so) |
| 347 | 347 | ||
| 348 | if (DEFINED BUILD_TENSORPIPE) | 348 | if (DEFINED BUILD_TENSORPIPE) |
| 349 | target_link_libraries(${PLUGIN_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/build/packages/torch_npu/lib/libtensorpipe.so) | 349 | target_link_libraries(${PLUGIN_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/build/packages/torch_npu/lib/libtensorpipe.so) |
| 350 | endif() | 350 | endif() |
| 351 | 351 | ||
| 352 | target_link_libraries(${PLUGIN_NAME} PUBLIC torch torch_cpu c10 fmt::fmt-header-only) | 352 | target_link_libraries(${PLUGIN_NAME} PUBLIC torch torch_cpu c10 fmt::fmt-header-only) |
| 353 | 353 | ||
| 354 | if (NOT DEFINED BUILD_LIBTORCH) | 354 | if (NOT DEFINED BUILD_LIBTORCH) |
| 355 | set(ATEN_THREADING "OMP" CACHE STRING "ATen parallel backend") | 355 | set(ATEN_THREADING "OMP" CACHE STRING "ATen parallel backend") |
| 356 | message(STATUS "Using ATen parallel backend: ${ATEN_THREADING}") | 356 | message(STATUS "Using ATen parallel backend: ${ATEN_THREADING}") |
| 357 | if ("${ATEN_THREADING}" STREQUAL "OMP") | 357 | if ("${ATEN_THREADING}" STREQUAL "OMP") |
| 358 | target_compile_definitions(${PLUGIN_NAME} PUBLIC "-DAT_PARALLEL_OPENMP=1") | 358 | target_compile_definitions(${PLUGIN_NAME} PUBLIC "-DAT_PARALLEL_OPENMP=1") |
| 359 | elseif ("${ATEN_THREADING}" STREQUAL "NATIVE") | 359 | elseif ("${ATEN_THREADING}" STREQUAL "NATIVE") |
| 360 | target_compile_definitions(${PLUGIN_NAME} PUBLIC "-DAT_PARALLEL_NATIVE=1") | 360 | target_compile_definitions(${PLUGIN_NAME} PUBLIC "-DAT_PARALLEL_NATIVE=1") |
| 361 | elseif ("${ATEN_THREADING}" STREQUAL "TBB") | 361 | elseif ("${ATEN_THREADING}" STREQUAL "TBB") |
| 362 | target_compile_definitions(${PLUGIN_NAME} PUBLIC "-DAT_PARALLEL_NATIVE_TBB=1") | 362 | target_compile_definitions(${PLUGIN_NAME} PUBLIC "-DAT_PARALLEL_NATIVE_TBB=1") |
| 363 | else() | 363 | else() |
| 364 | message(FATAL_ERROR "Unknown ATen parallel backend: ${ATEN_THREADING}") | 364 | message(FATAL_ERROR "Unknown ATen parallel backend: ${ATEN_THREADING}") |
| 365 | endif() | 365 | endif() |
| 366 | 366 | ||
| 367 | include(GNUInstallDirs) | 367 | include(GNUInstallDirs) |
| 368 | target_compile_options(${PLUGIN_NAME} PRIVATE "-DC10_BUILD_MAIN_LIB") | 368 | target_compile_options(${PLUGIN_NAME} PRIVATE "-DC10_BUILD_MAIN_LIB") |
| 369 | install(TARGETS ${PLUGIN_NAME} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) | 369 | install(TARGETS ${PLUGIN_NAME} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) |
| 370 | endif() | 370 | endif() |
| 371 | 371 | ||
| 372 | if (NOT DEFINED BUILD_LIBTORCH) | 372 | if (NOT DEFINED BUILD_LIBTORCH) |
| 373 | add_subdirectory(${TORCHNPU_ROOT}/toolkit) | 373 | add_subdirectory(${TORCHNPU_ROOT}/toolkit) |
| 374 | target_link_libraries(${PLUGIN_NAME} PUBLIC npu_profiler) | 374 | target_link_libraries(${PLUGIN_NAME} PUBLIC npu_profiler) |
| 375 | endif() | 375 | endif() |
| 376 | 376 | ||
| 377 | if (DEFINED BUILD_GTEST) | 377 | if (DEFINED BUILD_GTEST) |
| 378 | enable_testing() | 378 | enable_testing() |
| 379 | SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/build/gtest) | 379 | SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/build/gtest) |
| 380 | add_subdirectory(${PROJECT_SOURCE_DIR}/third_party/googletest) | 380 | add_subdirectory(${PROJECT_SOURCE_DIR}/third_party/googletest) |
| 381 | include_directories(${PROJECT_SOURCE_DIR}/third_party/googletest/googletest/include) | 381 | include_directories(${PROJECT_SOURCE_DIR}/third_party/googletest/googletest/include) |
| 382 | 382 | ||
| 383 | set(TORCH_API_TEST_SOURCES) | 383 | set(TORCH_API_TEST_SOURCES) |
| 384 | add_subdirectory(${PROJECT_SOURCE_DIR}/test/cpp/api) | 384 | add_subdirectory(${PROJECT_SOURCE_DIR}/test/cpp/api) |
| 385 | add_executable(test_api ${TORCH_API_TEST_SOURCES}) | 385 | add_executable(test_api ${TORCH_API_TEST_SOURCES}) |
| 386 | 386 | ||
| 387 | target_link_libraries(test_api PUBLIC torch_npu) | 387 | target_link_libraries(test_api PUBLIC torch_npu) |
| 388 | target_link_libraries(test_api PUBLIC gtest_main gtest) | 388 | target_link_libraries(test_api PUBLIC gtest_main gtest) |
| 389 | endif() | 389 | endif() |
| 390 | 390 | ||
| 391 | if (DEFINED BUILD_LIBTORCH) | 391 | if (DEFINED BUILD_LIBTORCH) |
| 392 | configure_file( | 392 | configure_file( |
| 393 | ${PROJECT_SOURCE_DIR}/cmake/Torch_npuConfig.cmake.in | 393 | ${PROJECT_SOURCE_DIR}/cmake/Torch_npuConfig.cmake.in |
| 394 | ${PROJECT_SOURCE_DIR}/build/Torch_npuConfig.cmake | 394 | ${PROJECT_SOURCE_DIR}/build/Torch_npuConfig.cmake |
| 395 | @ONLY) | 395 | @ONLY) |
| 396 | endif() | 396 | endif() |
| 397 | |||
| @@ -1,190 +1,190 @@ | |||
| 1 | OPEN SOURCE SOFTWARE NOTICE | 1 | OPEN SOURCE SOFTWARE NOTICE |
| 2 | Please note we provide an open source software notice along with this product and/or this product firmware (in the following just "this product"). The open source software licenses are granted by the respective right holders. And the open source licenses prevail all other license information with regard to the respective open source software contained in the product, including but not limited to End User Software Licensing Agreement. This notice is provided on behalf of Huawei Technologies Co. Ltd. and any of its local subsidiaries which may have provided this product to you in your local country. | 2 | Please note we provide an open source software notice along with this product and/or this product firmware (in the following just "this product"). The open source software licenses are granted by the respective right holders. And the open source licenses prevail all other license information with regard to the respective open source software contained in the product, including but not limited to End User Software Licensing Agreement. This notice is provided on behalf of Huawei Technologies Co. Ltd. and any of its local subsidiaries which may have provided this product to you in your local country. |
| 3 | 3 | ||
| 4 | Warranty Disclaimer | 4 | Warranty Disclaimer |
| 5 | THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS. | 5 | THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS. |
| 6 | 6 | ||
| 7 | Copyright Notice and License Texts | 7 | Copyright Notice and License Texts |
| 8 | Software: pytorch v2.6.0 | 8 | Software: pytorch v2.6.0 |
| 9 | Copyright notice: | 9 | Copyright notice: |
| 10 | Copyright (c) Advanced Micro Devices, Inc. | 10 | Copyright (c) Advanced Micro Devices, Inc. |
| 11 | 11 | ||
| 12 | Copyright (c) Microsoft Corporation | 12 | Copyright (c) Microsoft Corporation |
| 13 | 13 | ||
| 14 | Copyright (c) Bjorn Fahller | 14 | Copyright (c) Bjorn Fahller |
| 15 | 15 | ||
| 16 | Copyright (c) 2001-2014 Python Software Foundation All Rights Reserved | 16 | Copyright (c) 2001-2014 Python Software Foundation All Rights Reserved |
| 17 | 17 | ||
| 18 | Copyright (c) 2011-2013 NYU | 18 | Copyright (c) 2011-2013 NYU |
| 19 | 19 | ||
| 20 | Copyright (c) 1995-2011 by Fredrik Lundh | 20 | Copyright (c) 1995-2011 by Fredrik Lundh |
| 21 | 21 | ||
| 22 | Copyright (c) Edward Z. Yang ezyang@mit.edu | 22 | Copyright (c) Edward Z. Yang ezyang@mit.edu |
| 23 | 23 | ||
| 24 | Copyright (c) 2014- Facebook, Inc | 24 | Copyright (c) 2014- Facebook, Inc |
| 25 | 25 | ||
| 26 | Copyright (c) 2017 The Android Open Source Project | 26 | Copyright (c) 2017 The Android Open Source Project |
| 27 | 27 | ||
| 28 | Copyright Python Software Foundation | 28 | Copyright Python Software Foundation |
| 29 | 29 | ||
| 30 | Copyright (c) 2012 Massachusetts Institute of Technology | 30 | Copyright (c) 2012 Massachusetts Institute of Technology |
| 31 | 31 | ||
| 32 | Copyright (c) 2018 Alex Rogozhnikov | 32 | Copyright (c) 2018 Alex Rogozhnikov |
| 33 | 33 | ||
| 34 | Copyright (c) 2007-2009 Scientific Computing and Imaging Institute, University of Utah | 34 | Copyright (c) 2007-2009 Scientific Computing and Imaging Institute, University of Utah |
| 35 | 35 | ||
| 36 | Copyright (c) 2006 Idiap Research Institute | 36 | Copyright (c) 2006 Idiap Research Institute |
| 37 | 37 | ||
| 38 | Copyright (c) 2011-2021, NVIDIA CORPORATION. All rights reserved | 38 | Copyright (c) 2011-2021, NVIDIA CORPORATION. All rights reserved |
| 39 | 39 | ||
| 40 | Copyright (c) 2015 Yangqing Jia All rights reserved | 40 | Copyright (c) 2015 Yangqing Jia All rights reserved |
| 41 | 41 | ||
| 42 | Copyright (c) Meta Platforms, Inc. | 42 | Copyright (c) Meta Platforms, Inc. |
| 43 | 43 | ||
| 44 | Copyright 2023-present Facebook. All Rights Reserved | 44 | Copyright 2023-present Facebook. All Rights Reserved |
| 45 | 45 | ||
| 46 | Copyright (c) 2022 Apple Inc. | 46 | Copyright (c) 2022 Apple Inc. |
| 47 | 47 | ||
| 48 | Copyright (c) 2005-2017, NumPy Developers. All rights reserved | 48 | Copyright (c) 2005-2017, NumPy Developers. All rights reserved |
| 49 | 49 | ||
| 50 | Copyright (c) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, All rights reserved | 50 | Copyright (c) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, All rights reserved |
| 51 | 51 | ||
| 52 | Copyright (c) 2014, The Regents | 52 | Copyright (c) 2014, The Regents |
| 53 | 53 | ||
| 54 | Copyright (c) 2005-2010 ActiveState Software Inc. | 54 | Copyright (c) 2005-2010 ActiveState Software Inc. |
| 55 | 55 | ||
| 56 | Copyright Malte Skarupke 2017 | 56 | Copyright Malte Skarupke 2017 |
| 57 | 57 | ||
| 58 | Copyright (c) 2006-2010 NEC Laboratories America (Ronan Collobert, Leon Bottou, Iain Melvin, Jason Weston) | 58 | Copyright (c) 2006-2010 NEC Laboratories America (Ronan Collobert, Leon Bottou, Iain Melvin, Jason Weston) |
| 59 | 59 | ||
| 60 | Copyright 2005, Google Inc. All rights reserved | 60 | Copyright 2005, Google Inc. All rights reserved |
| 61 | 61 | ||
| 62 | Copyright (c) Meta Platforms, Inc. and affiliates | 62 | Copyright (c) Meta Platforms, Inc. and affiliates |
| 63 | 63 | ||
| 64 | Copyright (c) 2023, Advanced Micro Devices, Inc. | 64 | Copyright (c) 2023, Advanced Micro Devices, Inc. |
| 65 | 65 | ||
| 66 | Copyright (c) 2022, Tri Dao | 66 | Copyright (c) 2022, Tri Dao |
| 67 | 67 | ||
| 68 | Copyright (c) 2005-2023 NVIDIA Corporation Built | 68 | Copyright (c) 2005-2023 NVIDIA Corporation Built |
| 69 | 69 | ||
| 70 | Copyright (c) 2001-2002 Enthought, Inc. 2003-2019, SciPy Developers. All rights reserved | 70 | Copyright (c) 2001-2002 Enthought, Inc. 2003-2019, SciPy Developers. All rights reserved |
| 71 | 71 | ||
| 72 | Copyright 2008 Google Inc. All rights reserved | 72 | Copyright 2008 Google Inc. All rights reserved |
| 73 | 73 | ||
| 74 | Copyright (c) 2021, 2023-2024 Arm Limited | 74 | Copyright (c) 2021, 2023-2024 Arm Limited |
| 75 | 75 | ||
| 76 | Copyright (c) 2003-2017 Josef Weidendorfer. All rights reserved | 76 | Copyright (c) 2003-2017 Josef Weidendorfer. All rights reserved |
| 77 | 77 | ||
| 78 | Copyright (c) 1997-2011 by Secret Labs AB | 78 | Copyright (c) 1997-2011 by Secret Labs AB |
| 79 | 79 | ||
| 80 | Copyright (c) 2016- Facebook, Inc | 80 | Copyright (c) 2016- Facebook, Inc |
| 81 | 81 | ||
| 82 | Copyright (c) 2014 Matthew Rocklin | 82 | Copyright (c) 2014 Matthew Rocklin |
| 83 | 83 | ||
| 84 | Copyright (c) 2005-2022 NVIDIA Corporation Built | 84 | Copyright (c) 2005-2022 NVIDIA Corporation Built |
| 85 | 85 | ||
| 86 | Copyright (c) Facebook, Inc. | 86 | Copyright (c) Facebook, Inc. |
| 87 | 87 | ||
| 88 | Copyright 2019-2020 Kakao Brain | 88 | Copyright 2019-2020 Kakao Brain |
| 89 | 89 | ||
| 90 | Copyright (c) 2000-2017 Julian Seward. All rights reserved | 90 | Copyright (c) 2000-2017 Julian Seward. All rights reserved |
| 91 | 91 | ||
| 92 | Copyright (c) 2005-2020 Rich Felker | 92 | Copyright (c) 2005-2020 Rich Felker |
| 93 | 93 | ||
| 94 | Copyright (c) 2008 - 2009 NVIDIA Corporation. All rights reserved | 94 | Copyright (c) 2008 - 2009 NVIDIA Corporation. All rights reserved |
| 95 | 95 | ||
| 96 | Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC | 96 | Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC |
| 97 | 97 | ||
| 98 | Copyright (c) 2016 Facebook Inc. | 98 | Copyright (c) 2016 Facebook Inc. |
| 99 | 99 | ||
| 100 | Copyright (c) 2001-2004 Idiap Research Institute (Ronan Collobert, Samy Bengio, Johnny Mariethoz) | 100 | Copyright (c) 2001-2004 Idiap Research Institute (Ronan Collobert, Samy Bengio, Johnny Mariethoz) |
| 101 | 101 | ||
| 102 | Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC All Rights Reserved | 102 | Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC All Rights Reserved |
| 103 | 103 | ||
| 104 | Copyright (c) 2012-2014 Deepmind Technologies | 104 | Copyright (c) 2012-2014 Deepmind Technologies |
| 105 | 105 | ||
| 106 | Copyright (c) 2012 Giovanni Garberoglio Interdisciplinary Laboratory | 106 | Copyright (c) 2012 Giovanni Garberoglio Interdisciplinary Laboratory |
| 107 | 107 | ||
| 108 | Copyright (c) 2024, Tri Dao | 108 | Copyright (c) 2024, Tri Dao |
| 109 | 109 | ||
| 110 | Copyright (c) Donald Stufft and individual contributors. All rights reserved | 110 | Copyright (c) Donald Stufft and individual contributors. All rights reserved |
| 111 | 111 | ||
| 112 | Copyright (c) 2018, Steven Moshier All rights reserved | 112 | Copyright (c) 2018, Steven Moshier All rights reserved |
| 113 | 113 | ||
| 114 | Copyright (c) 2015 Google Inc. All rights reserved | 114 | Copyright (c) 2015 Google Inc. All rights reserved |
| 115 | 115 | ||
| 116 | Copyright (c) 2015-2016 Advanced Micro Devices, Inc. All rights reserved | 116 | Copyright (c) 2015-2016 Advanced Micro Devices, Inc. All rights reserved |
| 117 | 117 | ||
| 118 | Copyright (c) 2010-2022 by Alex Clark and contributors | 118 | Copyright (c) 2010-2022 by Alex Clark and contributors |
| 119 | 119 | ||
| 120 | Copyright 2015 Google Inc. All Rights Reserved | 120 | Copyright 2015 Google Inc. All Rights Reserved |
| 121 | 121 | ||
| 122 | Copyright (c) 2017 - 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved | 122 | Copyright (c) 2017 - 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved |
| 123 | 123 | ||
| 124 | Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved | 124 | Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved |
| 125 | 125 | ||
| 126 | Copyright (c) 2016 manylinux | 126 | Copyright (c) 2016 manylinux |
| 127 | 127 | ||
| 128 | Copyright (c) 2017 - 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved | 128 | Copyright (c) 2017 - 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved |
| 129 | 129 | ||
| 130 | Copyright (c) 2011-2012 NEC Laboratories America (Koray Kavukcuoglu) | 130 | Copyright (c) 2011-2012 NEC Laboratories America (Koray Kavukcuoglu) |
| 131 | 131 | ||
| 132 | Copyright 2013-2014 RAD Game | 132 | Copyright 2013-2014 RAD Game |
| 133 | 133 | ||
| 134 | Copyright (c) 2011-2019 Stephan Brumme. All rights reserved | 134 | Copyright (c) 2011-2019 Stephan Brumme. All rights reserved |
| 135 | 135 | ||
| 136 | Copyright (c) 2018 MathInf GmbH, Thomas Viehmann | 136 | Copyright (c) 2018 MathInf GmbH, Thomas Viehmann |
| 137 | 137 | ||
| 138 | Copyright (c) 2013 Eddy Petrisor | 138 | Copyright (c) 2013 Eddy Petrisor |
| 139 | 139 | ||
| 140 | Copyright (c) 2023-2024 The ggml | 140 | Copyright (c) 2023-2024 The ggml |
| 141 | 141 | ||
| 142 | Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Python Software Foundation All Rights Reserved | 142 | Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Python Software Foundation All Rights Reserved |
| 143 | 143 | ||
| 144 | Copyright 2004-present Facebook. All Rights Reserved | 144 | Copyright 2004-present Facebook. All Rights Reserved |
| 145 | 145 | ||
| 146 | Copyright (c) 2010 ActiveState Software Inc. | 146 | Copyright (c) 2010 ActiveState Software Inc. |
| 147 | 147 | ||
| 148 | Copyright (c) 2006 The Android Open Source Project | 148 | Copyright (c) 2006 The Android Open Source Project |
| 149 | 149 | ||
| 150 | (c) Meta Platforms, Inc. and affiliates. All rights reserved | 150 | (c) Meta Platforms, Inc. and affiliates. All rights reserved |
| 151 | 151 | ||
| 152 | Copyright (c) 2023 Apple Inc. | 152 | Copyright (c) 2023 Apple Inc. |
| 153 | 153 | ||
| 154 | Copyright (c) Microsoft Corporation. All rights reserved | 154 | Copyright (c) Microsoft Corporation. All rights reserved |
| 155 | 155 | ||
| 156 | Copyright 2015 The TensorFlow Authors. All Rights Reserved | 156 | Copyright 2015 The TensorFlow Authors. All Rights Reserved |
| 157 | 157 | ||
| 158 | Copyright (c) 2023, Tri Dao | 158 | Copyright (c) 2023, Tri Dao |
| 159 | 159 | ||
| 160 | Copyright 2022 Cruise LLC | 160 | Copyright 2022 Cruise LLC |
| 161 | 161 | ||
| 162 | Copyright (c) Meta Platforms, Inc. and affiliates. All rights reserved | 162 | Copyright (c) Meta Platforms, Inc. and affiliates. All rights reserved |
| 163 | 163 | ||
| 164 | Copyright (c) 2022 Cruise LLC. All rights reserved | 164 | Copyright (c) 2022 Cruise LLC. All rights reserved |
| 165 | 165 | ||
| 166 | Copyright (c) 2016-present, Facebook, Inc. | 166 | Copyright (c) 2016-present, Facebook, Inc. |
| 167 | 167 | ||
| 168 | (c) Copyright John Maddock 2006 | 168 | (c) Copyright John Maddock 2006 |
| 169 | 169 | ||
| 170 | Copyright (c) 2011-2014 Idiap Research Institute | 170 | Copyright (c) 2011-2014 Idiap Research Institute |
| 171 | 171 | ||
| 172 | Copyright (c) 2014 Indiana University All rights reserved | 172 | Copyright (c) 2014 Indiana University All rights reserved |
| 173 | 173 | ||
| 174 | copyright 2019 The TensorFlow Authors | 174 | copyright 2019 The TensorFlow Authors |
| 175 | 175 | ||
| 176 | Copyright (c) 2016-present, Facebook Inc. All rights reserved | 176 | Copyright (c) 2016-present, Facebook Inc. All rights reserved |
| 177 | 177 | ||
| 178 | License: BSD 3-Clause License | 178 | License: BSD 3-Clause License |
| 179 | Copyright (c) , | 179 | Copyright (c) , |
| 180 | All rights reserved. | 180 | All rights reserved. |
| 181 | 181 | ||
| 182 | 1. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: | 182 | 1. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: |
| 183 | 183 | ||
| 184 | 2. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. | 184 | 2. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. |
| 185 | 185 | ||
| 186 | 3. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. | 186 | 3. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. |
| 187 | 187 | ||
| 188 | Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. | 188 | Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. |
| 189 | 189 | ||
| 190 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 190 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| @@ -1,7 +1,7 @@ | |||
| 1 | --extra-index-url https://download.pytorch.org/whl/cpu | 1 | --extra-index-url https://download.pytorch.org/whl/cpu |
| 2 | 2 | ||
| 3 | pyyaml | 3 | pyyaml |
| 4 | setuptools | 4 | setuptools |
| 5 | auditwheel | 5 | auditwheel |
| 6 | 6 | ||
| 7 | torch==2.7.1 | 7 | torch==2.7.1 |
| @@ -1,782 +1,782 @@ | |||
| 1 | import glob | 1 | import glob |
| 2 | import multiprocessing | 2 | import multiprocessing |
| 3 | import multiprocessing.pool | 3 | import multiprocessing.pool |
| 4 | import os | 4 | import os |
| 5 | import re | 5 | import re |
| 6 | import shutil | 6 | import shutil |
| 7 | import stat | 7 | import stat |
| 8 | import subprocess | 8 | import subprocess |
| 9 | import sys | 9 | import sys |
| 10 | import traceback | 10 | import traceback |
| 11 | import platform | 11 | import platform |
| 12 | import time | 12 | import time |
| 13 | import sysconfig | 13 | import sysconfig |
| 14 | from sysconfig import get_paths | 14 | from sysconfig import get_paths |
| 15 | from pathlib import Path | 15 | from pathlib import Path |
| 16 | from typing import Union | 16 | from typing import Union |
| 17 | 17 | ||
| 18 | import distutils.ccompiler | 18 | import distutils.ccompiler |
| 19 | import distutils.command.clean | 19 | import distutils.command.clean |
| 20 | from distutils.version import LooseVersion | 20 | from distutils.version import LooseVersion |
| 21 | from distutils.command.build_py import build_py | 21 | from distutils.command.build_py import build_py |
| 22 | from setuptools import setup, distutils, Extension, find_packages | 22 | from setuptools import setup, distutils, Extension, find_packages |
| 23 | from setuptools.command.build_clib import build_clib | 23 | from setuptools.command.build_clib import build_clib |
| 24 | from setuptools.command.build_ext import build_ext | 24 | from setuptools.command.build_ext import build_ext |
| 25 | from setuptools.command.egg_info import egg_info | 25 | from setuptools.command.egg_info import egg_info |
| 26 | from setuptools.command.install import install | 26 | from setuptools.command.install import install |
| 27 | from wheel.bdist_wheel import bdist_wheel | 27 | from wheel.bdist_wheel import bdist_wheel |
| 28 | 28 | ||
| 29 | # Disable autoloading before running 'import torch' to avoid circular dependencies | 29 | # Disable autoloading before running 'import torch' to avoid circular dependencies |
| 30 | os.environ["TORCH_DEVICE_BACKEND_AUTOLOAD"] = "0" | 30 | os.environ["TORCH_DEVICE_BACKEND_AUTOLOAD"] = "0" |
| 31 | 31 | ||
| 32 | from torchnpugen.utils import PathManager | 32 | from torchnpugen.utils import PathManager |
| 33 | 33 | ||
| 34 | BASE_DIR = os.path.dirname(os.path.realpath(__file__)) | 34 | BASE_DIR = os.path.dirname(os.path.realpath(__file__)) |
| 35 | THIRD_PARTY_PATH = os.path.join(BASE_DIR, "third_party") | 35 | THIRD_PARTY_PATH = os.path.join(BASE_DIR, "third_party") |
| 36 | PathManager.check_directory_path_readable(os.path.join(BASE_DIR, "version.txt")) | 36 | PathManager.check_directory_path_readable(os.path.join(BASE_DIR, "version.txt")) |
| 37 | with open(os.path.join(BASE_DIR, "version.txt")) as version_f: | 37 | with open(os.path.join(BASE_DIR, "version.txt")) as version_f: |
| 38 | VERSION = version_f.read().strip() | 38 | VERSION = version_f.read().strip() |
| 39 | UNKNOWN = "Unknown" | 39 | UNKNOWN = "Unknown" |
| 40 | BUILD_PERMISSION = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | stat.S_IRGRP | stat.S_IXGRP | 40 | BUILD_PERMISSION = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | stat.S_IRGRP | stat.S_IXGRP |
| 41 | 41 | ||
| 42 | DISABLE_TORCHAIR = "FALSE" | 42 | DISABLE_TORCHAIR = "FALSE" |
| 43 | if os.environ.get("DISABLE_INSTALL_TORCHAIR") is not None: | 43 | if os.environ.get("DISABLE_INSTALL_TORCHAIR") is not None: |
| 44 | DISABLE_TORCHAIR = os.environ.get("DISABLE_INSTALL_TORCHAIR") | 44 | DISABLE_TORCHAIR = os.environ.get("DISABLE_INSTALL_TORCHAIR") |
| 45 | DISABLE_RPC = "FALSE" | 45 | DISABLE_RPC = "FALSE" |
| 46 | if os.environ.get("DISABLE_RPC_FRAMEWORK") is not None: | 46 | if os.environ.get("DISABLE_RPC_FRAMEWORK") is not None: |
| 47 | DISABLE_RPC = os.environ.get("DISABLE_RPC_FRAMEWORK") | 47 | DISABLE_RPC = os.environ.get("DISABLE_RPC_FRAMEWORK") |
| 48 | ENABLE_LTO = "FALSE" | 48 | ENABLE_LTO = "FALSE" |
| 49 | if os.environ.get("ENABLE_LTO") is not None: | 49 | if os.environ.get("ENABLE_LTO") is not None: |
| 50 | ENABLE_LTO = os.environ.get("ENABLE_LTO") | 50 | ENABLE_LTO = os.environ.get("ENABLE_LTO") |
| 51 | PGO_MODE = 0 | 51 | PGO_MODE = 0 |
| 52 | if os.environ.get("PGO_MODE") is not None: | 52 | if os.environ.get("PGO_MODE") is not None: |
| 53 | PGO_MODE = int(os.environ.get("PGO_MODE")) | 53 | PGO_MODE = int(os.environ.get("PGO_MODE")) |
| 54 | 54 | ||
| 55 | # change to use cxx11.abi in default since 2.7 | 55 | # change to use cxx11.abi in default since 2.7 |
| 56 | USE_CXX11_ABI = True | 56 | USE_CXX11_ABI = True |
| 57 | if os.environ.get("_GLIBCXX_USE_CXX11_ABI") is not None and os.environ.get("_GLIBCXX_USE_CXX11_ABI") == "0": | 57 | if os.environ.get("_GLIBCXX_USE_CXX11_ABI") is not None and os.environ.get("_GLIBCXX_USE_CXX11_ABI") == "0": |
| 58 | USE_CXX11_ABI = False | 58 | USE_CXX11_ABI = False |
| 59 | 59 | ||
| 60 | 60 | ||
| 61 | def get_submodule_folders(): | 61 | def get_submodule_folders(): |
| 62 | git_modules_path = os.path.join(BASE_DIR, ".gitmodules") | 62 | git_modules_path = os.path.join(BASE_DIR, ".gitmodules") |
| 63 | default_modules_path = [ | 63 | default_modules_path = [ |
| 64 | os.path.join(THIRD_PARTY_PATH, name) | 64 | os.path.join(THIRD_PARTY_PATH, name) |
| 65 | for name in [ | 65 | for name in [ |
| 66 | "op-plugin", | 66 | "op-plugin", |
| 67 | ] | 67 | ] |
| 68 | ] | 68 | ] |
| 69 | if not os.path.exists(git_modules_path): | 69 | if not os.path.exists(git_modules_path): |
| 70 | return default_modules_path | 70 | return default_modules_path |
| 71 | with open(git_modules_path) as f: | 71 | with open(git_modules_path) as f: |
| 72 | return [ | 72 | return [ |
| 73 | os.path.join(BASE_DIR, line.split("=", 1)[1].strip()) | 73 | os.path.join(BASE_DIR, line.split("=", 1)[1].strip()) |
| 74 | for line in f.readlines() | 74 | for line in f.readlines() |
| 75 | if line.strip().startswith("path") | 75 | if line.strip().startswith("path") |
| 76 | ] | 76 | ] |
| 77 | 77 | ||
| 78 | 78 | ||
| 79 | def check_submodules(): | 79 | def check_submodules(): |
| 80 | def not_exists_or_empty(folder): | 80 | def not_exists_or_empty(folder): |
| 81 | return not os.path.exists(folder) or ( | 81 | return not os.path.exists(folder) or ( |
| 82 | os.path.isdir(folder) and len(os.listdir(folder)) == 0 | 82 | os.path.isdir(folder) and len(os.listdir(folder)) == 0 |
| 83 | ) | 83 | ) |
| 84 | 84 | ||
| 85 | folders = get_submodule_folders() | 85 | folders = get_submodule_folders() |
| 86 | # If none of the submodule folders exists, try to initialize them | 86 | # If none of the submodule folders exists, try to initialize them |
| 87 | if all(not_exists_or_empty(folder) for folder in folders): | 87 | if all(not_exists_or_empty(folder) for folder in folders): |
| 88 | try: | 88 | try: |
| 89 | print(" --- Trying to initialize submodules") | 89 | print(" --- Trying to initialize submodules") |
| 90 | start = time.time() | 90 | start = time.time() |
| 91 | subprocess.check_call(["git", "submodule", "update", "--init", "--recursive"], cwd=BASE_DIR) # Compliant | 91 | subprocess.check_call(["git", "submodule", "update", "--init", "--recursive"], cwd=BASE_DIR) # Compliant |
| 92 | end = time.time() | 92 | end = time.time() |
| 93 | print(f" --- Submodule initialization took {end - start:.2f} sec") | 93 | print(f" --- Submodule initialization took {end - start:.2f} sec") |
| 94 | except Exception: | 94 | except Exception: |
| 95 | print(" --- Submodule initalization failed") | 95 | print(" --- Submodule initalization failed") |
| 96 | print("Please run:\n\tgit submodule init && git submodule update") | 96 | print("Please run:\n\tgit submodule init && git submodule update") |
| 97 | sys.exit(1) | 97 | sys.exit(1) |
| 98 | 98 | ||
| 99 | 99 | ||
| 100 | check_submodules() | 100 | check_submodules() |
| 101 | 101 | ||
| 102 | 102 | ||
| 103 | def get_sha(pytorch_root: Union[str, Path]) -> str: | 103 | def get_sha(pytorch_root: Union[str, Path]) -> str: |
| 104 | try: | 104 | try: |
| 105 | return ( | 105 | return ( |
| 106 | subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=pytorch_root) # Compliant | 106 | subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=pytorch_root) # Compliant |
| 107 | .decode("ascii") | 107 | .decode("ascii") |
| 108 | .strip() | 108 | .strip() |
| 109 | ) | 109 | ) |
| 110 | except Exception: | 110 | except Exception: |
| 111 | return UNKNOWN | 111 | return UNKNOWN |
| 112 | 112 | ||
| 113 | 113 | ||
| 114 | def generate_torch_npu_version(): | 114 | def generate_torch_npu_version(): |
| 115 | torch_npu_root = Path(__file__).parent | 115 | torch_npu_root = Path(__file__).parent |
| 116 | version_path = torch_npu_root / "torch_npu" / "version.py" | 116 | version_path = torch_npu_root / "torch_npu" / "version.py" |
| 117 | if version_path.exists(): | 117 | if version_path.exists(): |
| 118 | version_path.unlink() | 118 | version_path.unlink() |
| 119 | flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL | 119 | flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL |
| 120 | modes = stat.S_IWUSR | stat.S_IRUSR | 120 | modes = stat.S_IWUSR | stat.S_IRUSR |
| 121 | sha = get_sha(torch_npu_root) | 121 | sha = get_sha(torch_npu_root) |
| 122 | if os.getenv("BUILD_WITHOUT_SHA") is None: | 122 | if os.getenv("BUILD_WITHOUT_SHA") is None: |
| 123 | global VERSION | 123 | global VERSION |
| 124 | VERSION += "+git" + sha[:7] | 124 | VERSION += "+git" + sha[:7] |
| 125 | with os.fdopen(os.open(version_path, flags, modes), 'w') as f: | 125 | with os.fdopen(os.open(version_path, flags, modes), 'w') as f: |
| 126 | f.write("__version__ = '{version}'\n".format(version=VERSION)) | 126 | f.write("__version__ = '{version}'\n".format(version=VERSION)) |
| 127 | f.write("git_version = {}\n".format(repr(sha))) | 127 | f.write("git_version = {}\n".format(repr(sha))) |
| 128 | os.chmod(version_path, mode=stat.S_IRUSR | stat.S_IEXEC | stat.S_IRGRP | stat.S_IXGRP) | 128 | os.chmod(version_path, mode=stat.S_IRUSR | stat.S_IEXEC | stat.S_IRGRP | stat.S_IXGRP) |
| 129 | 129 | ||
| 130 | 130 | ||
| 131 | generate_torch_npu_version() | 131 | generate_torch_npu_version() |
| 132 | 132 | ||
| 133 | 133 | ||
| 134 | def which(thefile): | 134 | def which(thefile): |
| 135 | path = os.environ.get("PATH", os.defpath).split(os.pathsep) | 135 | path = os.environ.get("PATH", os.defpath).split(os.pathsep) |
| 136 | for d in path: | 136 | for d in path: |
| 137 | fname = os.path.join(d, thefile) | 137 | fname = os.path.join(d, thefile) |
| 138 | fnames = [fname] | 138 | fnames = [fname] |
| 139 | if sys.platform == 'win32': | 139 | if sys.platform == 'win32': |
| 140 | exts = os.environ.get('PATHEXT', '').split(os.pathsep) | 140 | exts = os.environ.get('PATHEXT', '').split(os.pathsep) |
| 141 | fnames += [fname + ext for ext in exts] | 141 | fnames += [fname + ext for ext in exts] |
| 142 | for name in fnames: | 142 | for name in fnames: |
| 143 | if os.access(name, os.F_OK | os.X_OK) and not os.path.isdir(name): | 143 | if os.access(name, os.F_OK | os.X_OK) and not os.path.isdir(name): |
| 144 | return name | 144 | return name |
| 145 | return None | 145 | return None |
| 146 | 146 | ||
| 147 | 147 | ||
| 148 | def get_cmake_command(): | 148 | def get_cmake_command(): |
| 149 | def _get_version(cmd): | 149 | def _get_version(cmd): |
| 150 | for line in subprocess.check_output([cmd, '--version']).decode('utf-8').split('\n'): | 150 | for line in subprocess.check_output([cmd, '--version']).decode('utf-8').split('\n'): |
| 151 | if 'version' in line: | 151 | if 'version' in line: |
| 152 | return LooseVersion(line.strip().split(' ')[2]) | 152 | return LooseVersion(line.strip().split(' ')[2]) |
| 153 | raise RuntimeError('no version found') | 153 | raise RuntimeError('no version found') |
| 154 | "Returns cmake command." | 154 | "Returns cmake command." |
| 155 | cmake_command = 'cmake' | 155 | cmake_command = 'cmake' |
| 156 | if platform.system() == 'Windows': | 156 | if platform.system() == 'Windows': |
| 157 | return cmake_command | 157 | return cmake_command |
| 158 | cmake3 = which('cmake3') | 158 | cmake3 = which('cmake3') |
| 159 | cmake = which('cmake') | 159 | cmake = which('cmake') |
| 160 | if cmake3 is not None and _get_version(cmake3) >= LooseVersion("3.18.0"): | 160 | if cmake3 is not None and _get_version(cmake3) >= LooseVersion("3.18.0"): |
| 161 | cmake_command = 'cmake3' | 161 | cmake_command = 'cmake3' |
| 162 | return cmake_command | 162 | return cmake_command |
| 163 | elif cmake is not None and _get_version(cmake) >= LooseVersion("3.18.0"): | 163 | elif cmake is not None and _get_version(cmake) >= LooseVersion("3.18.0"): |
| 164 | return cmake_command | 164 | return cmake_command |
| 165 | else: | 165 | else: |
| 166 | raise RuntimeError('no cmake or cmake3 with version >= 3.18.0 found') | 166 | raise RuntimeError('no cmake or cmake3 with version >= 3.18.0 found') |
| 167 | 167 | ||
| 168 | 168 | ||
| 169 | def get_build_type(): | 169 | def get_build_type(): |
| 170 | build_type = 'Release' | 170 | build_type = 'Release' |
| 171 | if os.getenv('DEBUG', default='0').upper() in ['ON', '1', 'YES', 'TRUE', 'Y']: | 171 | if os.getenv('DEBUG', default='0').upper() in ['ON', '1', 'YES', 'TRUE', 'Y']: |
| 172 | build_type = 'Debug' | 172 | build_type = 'Debug' |
| 173 | 173 | ||
| 174 | if os.getenv('REL_WITH_DEB_INFO', default='0').upper() in ['ON', '1', 'YES', 'TRUE', 'Y']: | 174 | if os.getenv('REL_WITH_DEB_INFO', default='0').upper() in ['ON', '1', 'YES', 'TRUE', 'Y']: |
| 175 | build_type = 'RelWithDebInfo' | 175 | build_type = 'RelWithDebInfo' |
| 176 | 176 | ||
| 177 | return build_type | 177 | return build_type |
| 178 | 178 | ||
| 179 | 179 | ||
| 180 | def _get_build_mode(): | 180 | def _get_build_mode(): |
| 181 | for i in range(1, len(sys.argv)): | 181 | for i in range(1, len(sys.argv)): |
| 182 | if not sys.argv[i].startswith('-'): | 182 | if not sys.argv[i].startswith('-'): |
| 183 | return sys.argv[i] | 183 | return sys.argv[i] |
| 184 | 184 | ||
| 185 | raise RuntimeError("Run setup.py without build mode.") | 185 | raise RuntimeError("Run setup.py without build mode.") |
| 186 | 186 | ||
| 187 | 187 | ||
| 188 | def get_pytorch_dir(): | 188 | def get_pytorch_dir(): |
| 189 | try: | 189 | try: |
| 190 | import torch | 190 | import torch |
| 191 | return os.path.dirname(os.path.realpath(torch.__file__)) | 191 | return os.path.dirname(os.path.realpath(torch.__file__)) |
| 192 | except Exception: | 192 | except Exception: |
| 193 | _, _, exc_traceback = sys.exc_info() | 193 | _, _, exc_traceback = sys.exc_info() |
| 194 | frame_summary = traceback.extract_tb(exc_traceback)[-1] | 194 | frame_summary = traceback.extract_tb(exc_traceback)[-1] |
| 195 | return os.path.dirname(frame_summary.filename) | 195 | return os.path.dirname(frame_summary.filename) |
| 196 | 196 | ||
| 197 | 197 | ||
| 198 | def generate_bindings_code(base_dir): | 198 | def generate_bindings_code(base_dir): |
| 199 | python_execute = sys.executable | 199 | python_execute = sys.executable |
| 200 | generate_code_cmd = ["bash", os.path.join(base_dir, 'generate_code.sh'), python_execute, VERSION] | 200 | generate_code_cmd = ["bash", os.path.join(base_dir, 'generate_code.sh'), python_execute, VERSION] |
| 201 | if subprocess.call(generate_code_cmd) != 0: # Compliant | 201 | if subprocess.call(generate_code_cmd) != 0: # Compliant |
| 202 | print( | 202 | print( |
| 203 | 'Failed to generate ATEN bindings: {}'.format(generate_code_cmd), | 203 | 'Failed to generate ATEN bindings: {}'.format(generate_code_cmd), |
| 204 | file=sys.stderr) | 204 | file=sys.stderr) |
| 205 | sys.exit(1) | 205 | sys.exit(1) |
| 206 | 206 | ||
| 207 | 207 | ||
| 208 | def build_stub(base_dir): | 208 | def build_stub(base_dir): |
| 209 | build_stub_cmd = ["sh", os.path.join(base_dir, 'third_party/acl/libs/build_stub.sh')] | 209 | build_stub_cmd = ["sh", os.path.join(base_dir, 'third_party/acl/libs/build_stub.sh')] |
| 210 | if subprocess.call(build_stub_cmd) != 0: | 210 | if subprocess.call(build_stub_cmd) != 0: |
| 211 | print( | 211 | print( |
| 212 | 'Failed to build stub: {}'.format(build_stub_cmd), | 212 | 'Failed to build stub: {}'.format(build_stub_cmd), |
| 213 | file=sys.stderr) | 213 | file=sys.stderr) |
| 214 | sys.exit(1) | 214 | sys.exit(1) |
| 215 | 215 | ||
| 216 | 216 | ||
| 217 | def check_torchair_valid(base_dir): | 217 | def check_torchair_valid(base_dir): |
| 218 | # build with submodule of torchair, if path of torchair is valid | 218 | # build with submodule of torchair, if path of torchair is valid |
| 219 | torchair_path = os.path.join(base_dir, 'third_party/torchair/torchair') | 219 | torchair_path = os.path.join(base_dir, 'third_party/torchair/torchair') |
| 220 | return os.path.exists(torchair_path) and ( | 220 | return os.path.exists(torchair_path) and ( |
| 221 | os.path.isdir(torchair_path) and len(os.listdir(torchair_path)) != 0 | 221 | os.path.isdir(torchair_path) and len(os.listdir(torchair_path)) != 0 |
| 222 | ) | 222 | ) |
| 223 | 223 | ||
| 224 | 224 | ||
| 225 | def check_tensorpipe_valid(base_dir): | 225 | def check_tensorpipe_valid(base_dir): |
| 226 | tensorpipe_path = os.path.join(base_dir, 'third_party/Tensorpipe/tensorpipe') | 226 | tensorpipe_path = os.path.join(base_dir, 'third_party/Tensorpipe/tensorpipe') |
| 227 | return os.path.exists(tensorpipe_path) | 227 | return os.path.exists(tensorpipe_path) |
| 228 | 228 | ||
| 229 | 229 | ||
| 230 | def generate_dbg_files_and_strip(): | 230 | def generate_dbg_files_and_strip(): |
| 231 | library_dir = Path(BASE_DIR).joinpath("build/packages/torch_npu") | 231 | library_dir = Path(BASE_DIR).joinpath("build/packages/torch_npu") |
| 232 | dbg_dir = Path(BASE_DIR).joinpath("build/dbg") | 232 | dbg_dir = Path(BASE_DIR).joinpath("build/dbg") |
| 233 | os.makedirs(dbg_dir, exist_ok=True) | 233 | os.makedirs(dbg_dir, exist_ok=True) |
| 234 | library_files = [Path(i) for i in library_dir.rglob('*.so')] | 234 | library_files = [Path(i) for i in library_dir.rglob('*.so')] |
| 235 | for library_file in library_files: | 235 | for library_file in library_files: |
| 236 | subprocess.check_call(["eu-strip", library_file, "-f", | 236 | subprocess.check_call(["eu-strip", library_file, "-f", |
| 237 | str(dbg_dir.joinpath(library_file.name)) + ".debug"], cwd=BASE_DIR) # Compliant | 237 | str(dbg_dir.joinpath(library_file.name)) + ".debug"], cwd=BASE_DIR) # Compliant |
| 238 | 238 | ||
| 239 | 239 | ||
| 240 | def patchelf_dynamic_library(): | 240 | def patchelf_dynamic_library(): |
| 241 | # Process all .so files in lib directory | 241 | # Process all .so files in lib directory |
| 242 | lib_dir = Path(BASE_DIR).joinpath("build/packages/torch_npu/lib") | 242 | lib_dir = Path(BASE_DIR).joinpath("build/packages/torch_npu/lib") |
| 243 | lib_files = [str(i) for i in lib_dir.rglob('*.so')] | 243 | lib_files = [str(i) for i in lib_dir.rglob('*.so')] |
| 244 | 244 | ||
| 245 | for library_file in lib_files: | 245 | for library_file in lib_files: |
| 246 | subprocess.check_call(["patchelf", "--remove-needed", "libgomp.so.1", library_file], cwd=BASE_DIR) # Compliant | 246 | subprocess.check_call(["patchelf", "--remove-needed", "libgomp.so.1", library_file], cwd=BASE_DIR) # Compliant |
| 247 | 247 | ||
| 248 | 248 | ||
| 249 | def CppExtension(name, sources, *args, **kwargs): | 249 | def CppExtension(name, sources, *args, **kwargs): |
| 250 | r''' | 250 | r''' |
| 251 | Creates a :class:`setuptools.Extension` for C++. | 251 | Creates a :class:`setuptools.Extension` for C++. |
| 252 | ''' | 252 | ''' |
| 253 | pytorch_dir = get_pytorch_dir() | 253 | pytorch_dir = get_pytorch_dir() |
| 254 | temp_include_dirs = kwargs.get('include_dirs', []) | 254 | temp_include_dirs = kwargs.get('include_dirs', []) |
| 255 | temp_include_dirs.append(os.path.join(pytorch_dir, 'include')) | 255 | temp_include_dirs.append(os.path.join(pytorch_dir, 'include')) |
| 256 | temp_include_dirs.append(os.path.join(pytorch_dir, 'include/torch/csrc/api/include')) | 256 | temp_include_dirs.append(os.path.join(pytorch_dir, 'include/torch/csrc/api/include')) |
| 257 | kwargs['include_dirs'] = temp_include_dirs | 257 | kwargs['include_dirs'] = temp_include_dirs |
| 258 | 258 | ||
| 259 | temp_library_dirs = kwargs.get('library_dirs', []) | 259 | temp_library_dirs = kwargs.get('library_dirs', []) |
| 260 | temp_library_dirs.append(os.path.join(pytorch_dir, 'lib')) | 260 | temp_library_dirs.append(os.path.join(pytorch_dir, 'lib')) |
| 261 | temp_library_dirs.append(os.path.join(BASE_DIR, "third_party/acl/libs")) | 261 | temp_library_dirs.append(os.path.join(BASE_DIR, "third_party/acl/libs")) |
| 262 | kwargs['library_dirs'] = temp_library_dirs | 262 | kwargs['library_dirs'] = temp_library_dirs |
| 263 | 263 | ||
| 264 | libraries = kwargs.get('libraries', []) | 264 | libraries = kwargs.get('libraries', []) |
| 265 | libraries.append('c10') | 265 | libraries.append('c10') |
| 266 | libraries.append('torch') | 266 | libraries.append('torch') |
| 267 | libraries.append('torch_cpu') | 267 | libraries.append('torch_cpu') |
| 268 | libraries.append('torch_python') | 268 | libraries.append('torch_python') |
| 269 | libraries.append('hccl') | 269 | libraries.append('hccl') |
| 270 | kwargs['libraries'] = libraries | 270 | kwargs['libraries'] = libraries |
| 271 | kwargs['language'] = 'c++' | 271 | kwargs['language'] = 'c++' |
| 272 | return Extension(name, sources, *args, **kwargs) | 272 | return Extension(name, sources, *args, **kwargs) |
| 273 | 273 | ||
| 274 | 274 | ||
| 275 | class Clean(distutils.command.clean.clean): | 275 | class Clean(distutils.command.clean.clean): |
| 276 | 276 | ||
| 277 | def run(self): | 277 | def run(self): |
| 278 | f_ignore = open('.gitignore', 'r') | 278 | f_ignore = open('.gitignore', 'r') |
| 279 | ignores = f_ignore.read() | 279 | ignores = f_ignore.read() |
| 280 | pat = re.compile(r'^#( BEGIN NOT-CLEAN-FILES )?') | 280 | pat = re.compile(r'^#( BEGIN NOT-CLEAN-FILES )?') |
| 281 | for wildcard in filter(None, ignores.split('\n')): | 281 | for wildcard in filter(None, ignores.split('\n')): |
| 282 | match = pat.match(wildcard) | 282 | match = pat.match(wildcard) |
| 283 | if match: | 283 | if match: |
| 284 | if match.group(1): | 284 | if match.group(1): |
| 285 | # Marker is found and stop reading .gitignore. | 285 | # Marker is found and stop reading .gitignore. |
| 286 | break | 286 | break |
| 287 | # Ignore lines which begin with '#'. | 287 | # Ignore lines which begin with '#'. |
| 288 | else: | 288 | else: |
| 289 | for filename in glob.glob(wildcard): | 289 | for filename in glob.glob(wildcard): |
| 290 | if os.path.islink(filename): | 290 | if os.path.islink(filename): |
| 291 | raise RuntimeError(f"Failed to remove path: {filename}") | 291 | raise RuntimeError(f"Failed to remove path: {filename}") |
| 292 | if os.path.exists(filename): | 292 | if os.path.exists(filename): |
| 293 | try: | 293 | try: |
| 294 | shutil.rmtree(filename, ignore_errors=True) | 294 | shutil.rmtree(filename, ignore_errors=True) |
| 295 | except Exception as err: | 295 | except Exception as err: |
| 296 | raise RuntimeError(f"Failed to remove path: {filename}") from err | 296 | raise RuntimeError(f"Failed to remove path: {filename}") from err |
| 297 | f_ignore.close() | 297 | f_ignore.close() |
| 298 | 298 | ||
| 299 | # It's an old-style class in Python 2.7... | 299 | # It's an old-style class in Python 2.7... |
| 300 | distutils.command.clean.clean.run(self) | 300 | distutils.command.clean.clean.run(self) |
| 301 | 301 | ||
| 302 | remove_files = [ | 302 | remove_files = [ |
| 303 | 'torch_npu/csrc/aten/RegisterCPU.cpp', | 303 | 'torch_npu/csrc/aten/RegisterCPU.cpp', |
| 304 | 'torch_npu/csrc/aten/RegisterNPU.cpp', | 304 | 'torch_npu/csrc/aten/RegisterNPU.cpp', |
| 305 | 'torch_npu/csrc/aten/RegisterAutogradNPU.cpp', | 305 | 'torch_npu/csrc/aten/RegisterAutogradNPU.cpp', |
| 306 | 'torch_npu/csrc/aten/NPUNativeFunctions.h', | 306 | 'torch_npu/csrc/aten/NPUNativeFunctions.h', |
| 307 | 'torch_npu/csrc/aten/CustomRegisterSchema.cpp', | 307 | 'torch_npu/csrc/aten/CustomRegisterSchema.cpp', |
| 308 | 'torch_npu/csrc/aten/ForeachRegister.cpp', | 308 | 'torch_npu/csrc/aten/ForeachRegister.cpp', |
| 309 | 'torch_npu/utils/custom_ops.py', | 309 | 'torch_npu/utils/custom_ops.py', |
| 310 | 'torch_npu/version.py', | 310 | 'torch_npu/version.py', |
| 311 | ] | 311 | ] |
| 312 | for remove_file in remove_files: | 312 | for remove_file in remove_files: |
| 313 | file_path = os.path.join(BASE_DIR, remove_file) | 313 | file_path = os.path.join(BASE_DIR, remove_file) |
| 314 | if os.path.exists(file_path): | 314 | if os.path.exists(file_path): |
| 315 | os.remove(file_path) | 315 | os.remove(file_path) |
| 316 | 316 | ||
| 317 | USE_NINJA = os.environ["CMAKE_GENERATOR"].lower() == "ninja" if "CMAKE_GENERATOR" in os.environ else shutil.which("ninja") | 317 | USE_NINJA = os.environ["CMAKE_GENERATOR"].lower() == "ninja" if "CMAKE_GENERATOR" in os.environ else shutil.which("ninja") |
| 318 | 318 | ||
| 319 | class CPPLibBuild(build_clib, object): | 319 | class CPPLibBuild(build_clib, object): |
| 320 | def run(self): | 320 | def run(self): |
| 321 | cmake = get_cmake_command() | 321 | cmake = get_cmake_command() |
| 322 | 322 | ||
| 323 | if cmake is None: | 323 | if cmake is None: |
| 324 | raise RuntimeError( | 324 | raise RuntimeError( |
| 325 | "CMake must be installed to build the following extensions: " + | 325 | "CMake must be installed to build the following extensions: " + |
| 326 | ", ".join(e.name for e in self.extensions)) | 326 | ", ".join(e.name for e in self.extensions)) |
| 327 | self.cmake = cmake | 327 | self.cmake = cmake |
| 328 | 328 | ||
| 329 | build_dir = os.path.join(BASE_DIR, "build") | 329 | build_dir = os.path.join(BASE_DIR, "build") |
| 330 | build_type_dir = os.path.join(build_dir) | 330 | build_type_dir = os.path.join(build_dir) |
| 331 | output_lib_path = os.path.join(build_type_dir, "packages/torch_npu/lib") | 331 | output_lib_path = os.path.join(build_type_dir, "packages/torch_npu/lib") |
| 332 | os.makedirs(build_type_dir, exist_ok=True) | 332 | os.makedirs(build_type_dir, exist_ok=True) |
| 333 | os.chmod(build_type_dir, mode=BUILD_PERMISSION) | 333 | os.chmod(build_type_dir, mode=BUILD_PERMISSION) |
| 334 | os.makedirs(output_lib_path, exist_ok=True) | 334 | os.makedirs(output_lib_path, exist_ok=True) |
| 335 | self.build_lib = os.path.relpath(os.path.join(build_dir, "packages/torch_npu")) | 335 | self.build_lib = os.path.relpath(os.path.join(build_dir, "packages/torch_npu")) |
| 336 | self.build_temp = os.path.relpath(build_type_dir) | 336 | self.build_temp = os.path.relpath(build_type_dir) |
| 337 | 337 | ||
| 338 | cmake_args = [ | 338 | cmake_args = [ |
| 339 | '-DCMAKE_BUILD_TYPE=' + get_build_type(), | 339 | '-DCMAKE_BUILD_TYPE=' + get_build_type(), |
| 340 | '-DCMAKE_INSTALL_PREFIX=' + os.path.realpath(output_lib_path), | 340 | '-DCMAKE_INSTALL_PREFIX=' + os.path.realpath(output_lib_path), |
| 341 | '-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=' + os.path.realpath(output_lib_path), | 341 | '-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=' + os.path.realpath(output_lib_path), |
| 342 | '-DCMAKE_ARCHIVE_OUTPUT_DIRECTORY=' + os.path.realpath(output_lib_path), | 342 | '-DCMAKE_ARCHIVE_OUTPUT_DIRECTORY=' + os.path.realpath(output_lib_path), |
| 343 | '-DTORCHNPU_INSTALL_LIBDIR=' + os.path.realpath(output_lib_path), | 343 | '-DTORCHNPU_INSTALL_LIBDIR=' + os.path.realpath(output_lib_path), |
| 344 | '-DPYTHON_INCLUDE_DIR=' + get_paths().get('include'), | 344 | '-DPYTHON_INCLUDE_DIR=' + get_paths().get('include'), |
| 345 | '-DTORCH_VERSION=' + VERSION, | 345 | '-DTORCH_VERSION=' + VERSION, |
| 346 | '-DPYTORCH_INSTALL_DIR=' + get_pytorch_dir()] | 346 | '-DPYTORCH_INSTALL_DIR=' + get_pytorch_dir()] |
| 347 | 347 | ||
| 348 | if DISABLE_TORCHAIR == 'FALSE': | 348 | if DISABLE_TORCHAIR == 'FALSE': |
| 349 | if check_torchair_valid(BASE_DIR): | 349 | if check_torchair_valid(BASE_DIR): |
| 350 | cmake_args.append('-DBUILD_TORCHAIR=on') | 350 | cmake_args.append('-DBUILD_TORCHAIR=on') |
| 351 | torchair_install_prefix = os.path.join(build_type_dir, "packages/torch_npu/dynamo/torchair") | 351 | torchair_install_prefix = os.path.join(build_type_dir, "packages/torch_npu/dynamo/torchair") |
| 352 | cmake_args.append(f'-DTORCHAIR_INSTALL_PREFIX={torchair_install_prefix}') | 352 | cmake_args.append(f'-DTORCHAIR_INSTALL_PREFIX={torchair_install_prefix}') |
| 353 | cmake_args.append(f'-DTORCHAIR_TARGET_PYTHON={sys.executable}') | 353 | cmake_args.append(f'-DTORCHAIR_TARGET_PYTHON={sys.executable}') |
| 354 | 354 | ||
| 355 | if DISABLE_RPC == 'FALSE': | 355 | if DISABLE_RPC == 'FALSE': |
| 356 | if check_tensorpipe_valid(BASE_DIR): | 356 | if check_tensorpipe_valid(BASE_DIR): |
| 357 | cmake_args.append('-DBUILD_TENSORPIPE=on') | 357 | cmake_args.append('-DBUILD_TENSORPIPE=on') |
| 358 | 358 | ||
| 359 | if ENABLE_LTO == "TRUE": | 359 | if ENABLE_LTO == "TRUE": |
| 360 | cmake_args.append('-DENABLE_LTO=on') | 360 | cmake_args.append('-DENABLE_LTO=on') |
| 361 | if PGO_MODE != 0: | 361 | if PGO_MODE != 0: |
| 362 | cmake_args.append('-DPGO_MODE=' + str(PGO_MODE)) | 362 | cmake_args.append('-DPGO_MODE=' + str(PGO_MODE)) |
| 363 | 363 | ||
| 364 | if USE_CXX11_ABI: | 364 | if USE_CXX11_ABI: |
| 365 | cmake_args.append('-DGLIBCXX_USE_CXX11_ABI=1') | 365 | cmake_args.append('-DGLIBCXX_USE_CXX11_ABI=1') |
| 366 | 366 | ||
| 367 | if os.getenv('_ABI_VERSION') is not None: | 367 | if os.getenv('_ABI_VERSION') is not None: |
| 368 | cmake_args.append('-DABI_VERSION=' + os.getenv('_ABI_VERSION')) | 368 | cmake_args.append('-DABI_VERSION=' + os.getenv('_ABI_VERSION')) |
| 369 | 369 | ||
| 370 | if USE_NINJA: | 370 | if USE_NINJA: |
| 371 | cmake_args.append("-GNinja") | 371 | cmake_args.append("-GNinja") |
| 372 | 372 | ||
| 373 | max_jobs = os.getenv("MAX_JOBS") | 373 | max_jobs = os.getenv("MAX_JOBS") |
| 374 | if max_jobs is not None or not USE_NINJA: | 374 | if max_jobs is not None or not USE_NINJA: |
| 375 | max_jobs = max_jobs or str(multiprocessing.cpu_count()) | 375 | max_jobs = max_jobs or str(multiprocessing.cpu_count()) |
| 376 | build_args = ['-j', max_jobs] | 376 | build_args = ['-j', max_jobs] |
| 377 | else: | 377 | else: |
| 378 | build_args = [] | 378 | build_args = [] |
| 379 | 379 | ||
| 380 | subprocess.check_call([self.cmake, BASE_DIR] + cmake_args, cwd=build_type_dir, env=os.environ) | 380 | subprocess.check_call([self.cmake, BASE_DIR] + cmake_args, cwd=build_type_dir, env=os.environ) |
| 381 | for base_dir, dirs, files in os.walk(build_type_dir): | 381 | for base_dir, dirs, files in os.walk(build_type_dir): |
| 382 | for dir_name in dirs: | 382 | for dir_name in dirs: |
| 383 | dir_path = os.path.join(base_dir, dir_name) | 383 | dir_path = os.path.join(base_dir, dir_name) |
| 384 | os.chmod(dir_path, mode=BUILD_PERMISSION) | 384 | os.chmod(dir_path, mode=BUILD_PERMISSION) |
| 385 | for file_name in files: | 385 | for file_name in files: |
| 386 | file_path = os.path.join(base_dir, file_name) | 386 | file_path = os.path.join(base_dir, file_name) |
| 387 | os.chmod(file_path, mode=BUILD_PERMISSION) | 387 | os.chmod(file_path, mode=BUILD_PERMISSION) |
| 388 | 388 | ||
| 389 | if USE_NINJA: | 389 | if USE_NINJA: |
| 390 | subprocess.check_call(['ninja'] + build_args, cwd=build_type_dir, env=os.environ) | 390 | subprocess.check_call(['ninja'] + build_args, cwd=build_type_dir, env=os.environ) |
| 391 | else: | 391 | else: |
| 392 | subprocess.check_call(['make'] + build_args, cwd=build_type_dir, env=os.environ) | 392 | subprocess.check_call(['make'] + build_args, cwd=build_type_dir, env=os.environ) |
| 393 | 393 | ||
| 394 | 394 | ||
| 395 | class Build(build_ext, object): | 395 | class Build(build_ext, object): |
| 396 | 396 | ||
| 397 | def run(self): | 397 | def run(self): |
| 398 | self.run_command('build_clib') | 398 | self.run_command('build_clib') |
| 399 | self.run_command('build_py') | 399 | self.run_command('build_py') |
| 400 | # proceed with the normal build_ext process | 400 | # proceed with the normal build_ext process |
| 401 | self.build_lib = os.path.relpath(os.path.join(BASE_DIR, "build/packages")) | 401 | self.build_lib = os.path.relpath(os.path.join(BASE_DIR, "build/packages")) |
| 402 | self.build_temp = os.path.relpath(os.path.join(BASE_DIR, "build/temp")) | 402 | self.build_temp = os.path.relpath(os.path.join(BASE_DIR, "build/temp")) |
| 403 | self.library_dirs.append( | 403 | self.library_dirs.append( |
| 404 | os.path.relpath(os.path.join(BASE_DIR, "build/packages/torch_npu/lib")) | 404 | os.path.relpath(os.path.join(BASE_DIR, "build/packages/torch_npu/lib")) |
| 405 | ) | 405 | ) |
| 406 | super(Build, self).run() | 406 | super(Build, self).run() |
| 407 | 407 | ||
| 408 | 408 | ||
| 409 | class InstallCmd(install): | 409 | class InstallCmd(install): |
| 410 | 410 | ||
| 411 | def finalize_options(self) -> None: | 411 | def finalize_options(self) -> None: |
| 412 | self.build_lib = os.path.relpath(os.path.join(BASE_DIR, "build/packages")) | 412 | self.build_lib = os.path.relpath(os.path.join(BASE_DIR, "build/packages")) |
| 413 | return super(InstallCmd, self).finalize_options() | 413 | return super(InstallCmd, self).finalize_options() |
| 414 | 414 | ||
| 415 | 415 | ||
| 416 | def add_ops_files(base_dir, file_list): | 416 | def add_ops_files(base_dir, file_list): |
| 417 | # add ops header files | 417 | # add ops header files |
| 418 | plugin_path = os.path.join(base_dir, 'third_party/op-plugin/op_plugin/include') | 418 | plugin_path = os.path.join(base_dir, 'third_party/op-plugin/op_plugin/include') |
| 419 | if os.path.exists(plugin_path): | 419 | if os.path.exists(plugin_path): |
| 420 | file_list.append('third_party/op-plugin/op_plugin/include/*.h') | 420 | file_list.append('third_party/op-plugin/op_plugin/include/*.h') |
| 421 | plugin_utils_path = os.path.join(base_dir, 'third_party/op-plugin/op_plugin/utils') | 421 | plugin_utils_path = os.path.join(base_dir, 'third_party/op-plugin/op_plugin/utils') |
| 422 | if os.path.exists(plugin_utils_path): | 422 | if os.path.exists(plugin_utils_path): |
| 423 | file_list.append('third_party/op-plugin/op_plugin/utils/*.h') | 423 | file_list.append('third_party/op-plugin/op_plugin/utils/*.h') |
| 424 | return | 424 | return |
| 425 | 425 | ||
| 426 | 426 | ||
| 427 | def add_ops_python_files(ret_list): | 427 | def add_ops_python_files(ret_list): |
| 428 | # add ops python files | 428 | # add ops python files |
| 429 | opplugin_path = os.path.join(BASE_DIR, 'third_party/op-plugin/op_plugin/python') | 429 | opplugin_path = os.path.join(BASE_DIR, 'third_party/op-plugin/op_plugin/python') |
| 430 | 430 | ||
| 431 | if os.path.exists(opplugin_path): | 431 | if os.path.exists(opplugin_path): |
| 432 | ops_python_files = glob.glob(os.path.join(opplugin_path, '**/*.py'), recursive=True) | 432 | ops_python_files = glob.glob(os.path.join(opplugin_path, '**/*.py'), recursive=True) |
| 433 | for src in ops_python_files: | 433 | for src in ops_python_files: |
| 434 | dst = os.path.join( | 434 | dst = os.path.join( |
| 435 | os.path.join(BASE_DIR, "build/packages/torch_npu/op_plugin"), | 435 | os.path.join(BASE_DIR, "build/packages/torch_npu/op_plugin"), |
| 436 | os.path.relpath(src, opplugin_path)) | 436 | os.path.relpath(src, opplugin_path)) |
| 437 | os.makedirs(os.path.dirname(dst), exist_ok=True) | 437 | os.makedirs(os.path.dirname(dst), exist_ok=True) |
| 438 | ret_list.append((src, dst)) | 438 | ret_list.append((src, dst)) |
| 439 | return | 439 | return |
| 440 | 440 | ||
| 441 | 441 | ||
| 442 | def get_src_py_and_dst(): | 442 | def get_src_py_and_dst(): |
| 443 | ret = [] | 443 | ret = [] |
| 444 | generated_python_files = glob.glob( | 444 | generated_python_files = glob.glob( |
| 445 | os.path.join(BASE_DIR, "torch_npu", '**/*.py'), | 445 | os.path.join(BASE_DIR, "torch_npu", '**/*.py'), |
| 446 | recursive=True) + glob.glob( | 446 | recursive=True) + glob.glob( |
| 447 | os.path.join(BASE_DIR, "torch_npu", '**/*.yaml'), | 447 | os.path.join(BASE_DIR, "torch_npu", '**/*.yaml'), |
| 448 | recursive=True) + glob.glob( | 448 | recursive=True) + glob.glob( |
| 449 | os.path.join(BASE_DIR, "torch_npu", 'acl*.json'), | 449 | os.path.join(BASE_DIR, "torch_npu", 'acl*.json'), |
| 450 | recursive=True) + glob.glob( | 450 | recursive=True) + glob.glob( |
| 451 | os.path.join(BASE_DIR, "torch_npu", 'contrib/apis_config.json'), | 451 | os.path.join(BASE_DIR, "torch_npu", 'contrib/apis_config.json'), |
| 452 | recursive=True) | 452 | recursive=True) |
| 453 | for src in generated_python_files: | 453 | for src in generated_python_files: |
| 454 | dst = os.path.join( | 454 | dst = os.path.join( |
| 455 | os.path.join(BASE_DIR, "build/packages/torch_npu"), | 455 | os.path.join(BASE_DIR, "build/packages/torch_npu"), |
| 456 | os.path.relpath(src, os.path.join(BASE_DIR, "torch_npu"))) | 456 | os.path.relpath(src, os.path.join(BASE_DIR, "torch_npu"))) |
| 457 | os.makedirs(os.path.dirname(dst), exist_ok=True) | 457 | os.makedirs(os.path.dirname(dst), exist_ok=True) |
| 458 | ret.append((src, dst)) | 458 | ret.append((src, dst)) |
| 459 | 459 | ||
| 460 | add_ops_python_files(ret) | 460 | add_ops_python_files(ret) |
| 461 | 461 | ||
| 462 | header_files = [ | 462 | header_files = [ |
| 463 | "torch_npu/csrc/*.h", | 463 | "torch_npu/csrc/*.h", |
| 464 | "torch_npu/csrc/*/*.h", | 464 | "torch_npu/csrc/*/*.h", |
| 465 | "torch_npu/csrc/*/*.hpp", | 465 | "torch_npu/csrc/*/*.hpp", |
| 466 | "torch_npu/csrc/*/*/*.h", | 466 | "torch_npu/csrc/*/*/*.h", |
| 467 | "torch_npu/csrc/*/*/*/*.h", | 467 | "torch_npu/csrc/*/*/*/*.h", |
| 468 | "torch_npu/csrc/*/*/*/*/*.h", | 468 | "torch_npu/csrc/*/*/*/*/*.h", |
| 469 | "third_party/acl/inc/*/*.h", | 469 | "third_party/acl/inc/*/*.h", |
| 470 | "third_party/hccl/inc/*/*.h", | 470 | "third_party/hccl/inc/*/*.h", |
| 471 | "third_party/acl/inc/*/*/*.h", | 471 | "third_party/acl/inc/*/*/*.h", |
| 472 | "torch_npu/csrc/distributed/HCCLUtils.hpp", | 472 | "torch_npu/csrc/distributed/HCCLUtils.hpp", |
| 473 | "torch_npu/csrc/distributed/ProcessGroupHCCL.hpp" | 473 | "torch_npu/csrc/distributed/ProcessGroupHCCL.hpp" |
| 474 | ] | 474 | ] |
| 475 | add_ops_files(BASE_DIR, header_files) | 475 | add_ops_files(BASE_DIR, header_files) |
| 476 | glob_header_files = [] | 476 | glob_header_files = [] |
| 477 | for regex_pattern in header_files: | 477 | for regex_pattern in header_files: |
| 478 | glob_header_files += glob.glob(os.path.join(BASE_DIR, regex_pattern), recursive=True) | 478 | glob_header_files += glob.glob(os.path.join(BASE_DIR, regex_pattern), recursive=True) |
| 479 | 479 | ||
| 480 | for src in glob_header_files: | 480 | for src in glob_header_files: |
| 481 | dst = os.path.join( | 481 | dst = os.path.join( |
| 482 | os.path.join(BASE_DIR, "build/packages/torch_npu/include/torch_npu"), | 482 | os.path.join(BASE_DIR, "build/packages/torch_npu/include/torch_npu"), |
| 483 | os.path.relpath(src, os.path.join(BASE_DIR, "torch_npu"))) | 483 | os.path.relpath(src, os.path.join(BASE_DIR, "torch_npu"))) |
| 484 | os.makedirs(os.path.dirname(dst), exist_ok=True) | 484 | os.makedirs(os.path.dirname(dst), exist_ok=True) |
| 485 | ret.append((src, dst)) | 485 | ret.append((src, dst)) |
| 486 | 486 | ||
| 487 | torch_header_files = [ | 487 | torch_header_files = [ |
| 488 | "*/*.h", | 488 | "*/*.h", |
| 489 | "*/*/*.h", | 489 | "*/*/*.h", |
| 490 | "*/*/*/*.h", | 490 | "*/*/*/*.h", |
| 491 | "*/*/*/*/*.h", | 491 | "*/*/*/*/*.h", |
| 492 | "*/*/*/*/*/*.h" | 492 | "*/*/*/*/*/*.h" |
| 493 | ] | 493 | ] |
| 494 | torch_glob_header_files = [] | 494 | torch_glob_header_files = [] |
| 495 | for regex_pattern in torch_header_files: | 495 | for regex_pattern in torch_header_files: |
| 496 | torch_glob_header_files += glob.glob(os.path.join(BASE_DIR, "patch/include", regex_pattern), recursive=True) | 496 | torch_glob_header_files += glob.glob(os.path.join(BASE_DIR, "patch/include", regex_pattern), recursive=True) |
| 497 | 497 | ||
| 498 | for src in torch_glob_header_files: | 498 | for src in torch_glob_header_files: |
| 499 | dst = os.path.join( | 499 | dst = os.path.join( |
| 500 | os.path.join(BASE_DIR, "build/packages/torch_npu/include"), | 500 | os.path.join(BASE_DIR, "build/packages/torch_npu/include"), |
| 501 | os.path.relpath(src, os.path.join(BASE_DIR, "patch/include"))) | 501 | os.path.relpath(src, os.path.join(BASE_DIR, "patch/include"))) |
| 502 | os.makedirs(os.path.dirname(dst), exist_ok=True) | 502 | os.makedirs(os.path.dirname(dst), exist_ok=True) |
| 503 | ret.append((src, dst)) | 503 | ret.append((src, dst)) |
| 504 | 504 | ||
| 505 | aot_inductor_files = [ | 505 | aot_inductor_files = [ |
| 506 | # Follow torch v2.6.0. | 506 | # Follow torch v2.6.0. |
| 507 | # These aoti_runtime/*.cpp don't compile to libtorch_npu, | 507 | # These aoti_runtime/*.cpp don't compile to libtorch_npu, |
| 508 | # but act like header files when generate cppwrapper in aot-inductor. | 508 | # but act like header files when generate cppwrapper in aot-inductor. |
| 509 | "torch_npu/_inductor/codegen/aoti_runtime/*.cpp" | 509 | "torch_npu/_inductor/codegen/aoti_runtime/*.cpp" |
| 510 | ] | 510 | ] |
| 511 | glob_aoti_files = [] | 511 | glob_aoti_files = [] |
| 512 | for regex_pattern in aot_inductor_files: | 512 | for regex_pattern in aot_inductor_files: |
| 513 | glob_aoti_files += glob.glob( | 513 | glob_aoti_files += glob.glob( |
| 514 | os.path.join(BASE_DIR, regex_pattern), recursive=True | 514 | os.path.join(BASE_DIR, regex_pattern), recursive=True |
| 515 | ) | 515 | ) |
| 516 | 516 | ||
| 517 | for src in glob_aoti_files: | 517 | for src in glob_aoti_files: |
| 518 | # Dst: torch_npu/_inductor/codegen/aoti_runtime/*.cpp | 518 | # Dst: torch_npu/_inductor/codegen/aoti_runtime/*.cpp |
| 519 | dst = os.path.join( | 519 | dst = os.path.join( |
| 520 | os.path.join(BASE_DIR, "build/packages/torch_npu/"), | 520 | os.path.join(BASE_DIR, "build/packages/torch_npu/"), |
| 521 | os.path.relpath(src, os.path.join(BASE_DIR, "torch_npu")), | 521 | os.path.relpath(src, os.path.join(BASE_DIR, "torch_npu")), |
| 522 | ) | 522 | ) |
| 523 | os.makedirs(os.path.dirname(dst), exist_ok=True) | 523 | os.makedirs(os.path.dirname(dst), exist_ok=True) |
| 524 | ret.append((src, dst)) | 524 | ret.append((src, dst)) |
| 525 | 525 | ||
| 526 | def add_torch_npu_codegen(codegen_src_dir, codegen_dst_dir, exclude_root_init=None): | 526 | def add_torch_npu_codegen(codegen_src_dir, codegen_dst_dir, exclude_root_init=None): |
| 527 | """ | 527 | """ |
| 528 | 复制codegen目录下的文件到目标路径 | 528 | 复制codegen目录下的文件到目标路径 |
| 529 | :param codegen_src_dir: 源codegen目录 | 529 | :param codegen_src_dir: 源codegen目录 |
| 530 | :param codegen_dst_dir: 目标目录 | 530 | :param codegen_dst_dir: 目标目录 |
| 531 | :param exclude_root_init: 需要排除根目录__init__.py的源目录(仅过滤该目录下的__init__.py) | 531 | :param exclude_root_init: 需要排除根目录__init__.py的源目录(仅过滤该目录下的__init__.py) |
| 532 | """ | 532 | """ |
| 533 | # 匹配需要复制的文件类型 | 533 | # 匹配需要复制的文件类型 |
| 534 | codegen_files = glob.glob( | 534 | codegen_files = glob.glob( |
| 535 | os.path.join(codegen_src_dir, '**/*.py'), recursive=True | 535 | os.path.join(codegen_src_dir, '**/*.py'), recursive=True |
| 536 | ) + glob.glob( | 536 | ) + glob.glob( |
| 537 | os.path.join(codegen_src_dir, '**/*.yaml'), recursive=True | 537 | os.path.join(codegen_src_dir, '**/*.yaml'), recursive=True |
| 538 | ) + glob.glob( | 538 | ) + glob.glob( |
| 539 | os.path.join(codegen_src_dir, '**/*.json'), recursive=True | 539 | os.path.join(codegen_src_dir, '**/*.json'), recursive=True |
| 540 | ) + glob.glob( | 540 | ) + glob.glob( |
| 541 | os.path.join(codegen_src_dir, '**/*.cpp'), recursive=True | 541 | os.path.join(codegen_src_dir, '**/*.cpp'), recursive=True |
| 542 | ) + glob.glob( | 542 | ) + glob.glob( |
| 543 | os.path.join(codegen_src_dir, '**/*.h'), recursive=True | 543 | os.path.join(codegen_src_dir, '**/*.h'), recursive=True |
| 544 | ) | 544 | ) |
| 545 | 545 | ||
| 546 | # 按原目录结构复制到目标路径 | 546 | # 按原目录结构复制到目标路径 |
| 547 | for src in codegen_files: | 547 | for src in codegen_files: |
| 548 | # 仅过滤指定目录下的根级__init__.py | 548 | # 仅过滤指定目录下的根级__init__.py |
| 549 | if (exclude_root_init is not None and | 549 | if (exclude_root_init is not None and |
| 550 | os.path.basename(src) == '__init__.py' and | 550 | os.path.basename(src) == '__init__.py' and |
| 551 | os.path.dirname(src) == exclude_root_init): | 551 | os.path.dirname(src) == exclude_root_init): |
| 552 | continue # 跳过op-plugin/codegen根目录的__init__.py | 552 | continue # 跳过op-plugin/codegen根目录的__init__.py |
| 553 | 553 | ||
| 554 | # 计算目标路径(保留原目录层级) | 554 | # 计算目标路径(保留原目录层级) |
| 555 | dst = os.path.join( | 555 | dst = os.path.join( |
| 556 | codegen_dst_dir, | 556 | codegen_dst_dir, |
| 557 | os.path.relpath(src, codegen_src_dir) # 保留torchnpugen内部的目录层级 | 557 | os.path.relpath(src, codegen_src_dir) # 保留torchnpugen内部的目录层级 |
| 558 | ) | 558 | ) |
| 559 | print(os.path.relpath(src, codegen_src_dir)) | 559 | print(os.path.relpath(src, codegen_src_dir)) |
| 560 | # 确保目标目录存在 | 560 | # 确保目标目录存在 |
| 561 | os.makedirs(os.path.dirname(dst), exist_ok=True) | 561 | os.makedirs(os.path.dirname(dst), exist_ok=True) |
| 562 | # 加入文件复制列表 | 562 | # 加入文件复制列表 |
| 563 | ret.append((src, dst)) | 563 | ret.append((src, dst)) |
| 564 | 564 | ||
| 565 | # 新增:提前创建 torchnpugen 根目录 | 565 | # 新增:提前创建 torchnpugen 根目录 |
| 566 | torchnpugen_root = os.path.join(BASE_DIR, "build/packages/torchnpugen") | 566 | torchnpugen_root = os.path.join(BASE_DIR, "build/packages/torchnpugen") |
| 567 | os.makedirs(torchnpugen_root, exist_ok=True) | 567 | os.makedirs(torchnpugen_root, exist_ok=True) |
| 568 | # 将codegen复制到package路径 | 568 | # 将codegen复制到package路径 |
| 569 | codegen_src_dir = os.path.join(BASE_DIR, "torchnpugen") | 569 | codegen_src_dir = os.path.join(BASE_DIR, "torchnpugen") |
| 570 | codegen_dst_dir = os.path.join(BASE_DIR, "build/packages/torchnpugen") | 570 | codegen_dst_dir = os.path.join(BASE_DIR, "build/packages/torchnpugen") |
| 571 | # 复制torch_npu的torchnpugen | 571 | # 复制torch_npu的torchnpugen |
| 572 | add_torch_npu_codegen(codegen_src_dir, codegen_dst_dir) | 572 | add_torch_npu_codegen(codegen_src_dir, codegen_dst_dir) |
| 573 | # 复制op-plugin的torchnpugen(仅过滤其根目录的__init__.py) | 573 | # 复制op-plugin的torchnpugen(仅过滤其根目录的__init__.py) |
| 574 | op_plugin_codegen_src = os.path.join(BASE_DIR, "third_party/op-plugin/torchnpugen") | 574 | op_plugin_codegen_src = os.path.join(BASE_DIR, "third_party/op-plugin/torchnpugen") |
| 575 | add_torch_npu_codegen( | 575 | add_torch_npu_codegen( |
| 576 | op_plugin_codegen_src, | 576 | op_plugin_codegen_src, |
| 577 | codegen_dst_dir, | 577 | codegen_dst_dir, |
| 578 | exclude_root_init=op_plugin_codegen_src # 指定要过滤根目录__init__.py的源目录 | 578 | exclude_root_init=op_plugin_codegen_src # 指定要过滤根目录__init__.py的源目录 |
| 579 | ) | 579 | ) |
| 580 | 580 | ||
| 581 | return ret | 581 | return ret |
| 582 | 582 | ||
| 583 | 583 | ||
| 584 | class EggInfoBuild(egg_info, object): | 584 | class EggInfoBuild(egg_info, object): |
| 585 | def finalize_options(self): | 585 | def finalize_options(self): |
| 586 | self.egg_base = os.path.relpath(os.path.join(BASE_DIR, "build/packages")) | 586 | self.egg_base = os.path.relpath(os.path.join(BASE_DIR, "build/packages")) |
| 587 | ret = get_src_py_and_dst() | 587 | ret = get_src_py_and_dst() |
| 588 | for src, dst in ret: | 588 | for src, dst in ret: |
| 589 | self.copy_file(src, dst) | 589 | self.copy_file(src, dst) |
| 590 | super(EggInfoBuild, self).finalize_options() | 590 | super(EggInfoBuild, self).finalize_options() |
| 591 | 591 | ||
| 592 | 592 | ||
| 593 | class PythonPackageBuild(build_py, object): | 593 | class PythonPackageBuild(build_py, object): |
| 594 | def run(self) -> None: | 594 | def run(self) -> None: |
| 595 | ret = get_src_py_and_dst() | 595 | ret = get_src_py_and_dst() |
| 596 | for src, dst in ret: | 596 | for src, dst in ret: |
| 597 | self.copy_file(src, dst) | 597 | self.copy_file(src, dst) |
| 598 | super(PythonPackageBuild, self).finalize_options() | 598 | super(PythonPackageBuild, self).finalize_options() |
| 599 | 599 | ||
| 600 | 600 | ||
| 601 | class BdistWheelBuild(bdist_wheel): | 601 | class BdistWheelBuild(bdist_wheel): |
| 602 | def run(self): | 602 | def run(self): |
| 603 | if which('patchelf') is not None: | 603 | if which('patchelf') is not None: |
| 604 | patchelf_dynamic_library() | 604 | patchelf_dynamic_library() |
| 605 | 605 | ||
| 606 | if not DEBUG and which('eu-strip') is not None: | 606 | if not DEBUG and which('eu-strip') is not None: |
| 607 | generate_dbg_files_and_strip() | 607 | generate_dbg_files_and_strip() |
| 608 | 608 | ||
| 609 | torch_dependencies = ["libc10.so", "libtorch.so", "libtorch_cpu.so", "libtorch_python.so"] | 609 | torch_dependencies = ["libc10.so", "libtorch.so", "libtorch_cpu.so", "libtorch_python.so"] |
| 610 | cann_dependencies = ["libhccl.so", "libascendcl.so", "libacl_op_compiler.so", "libge_runner.so", | 610 | cann_dependencies = ["libhccl.so", "libascendcl.so", "libacl_op_compiler.so", "libge_runner.so", |
| 611 | "libgraph.so", "libacl_tdt_channel.so", "libfmk_parser.so", "libascend_protobuf.so", | 611 | "libgraph.so", "libacl_tdt_channel.so", "libfmk_parser.so", "libascend_protobuf.so", |
| 612 | "libascend_ml.so"] | 612 | "libascend_ml.so"] |
| 613 | other_dependencies = ["libtorch_npu.so", "libnpu_profiler.so", "libgomp.so.1", "libatb.so"] | 613 | other_dependencies = ["libtorch_npu.so", "libnpu_profiler.so", "libgomp.so.1", "libatb.so"] |
| 614 | 614 | ||
| 615 | dependencies = torch_dependencies + cann_dependencies + other_dependencies | 615 | dependencies = torch_dependencies + cann_dependencies + other_dependencies |
| 616 | 616 | ||
| 617 | bdist_wheel.run(self) | 617 | bdist_wheel.run(self) |
| 618 | 618 | ||
| 619 | if is_manylinux: | 619 | if is_manylinux: |
| 620 | file = glob.glob(os.path.join(self.dist_dir, "*linux*.whl"))[0] | 620 | file = glob.glob(os.path.join(self.dist_dir, "*linux*.whl"))[0] |
| 621 | 621 | ||
| 622 | auditwheel_cmd = ["auditwheel", "-v", "repair", "-w", self.dist_dir, file] | 622 | auditwheel_cmd = ["auditwheel", "-v", "repair", "-w", self.dist_dir, file] |
| 623 | for i in dependencies: | 623 | for i in dependencies: |
| 624 | auditwheel_cmd += ["--exclude", i] | 624 | auditwheel_cmd += ["--exclude", i] |
| 625 | 625 | ||
| 626 | try: | 626 | try: |
| 627 | subprocess.run(auditwheel_cmd, check=True, stdout=subprocess.PIPE) | 627 | subprocess.run(auditwheel_cmd, check=True, stdout=subprocess.PIPE) |
| 628 | finally: | 628 | finally: |
| 629 | os.remove(file) | 629 | os.remove(file) |
| 630 | 630 | ||
| 631 | 631 | ||
| 632 | build_mode = _get_build_mode() | 632 | build_mode = _get_build_mode() |
| 633 | if build_mode not in ['clean']: | 633 | if build_mode not in ['clean']: |
| 634 | # Generate bindings code, including RegisterNPU.cpp & NPUNativeFunctions.h. | 634 | # Generate bindings code, including RegisterNPU.cpp & NPUNativeFunctions.h. |
| 635 | generate_bindings_code(BASE_DIR) | 635 | generate_bindings_code(BASE_DIR) |
| 636 | if Path(BASE_DIR).joinpath("third_party/Tensorpipe/third_party/acl/libs").exists(): | 636 | if Path(BASE_DIR).joinpath("third_party/Tensorpipe/third_party/acl/libs").exists(): |
| 637 | build_stub(Path(BASE_DIR).joinpath("third_party/Tensorpipe")) | 637 | build_stub(Path(BASE_DIR).joinpath("third_party/Tensorpipe")) |
| 638 | build_stub(BASE_DIR) | 638 | build_stub(BASE_DIR) |
| 639 | 639 | ||
| 640 | # Setup include directories folders. | 640 | # Setup include directories folders. |
| 641 | include_directories = [ | 641 | include_directories = [ |
| 642 | BASE_DIR, | 642 | BASE_DIR, |
| 643 | os.path.join(BASE_DIR, 'patch/include'), | 643 | os.path.join(BASE_DIR, 'patch/include'), |
| 644 | os.path.join(BASE_DIR, 'third_party/hccl/inc'), | 644 | os.path.join(BASE_DIR, 'third_party/hccl/inc'), |
| 645 | os.path.join(BASE_DIR, 'third_party/acl/inc') | 645 | os.path.join(BASE_DIR, 'third_party/acl/inc') |
| 646 | ] | 646 | ] |
| 647 | 647 | ||
| 648 | extra_link_args = [] | 648 | extra_link_args = [] |
| 649 | 649 | ||
| 650 | DEBUG = (os.getenv('DEBUG', default='').upper() in ['ON', '1', 'YES', 'TRUE', 'Y']) | 650 | DEBUG = (os.getenv('DEBUG', default='').upper() in ['ON', '1', 'YES', 'TRUE', 'Y']) |
| 651 | 651 | ||
| 652 | extra_compile_args = [ | 652 | extra_compile_args = [ |
| 653 | '-std=c++17', | 653 | '-std=c++17', |
| 654 | '-Wno-sign-compare', | 654 | '-Wno-sign-compare', |
| 655 | '-Wno-deprecated-declarations', | 655 | '-Wno-deprecated-declarations', |
| 656 | '-Wno-return-type' | 656 | '-Wno-return-type' |
| 657 | ] | 657 | ] |
| 658 | 658 | ||
| 659 | if re.match(r'clang', os.getenv('CC', '')): | 659 | if re.match(r'clang', os.getenv('CC', '')): |
| 660 | extra_compile_args += [ | 660 | extra_compile_args += [ |
| 661 | '-Wno-macro-redefined', | 661 | '-Wno-macro-redefined', |
| 662 | '-Wno-return-std-move', | 662 | '-Wno-return-std-move', |
| 663 | ] | 663 | ] |
| 664 | 664 | ||
| 665 | if DEBUG: | 665 | if DEBUG: |
| 666 | extra_compile_args += ['-O0', '-g'] | 666 | extra_compile_args += ['-O0', '-g'] |
| 667 | extra_link_args += ['-O0', '-g', '-Wl,-z,now'] | 667 | extra_link_args += ['-O0', '-g', '-Wl,-z,now'] |
| 668 | else: | 668 | else: |
| 669 | extra_compile_args += ['-DNDEBUG'] | 669 | extra_compile_args += ['-DNDEBUG'] |
| 670 | extra_link_args += ['-Wl,-z,now'] | 670 | extra_link_args += ['-Wl,-z,now'] |
| 671 | 671 | ||
| 672 | # valid manylinux tags | 672 | # valid manylinux tags |
| 673 | manylinux_tags = [ | 673 | manylinux_tags = [ |
| 674 | "manylinux1_x86_64", | 674 | "manylinux1_x86_64", |
| 675 | "manylinux2010_x86_64", | 675 | "manylinux2010_x86_64", |
| 676 | "manylinux2014_x86_64", | 676 | "manylinux2014_x86_64", |
| 677 | "manylinux2014_aarch64", | 677 | "manylinux2014_aarch64", |
| 678 | "manylinux_2_5_x86_64", | 678 | "manylinux_2_5_x86_64", |
| 679 | "manylinux_2_12_x86_64", | 679 | "manylinux_2_12_x86_64", |
| 680 | "manylinux_2_17_x86_64", | 680 | "manylinux_2_17_x86_64", |
| 681 | "manylinux_2_17_aarch64", | 681 | "manylinux_2_17_aarch64", |
| 682 | "manylinux_2_24_x86_64", | 682 | "manylinux_2_24_x86_64", |
| 683 | "manylinux_2_24_aarch64", | 683 | "manylinux_2_24_aarch64", |
| 684 | "manylinux_2_27_x86_64", | 684 | "manylinux_2_27_x86_64", |
| 685 | "manylinux_2_27_aarch64", | 685 | "manylinux_2_27_aarch64", |
| 686 | "manylinux_2_28_x86_64", | 686 | "manylinux_2_28_x86_64", |
| 687 | "manylinux_2_28_aarch64", | 687 | "manylinux_2_28_aarch64", |
| 688 | "manylinux_2_31_x86_64", | 688 | "manylinux_2_31_x86_64", |
| 689 | "manylinux_2_31_aarch64", | 689 | "manylinux_2_31_aarch64", |
| 690 | "manylinux_2_34_x86_64", | 690 | "manylinux_2_34_x86_64", |
| 691 | "manylinux_2_34_aarch64", | 691 | "manylinux_2_34_aarch64", |
| 692 | "manylinux_2_35_x86_64" | 692 | "manylinux_2_35_x86_64" |
| 693 | "manylinux_2_35_aarch64", | 693 | "manylinux_2_35_aarch64", |
| 694 | ] | 694 | ] |
| 695 | is_manylinux = os.environ.get("AUDITWHEEL_PLAT", None) in manylinux_tags | 695 | is_manylinux = os.environ.get("AUDITWHEEL_PLAT", None) in manylinux_tags |
| 696 | 696 | ||
| 697 | readme = os.path.join(BASE_DIR, "README.md") | 697 | readme = os.path.join(BASE_DIR, "README.md") |
| 698 | if not os.path.exists(readme): | 698 | if not os.path.exists(readme): |
| 699 | raise FileNotFoundError("Unable to find 'README.md'") | 699 | raise FileNotFoundError("Unable to find 'README.md'") |
| 700 | with open(readme, encoding="utf-8") as fdesc: | 700 | with open(readme, encoding="utf-8") as fdesc: |
| 701 | long_description = fdesc.read() | 701 | long_description = fdesc.read() |
| 702 | 702 | ||
| 703 | classifiers = [ | 703 | classifiers = [ |
| 704 | "Development Status :: 5 - Production/Stable", | 704 | "Development Status :: 5 - Production/Stable", |
| 705 | "Intended Audience :: Developers", | 705 | "Intended Audience :: Developers", |
| 706 | "License :: OSI Approved :: BSD License", | 706 | "License :: OSI Approved :: BSD License", |
| 707 | "Operating System :: POSIX :: Linux", | 707 | "Operating System :: POSIX :: Linux", |
| 708 | "Topic :: Scientific/Engineering", | 708 | "Topic :: Scientific/Engineering", |
| 709 | "Topic :: Scientific/Engineering :: Mathematics", | 709 | "Topic :: Scientific/Engineering :: Mathematics", |
| 710 | "Topic :: Scientific/Engineering :: Artificial Intelligence", | 710 | "Topic :: Scientific/Engineering :: Artificial Intelligence", |
| 711 | "Topic :: Software Development", | 711 | "Topic :: Software Development", |
| 712 | "Topic :: Software Development :: Libraries", | 712 | "Topic :: Software Development :: Libraries", |
| 713 | "Topic :: Software Development :: Libraries :: Python Modules", | 713 | "Topic :: Software Development :: Libraries :: Python Modules", |
| 714 | "Programming Language :: Python", | 714 | "Programming Language :: Python", |
| 715 | "Programming Language :: Python :: 3 :: Only", | 715 | "Programming Language :: Python :: 3 :: Only", |
| 716 | "Programming Language :: Python :: 3.8", | 716 | "Programming Language :: Python :: 3.8", |
| 717 | "Programming Language :: Python :: 3.9", | 717 | "Programming Language :: Python :: 3.9", |
| 718 | "Programming Language :: Python :: 3.10", | 718 | "Programming Language :: Python :: 3.10", |
| 719 | "Programming Language :: Python :: 3.11", | 719 | "Programming Language :: Python :: 3.11", |
| 720 | ] | 720 | ] |
| 721 | 721 | ||
| 722 | requirements = ['torch==2.7.1+cpu' if platform.machine() == 'x86_64' else 'torch==2.7.1'] | 722 | requirements = ['torch==2.7.1+cpu' if platform.machine() == 'x86_64' else 'torch==2.7.1'] |
| 723 | 723 | ||
| 724 | ext_modules = [CppExtension( | 724 | ext_modules = [CppExtension( |
| 725 | 'torch_npu._C', | 725 | 'torch_npu._C', |
| 726 | sources=["torch_npu/csrc/InitNpuBindings.cpp"], | 726 | sources=["torch_npu/csrc/InitNpuBindings.cpp"], |
| 727 | libraries=["torch_npu"], | 727 | libraries=["torch_npu"], |
| 728 | include_dirs=include_directories, | 728 | include_dirs=include_directories, |
| 729 | extra_compile_args=extra_compile_args + ['-fstack-protector-all'] + [ | 729 | extra_compile_args=extra_compile_args + ['-fstack-protector-all'] + [ |
| 730 | '-D__FILENAME__=\"InitNpuBindings.cpp\"'], | 730 | '-D__FILENAME__=\"InitNpuBindings.cpp\"'], |
| 731 | library_dirs=["lib"], | 731 | library_dirs=["lib"], |
| 732 | extra_link_args=extra_link_args + ['-Wl,-rpath,$ORIGIN/lib', '-Wl,-Bsymbolic-functions'], | 732 | extra_link_args=extra_link_args + ['-Wl,-rpath,$ORIGIN/lib', '-Wl,-Bsymbolic-functions'], |
| 733 | define_macros=[('_GLIBCXX_USE_CXX11_ABI', '1' if USE_CXX11_ABI else '0'), | 733 | define_macros=[('_GLIBCXX_USE_CXX11_ABI', '1' if USE_CXX11_ABI else '0'), |
| 734 | ('GLIBCXX_USE_CXX11_ABI', '1' if USE_CXX11_ABI else '0')] | 734 | ('GLIBCXX_USE_CXX11_ABI', '1' if USE_CXX11_ABI else '0')] |
| 735 | )] | 735 | )] |
| 736 | 736 | ||
| 737 | setup( | 737 | setup( |
| 738 | name=os.environ.get('TORCH_NPU_PACKAGE_NAME', 'torch_npu'), | 738 | name=os.environ.get('TORCH_NPU_PACKAGE_NAME', 'torch_npu'), |
| 739 | version=VERSION, | 739 | version=VERSION, |
| 740 | description='NPU bridge for PyTorch', | 740 | description='NPU bridge for PyTorch', |
| 741 | long_description=long_description, | 741 | long_description=long_description, |
| 742 | long_description_content_type="text/markdown", | 742 | long_description_content_type="text/markdown", |
| 743 | license="BSD License", | 743 | license="BSD License", |
| 744 | classifiers=classifiers, | 744 | classifiers=classifiers, |
| 745 | packages=["torch_npu", "torchnpugen"], | 745 | packages=["torch_npu", "torchnpugen"], |
| 746 | libraries=[('torch_npu', {'sources': list()})], | 746 | libraries=[('torch_npu', {'sources': list()})], |
| 747 | package_dir={'': os.path.relpath(os.path.join(BASE_DIR, "build/packages"))}, | 747 | package_dir={'': os.path.relpath(os.path.join(BASE_DIR, "build/packages"))}, |
| 748 | ext_modules=ext_modules, | 748 | ext_modules=ext_modules, |
| 749 | install_requires=requirements, | 749 | install_requires=requirements, |
| 750 | 750 | ||
| 751 | extras_require={ | 751 | extras_require={ |
| 752 | }, | 752 | }, |
| 753 | package_data={ | 753 | package_data={ |
| 754 | 'torch_npu': [ | 754 | 'torch_npu': [ |
| 755 | '*.so', | 755 | '*.so', |
| 756 | 'lib/*.so*', | 756 | 'lib/*.so*', |
| 757 | ], | 757 | ], |
| 758 | 'torchnpugen': [ | 758 | 'torchnpugen': [ |
| 759 | '*.py', '**/*.py', | 759 | '*.py', '**/*.py', |
| 760 | '*.yaml', '**/*.yaml', | 760 | '*.yaml', '**/*.yaml', |
| 761 | '*.json', '**/*.json', | 761 | '*.json', '**/*.json', |
| 762 | '*.cpp', '**/*.cpp', | 762 | '*.cpp', '**/*.cpp', |
| 763 | '*.h', '**/*.h', | 763 | '*.h', '**/*.h', |
| 764 | ], | 764 | ], |
| 765 | }, | 765 | }, |
| 766 | cmdclass={ | 766 | cmdclass={ |
| 767 | 'build_clib': CPPLibBuild, | 767 | 'build_clib': CPPLibBuild, |
| 768 | 'build_ext': Build, | 768 | 'build_ext': Build, |
| 769 | 'build_py': PythonPackageBuild, | 769 | 'build_py': PythonPackageBuild, |
| 770 | 'bdist_wheel': BdistWheelBuild, | 770 | 'bdist_wheel': BdistWheelBuild, |
| 771 | 'install': InstallCmd, | 771 | 'install': InstallCmd, |
| 772 | 'clean': Clean | 772 | 'clean': Clean |
| 773 | }, | 773 | }, |
| 774 | entry_points={ | 774 | entry_points={ |
| 775 | 'console_scripts': [ | 775 | 'console_scripts': [ |
| 776 | 'torch_npu_run = torch_npu.distributed.run:_main', | 776 | 'torch_npu_run = torch_npu.distributed.run:_main', |
| 777 | ], | 777 | ], |
| 778 | 'torch.backends': [ | 778 | 'torch.backends': [ |
| 779 | 'torch_npu = torch_npu:_autoload', | 779 | 'torch_npu = torch_npu:_autoload', |
| 780 | ], | 780 | ], |
| 781 | } | 781 | } |
| 782 | ) | 782 | ) |
| @@ -42,4 +42,3 @@ instantiate_parametrized_tests(TestEmpty) | |||
| 42 | 42 | ||
| 43 | if __name__ == "__main__": | 43 | if __name__ == "__main__": |
| 44 | run_tests() | 44 | run_tests() |
| 45 | |||
| @@ -25,4 +25,3 @@ instantiate_parametrized_tests(TestExp) | |||
| 25 | 25 | ||
| 26 | if __name__ == "__main__": | 26 | if __name__ == "__main__": |
| 27 | run_tests() | 27 | run_tests() |
| 28 | |||
| @@ -1,92 +1,92 @@ | |||
| 1 | import torch | 1 | import torch |
| 2 | import torch.fx as fx | 2 | import torch.fx as fx |
| 3 | from torch.fx.passes.shape_prop import ShapeProp | 3 | from torch.fx.passes.shape_prop import ShapeProp |
| 4 | from torch.testing._internal.common_utils import run_tests, parametrize, instantiate_parametrized_tests | 4 | from torch.testing._internal.common_utils import run_tests, parametrize, instantiate_parametrized_tests |
| 5 | from testutils import TestUtils | 5 | from testutils import TestUtils |
| 6 | import torch_npu | 6 | import torch_npu |
| 7 | import torch_npu._inductor | 7 | import torch_npu._inductor |
| 8 | from torch_npu._inductor.fx_passes.utils.fx_pass_level import FxPassLevel | 8 | from torch_npu._inductor.fx_passes.utils.fx_pass_level import FxPassLevel |
| 9 | from torch_npu._inductor.fx_passes.utils.fx_pass_level import PassType | 9 | from torch_npu._inductor.fx_passes.utils.fx_pass_level import PassType |
| 10 | 10 | ||
| 11 | 11 | ||
| 12 | class TestFxPassLevel(TestUtils): | 12 | class TestFxPassLevel(TestUtils): |
| 13 | 13 | ||
| 14 | def test_comparison_between_members(self): | 14 | def test_comparison_between_members(self): |
| 15 | """枚举成员之间的大小比较""" | 15 | """枚举成员之间的大小比较""" |
| 16 | assert FxPassLevel.LEVEL1 < FxPassLevel.LEVEL2 | 16 | assert FxPassLevel.LEVEL1 < FxPassLevel.LEVEL2 |
| 17 | assert FxPassLevel.LEVEL1 <= FxPassLevel.LEVEL2 | 17 | assert FxPassLevel.LEVEL1 <= FxPassLevel.LEVEL2 |
| 18 | assert FxPassLevel.LEVEL2 > FxPassLevel.LEVEL1 | 18 | assert FxPassLevel.LEVEL2 > FxPassLevel.LEVEL1 |
| 19 | assert FxPassLevel.LEVEL2 >= FxPassLevel.LEVEL1 | 19 | assert FxPassLevel.LEVEL2 >= FxPassLevel.LEVEL1 |
| 20 | assert FxPassLevel.LEVEL2 == FxPassLevel.LEVEL2 | 20 | assert FxPassLevel.LEVEL2 == FxPassLevel.LEVEL2 |
| 21 | assert FxPassLevel.LEVEL1 != FxPassLevel.LEVEL3 | 21 | assert FxPassLevel.LEVEL1 != FxPassLevel.LEVEL3 |
| 22 | 22 | ||
| 23 | # 严格顺序 | 23 | # 严格顺序 |
| 24 | assert FxPassLevel.LEVEL1 < FxPassLevel.LEVEL2 < FxPassLevel.LEVEL3 | 24 | assert FxPassLevel.LEVEL1 < FxPassLevel.LEVEL2 < FxPassLevel.LEVEL3 |
| 25 | 25 | ||
| 26 | 26 | ||
| 27 | def test_sorting_and_min_max(self): | 27 | def test_sorting_and_min_max(self): |
| 28 | """支持排序、min、max 等操作""" | 28 | """支持排序、min、max 等操作""" |
| 29 | levels = [FxPassLevel.LEVEL3, FxPassLevel.LEVEL1, FxPassLevel.LEVEL2] | 29 | levels = [FxPassLevel.LEVEL3, FxPassLevel.LEVEL1, FxPassLevel.LEVEL2] |
| 30 | sorted_levels = sorted(levels) | 30 | sorted_levels = sorted(levels) |
| 31 | assert sorted_levels == [FxPassLevel.LEVEL1, FxPassLevel.LEVEL2, FxPassLevel.LEVEL3] | 31 | assert sorted_levels == [FxPassLevel.LEVEL1, FxPassLevel.LEVEL2, FxPassLevel.LEVEL3] |
| 32 | 32 | ||
| 33 | assert min(levels) == FxPassLevel.LEVEL1 | 33 | assert min(levels) == FxPassLevel.LEVEL1 |
| 34 | assert max(levels) == FxPassLevel.LEVEL3 | 34 | assert max(levels) == FxPassLevel.LEVEL3 |
| 35 | 35 | ||
| 36 | 36 | ||
| 37 | def test_hash_consistency(self): | 37 | def test_hash_consistency(self): |
| 38 | """哈希值一致性""" | 38 | """哈希值一致性""" |
| 39 | assert hash(FxPassLevel.LEVEL1) == hash(1) | 39 | assert hash(FxPassLevel.LEVEL1) == hash(1) |
| 40 | assert hash(FxPassLevel.LEVEL2) == hash(2) | 40 | assert hash(FxPassLevel.LEVEL2) == hash(2) |
| 41 | assert hash(FxPassLevel.LEVEL3) == hash(3) | 41 | assert hash(FxPassLevel.LEVEL3) == hash(3) |
| 42 | 42 | ||
| 43 | # 相同值哈希相同 | 43 | # 相同值哈希相同 |
| 44 | assert hash(FxPassLevel.LEVEL1) == hash(FxPassLevel.LEVEL1) | 44 | assert hash(FxPassLevel.LEVEL1) == hash(FxPassLevel.LEVEL1) |
| 45 | 45 | ||
| 46 | # 不同值哈希不同 | 46 | # 不同值哈希不同 |
| 47 | assert hash(FxPassLevel.LEVEL1) != hash(FxPassLevel.LEVEL2) | 47 | assert hash(FxPassLevel.LEVEL1) != hash(FxPassLevel.LEVEL2) |
| 48 | 48 | ||
| 49 | # 可用于集合 | 49 | # 可用于集合 |
| 50 | s = {FxPassLevel.LEVEL1, FxPassLevel.LEVEL2} | 50 | s = {FxPassLevel.LEVEL1, FxPassLevel.LEVEL2} |
| 51 | assert FxPassLevel.LEVEL1 in s | 51 | assert FxPassLevel.LEVEL1 in s |
| 52 | assert FxPassLevel.LEVEL3 not in s | 52 | assert FxPassLevel.LEVEL3 not in s |
| 53 | 53 | ||
| 54 | 54 | ||
| 55 | class TestPassType(TestUtils): | 55 | class TestPassType(TestUtils): |
| 56 | 56 | ||
| 57 | def test_comparison_between_members(self): | 57 | def test_comparison_between_members(self): |
| 58 | """枚举成员之间的大小比较""" | 58 | """枚举成员之间的大小比较""" |
| 59 | assert PassType.PRE < PassType.POST | 59 | assert PassType.PRE < PassType.POST |
| 60 | assert PassType.PRE <= PassType.POST | 60 | assert PassType.PRE <= PassType.POST |
| 61 | assert PassType.POST > PassType.PRE | 61 | assert PassType.POST > PassType.PRE |
| 62 | assert PassType.POST >= PassType.PRE | 62 | assert PassType.POST >= PassType.PRE |
| 63 | assert PassType.PRE == PassType.PRE | 63 | assert PassType.PRE == PassType.PRE |
| 64 | assert PassType.PRE != PassType.POST | 64 | assert PassType.PRE != PassType.POST |
| 65 | 65 | ||
| 66 | 66 | ||
| 67 | def test_sorting_and_min_max(self): | 67 | def test_sorting_and_min_max(self): |
| 68 | """支持排序、min、max""" | 68 | """支持排序、min、max""" |
| 69 | types = [PassType.POST, PassType.PRE] | 69 | types = [PassType.POST, PassType.PRE] |
| 70 | sorted_types = sorted(types) | 70 | sorted_types = sorted(types) |
| 71 | assert sorted_types == [PassType.PRE, PassType.POST] | 71 | assert sorted_types == [PassType.PRE, PassType.POST] |
| 72 | 72 | ||
| 73 | assert min(types) == PassType.PRE | 73 | assert min(types) == PassType.PRE |
| 74 | assert max(types) == PassType.POST | 74 | assert max(types) == PassType.POST |
| 75 | 75 | ||
| 76 | 76 | ||
| 77 | def test_hash_consistency(self): | 77 | def test_hash_consistency(self): |
| 78 | """哈希值一致性""" | 78 | """哈希值一致性""" |
| 79 | assert hash(PassType.PRE) == hash(1) | 79 | assert hash(PassType.PRE) == hash(1) |
| 80 | assert hash(PassType.POST) == hash(2) | 80 | assert hash(PassType.POST) == hash(2) |
| 81 | 81 | ||
| 82 | s = {PassType.PRE, PassType.POST} | 82 | s = {PassType.PRE, PassType.POST} |
| 83 | assert PassType.PRE in s | 83 | assert PassType.PRE in s |
| 84 | assert PassType.PRE in s # 重复添加不影响 | 84 | assert PassType.PRE in s # 重复添加不影响 |
| 85 | 85 | ||
| 86 | 86 | ||
| 87 | instantiate_parametrized_tests(TestFxPassLevel) | 87 | instantiate_parametrized_tests(TestFxPassLevel) |
| 88 | instantiate_parametrized_tests(TestPassType) | 88 | instantiate_parametrized_tests(TestPassType) |
| 89 | 89 | ||
| 90 | 90 | ||
| 91 | if __name__ == "__main__": | 91 | if __name__ == "__main__": |
| 92 | run_tests() | 92 | run_tests() |
| @@ -29,4 +29,3 @@ instantiate_parametrized_tests(TestGt) | |||
| 29 | 29 | ||
| 30 | if __name__ == "__main__": | 30 | if __name__ == "__main__": |
| 31 | run_tests() | 31 | run_tests() |
| 32 | |||
| @@ -1,816 +1,816 @@ | |||
| 1 | import unittest | 1 | import unittest |
| 2 | from unittest import mock | 2 | from unittest import mock |
| 3 | 3 | ||
| 4 | import torch | 4 | import torch |
| 5 | from torch.nn.parallel import scatter_gather | 5 | from torch.nn.parallel import scatter_gather |
| 6 | from torch.testing._internal.common_utils import ( | 6 | from torch.testing._internal.common_utils import ( |
| 7 | IS_JETSON, | 7 | IS_JETSON, |
| 8 | IS_REMOTE_GPU, | 8 | IS_REMOTE_GPU, |
| 9 | IS_SANDCASTLE, | 9 | IS_SANDCASTLE, |
| 10 | NoTest, | 10 | NoTest, |
| 11 | TEST_PRIVATEUSE1, | 11 | TEST_PRIVATEUSE1, |
| 12 | TestCase, | 12 | TestCase, |
| 13 | instantiate_parametrized_tests, | 13 | instantiate_parametrized_tests, |
| 14 | run_tests, | 14 | run_tests, |
| 15 | skipCUDANonDefaultStreamIf, | 15 | skipCUDANonDefaultStreamIf, |
| 16 | skipIfRocm, | 16 | skipIfRocm, |
| 17 | ) | 17 | ) |
| 18 | import torch_npu | 18 | import torch_npu |
| 19 | import torch_npu._inductor | 19 | import torch_npu._inductor |
| 20 | import torch_npu.testing | 20 | import torch_npu.testing |
| 21 | from torch_npu.testing.common_utils import get_cycles_per_ms | 21 | from torch_npu.testing.common_utils import get_cycles_per_ms |
| 22 | from torch_npu._inductor.shape_handling import unified_copy | 22 | from torch_npu._inductor.shape_handling import unified_copy |
| 23 | import torch_npu._inductor.shape_handling as shape_handling_module | 23 | import torch_npu._inductor.shape_handling as shape_handling_module |
| 24 | 24 | ||
| 25 | torch._dynamo.config.cache_size_limit = 128 | 25 | torch._dynamo.config.cache_size_limit = 128 |
| 26 | 26 | ||
| 27 | if not torch.npu.is_available(): | 27 | if not torch.npu.is_available(): |
| 28 | raise unittest.SkipTest("NPU is not available") | 28 | raise unittest.SkipTest("NPU is not available") |
| 29 | 29 | ||
| 30 | device = "npu" | 30 | device = "npu" |
| 31 | 31 | ||
| 32 | 32 | ||
| 33 | def model_fn(A, B): | 33 | def model_fn(A, B): |
| 34 | return A + B | 34 | return A + B |
| 35 | 35 | ||
| 36 | shape_options = { | 36 | shape_options = { |
| 37 | "enable_shape_handling": True, | 37 | "enable_shape_handling": True, |
| 38 | "shape_handling_configs": [ | 38 | "shape_handling_configs": [ |
| 39 | { | 39 | { |
| 40 | "type": "BATCHSIZE", # 处理的维度类型(Required) | 40 | "type": "BATCHSIZE", # 处理的维度类型(Required) |
| 41 | "dimensions": 0, # 该维度在tensor中的下标(BATCHSIZE默认为0)(Optional) | 41 | "dimensions": 0, # 该维度在tensor中的下标(BATCHSIZE默认为0)(Optional) |
| 42 | # "indices": [0, 1], # 需要处理的tensor下标(默认为所有tensor)(Optional) | 42 | # "indices": [0, 1], # 需要处理的tensor下标(默认为所有tensor)(Optional) |
| 43 | "value": 0.0, # padding时填充的值, 默认为0.0(Optional) | 43 | "value": 0.0, # padding时填充的值, 默认为0.0(Optional) |
| 44 | "gears": [], # 自定义档位信息(Optional) | 44 | "gears": [], # 自定义档位信息(Optional) |
| 45 | "min_size": 1, # 该维度的最小大小(档位), 默认为1(Optional) | 45 | "min_size": 1, # 该维度的最小大小(档位), 默认为1(Optional) |
| 46 | "max_size": 1024, # 该维度的最大大小(档位), 默认为1024(Optional) | 46 | "max_size": 1024, # 该维度的最大大小(档位), 默认为1024(Optional) |
| 47 | "policy": "TIMES", # 依据min_size, max_size自动生成gears的策略, 默认为TIMES, 表示生成范围内2的整数幂档位(Optional) | 47 | "policy": "TIMES", # 依据min_size, max_size自动生成gears的策略, 默认为TIMES, 表示生成范围内2的整数幂档位(Optional) |
| 48 | }, | 48 | }, |
| 49 | { | 49 | { |
| 50 | "type": "SEQLEN", # 处理的维度类型(Required) | 50 | "type": "SEQLEN", # 处理的维度类型(Required) |
| 51 | "dimensions": [1, 1], # 该维度在tensor中的下标, 与indices一一对应, 也可以接收dimension表示所有tensor使用相同的dimension index, 优先接收dimension(SEQLEN默认为1)(Optional) | 51 | "dimensions": [1, 1], # 该维度在tensor中的下标, 与indices一一对应, 也可以接收dimension表示所有tensor使用相同的dimension index, 优先接收dimension(SEQLEN默认为1)(Optional) |
| 52 | # "indices": [0, 1], # 需要处理的tensor下标(默认为所有tensor)(Optional) | 52 | # "indices": [0, 1], # 需要处理的tensor下标(默认为所有tensor)(Optional) |
| 53 | "value": 0.0, # padding时填充的值, 默认为0.0(Optional) | 53 | "value": 0.0, # padding时填充的值, 默认为0.0(Optional) |
| 54 | "gears": [], # 自定义档位信息(Optional) | 54 | "gears": [], # 自定义档位信息(Optional) |
| 55 | "min_size": 1, # 该维度的最小大小(档位), 默认为1(Optional) | 55 | "min_size": 1, # 该维度的最小大小(档位), 默认为1(Optional) |
| 56 | "max_size": 1024, # 该维度的最大大小(档位), 默认为1024(Optional) | 56 | "max_size": 1024, # 该维度的最大大小(档位), 默认为1024(Optional) |
| 57 | "policy": "TIMES", # 依据min_size, max_size自动生成gears的策略, 默认为TIMES, 表示生成范围内2的整数幂档位(Optional) | 57 | "policy": "TIMES", # 依据min_size, max_size自动生成gears的策略, 默认为TIMES, 表示生成范围内2的整数幂档位(Optional) |
| 58 | } | 58 | } |
| 59 | ] | 59 | ] |
| 60 | } | 60 | } |
| 61 | 61 | ||
| 62 | 62 | ||
| 63 | class TestShapeHandling(TestCase): | 63 | class TestShapeHandling(TestCase): |
| 64 | def test_init_no_input(self): | 64 | def test_init_no_input(self): |
| 65 | shape_handling = torch_npu._inductor.NPUShapeHandling() | 65 | shape_handling = torch_npu._inductor.NPUShapeHandling() |
| 66 | self.assertNotEqual(shape_handling, None) | 66 | self.assertNotEqual(shape_handling, None) |
| 67 | 67 | ||
| 68 | def test_init_with_empty_conifg(self): | 68 | def test_init_with_empty_conifg(self): |
| 69 | configs = [] | 69 | configs = [] |
| 70 | shape_handling = torch_npu._inductor.NPUShapeHandling(configs) | 70 | shape_handling = torch_npu._inductor.NPUShapeHandling(configs) |
| 71 | self.assertNotEqual(shape_handling, None) | 71 | self.assertNotEqual(shape_handling, None) |
| 72 | 72 | ||
| 73 | def test_init_with_gears(self): | 73 | def test_init_with_gears(self): |
| 74 | configs = [ | 74 | configs = [ |
| 75 | { | 75 | { |
| 76 | "type": "BATCHSIZE", | 76 | "type": "BATCHSIZE", |
| 77 | "gears": [16, 32, 64] | 77 | "gears": [16, 32, 64] |
| 78 | }, | 78 | }, |
| 79 | { | 79 | { |
| 80 | "type": "SEQLEN", | 80 | "type": "SEQLEN", |
| 81 | "gears": [16, 32, 64] | 81 | "gears": [16, 32, 64] |
| 82 | } | 82 | } |
| 83 | ] | 83 | ] |
| 84 | shape_handling = torch_npu._inductor.NPUShapeHandling(configs) | 84 | shape_handling = torch_npu._inductor.NPUShapeHandling(configs) |
| 85 | self.assertNotEqual(shape_handling, None) | 85 | self.assertNotEqual(shape_handling, None) |
| 86 | 86 | ||
| 87 | def test_init_with_policy(self): | 87 | def test_init_with_policy(self): |
| 88 | configs = [ | 88 | configs = [ |
| 89 | { | 89 | { |
| 90 | "type": "BATCHSIZE", | 90 | "type": "BATCHSIZE", |
| 91 | "min_size": 2, | 91 | "min_size": 2, |
| 92 | "max_size": 8, | 92 | "max_size": 8, |
| 93 | "policy": "TIMES" | 93 | "policy": "TIMES" |
| 94 | }, | 94 | }, |
| 95 | { | 95 | { |
| 96 | "type": "SEQLEN", | 96 | "type": "SEQLEN", |
| 97 | "min_size": 2, | 97 | "min_size": 2, |
| 98 | "max_size": 8, | 98 | "max_size": 8, |
| 99 | "policy": "TIMES" | 99 | "policy": "TIMES" |
| 100 | } | 100 | } |
| 101 | ] | 101 | ] |
| 102 | shape_handling = torch_npu._inductor.NPUShapeHandling(configs) | 102 | shape_handling = torch_npu._inductor.NPUShapeHandling(configs) |
| 103 | self.assertNotEqual(shape_handling, None) | 103 | self.assertNotEqual(shape_handling, None) |
| 104 | 104 | ||
| 105 | def test_transform_no_operation(self): | 105 | def test_transform_no_operation(self): |
| 106 | configs = [ | 106 | configs = [ |
| 107 | { | 107 | { |
| 108 | "type": "BATCHSIZE", | 108 | "type": "BATCHSIZE", |
| 109 | "gears": [32], | 109 | "gears": [32], |
| 110 | "dimensions": 0 | 110 | "dimensions": 0 |
| 111 | }, | 111 | }, |
| 112 | { | 112 | { |
| 113 | "type": "SEQLEN", | 113 | "type": "SEQLEN", |
| 114 | "gears": [128], | 114 | "gears": [128], |
| 115 | "dimensions": [1] | 115 | "dimensions": [1] |
| 116 | } | 116 | } |
| 117 | ] | 117 | ] |
| 118 | shape_handling = torch_npu._inductor.NPUShapeHandling(configs) | 118 | shape_handling = torch_npu._inductor.NPUShapeHandling(configs) |
| 119 | input_tensor = torch.randn(32, 128) | 119 | input_tensor = torch.randn(32, 128) |
| 120 | outputs = shape_handling.transform([input_tensor]) | 120 | outputs = shape_handling.transform([input_tensor]) |
| 121 | self.assertEqual(outputs[0][0].shape, input_tensor.shape) | 121 | self.assertEqual(outputs[0][0].shape, input_tensor.shape) |
| 122 | 122 | ||
| 123 | def test_transform_padding(self): | 123 | def test_transform_padding(self): |
| 124 | configs = [ | 124 | configs = [ |
| 125 | { | 125 | { |
| 126 | "type": "BATCHSIZE", | 126 | "type": "BATCHSIZE", |
| 127 | "min_size": 16, | 127 | "min_size": 16, |
| 128 | "max_size": 256, | 128 | "max_size": 256, |
| 129 | "policy": "TIMES", | 129 | "policy": "TIMES", |
| 130 | "dimensions": 0 | 130 | "dimensions": 0 |
| 131 | }, | 131 | }, |
| 132 | { | 132 | { |
| 133 | "type": "SEQLEN", | 133 | "type": "SEQLEN", |
| 134 | "min_size": 16, | 134 | "min_size": 16, |
| 135 | "max_size": 256, | 135 | "max_size": 256, |
| 136 | "policy": "TIMES", | 136 | "policy": "TIMES", |
| 137 | "dimensions": [1] | 137 | "dimensions": [1] |
| 138 | } | 138 | } |
| 139 | ] | 139 | ] |
| 140 | shape_handling = torch_npu._inductor.NPUShapeHandling(configs) | 140 | shape_handling = torch_npu._inductor.NPUShapeHandling(configs) |
| 141 | input_tensor = torch.randn(48, 96) | 141 | input_tensor = torch.randn(48, 96) |
| 142 | outputs = shape_handling.transform([input_tensor]) | 142 | outputs = shape_handling.transform([input_tensor]) |
| 143 | self.assertEqual(outputs[0][0].shape, (64, 128)) | 143 | self.assertEqual(outputs[0][0].shape, (64, 128)) |
| 144 | 144 | ||
| 145 | def test_transform_padding_with_indices(self): | 145 | def test_transform_padding_with_indices(self): |
| 146 | configs = [ | 146 | configs = [ |
| 147 | { | 147 | { |
| 148 | "type": "BATCHSIZE", | 148 | "type": "BATCHSIZE", |
| 149 | "min_size": 16, | 149 | "min_size": 16, |
| 150 | "max_size": 256, | 150 | "max_size": 256, |
| 151 | "policy": "TIMES", | 151 | "policy": "TIMES", |
| 152 | "dimensions": 0, | 152 | "dimensions": 0, |
| 153 | "indices": [0] | 153 | "indices": [0] |
| 154 | }, | 154 | }, |
| 155 | { | 155 | { |
| 156 | "type": "SEQLEN", | 156 | "type": "SEQLEN", |
| 157 | "min_size": 16, | 157 | "min_size": 16, |
| 158 | "max_size": 256, | 158 | "max_size": 256, |
| 159 | "policy": "TIMES", | 159 | "policy": "TIMES", |
| 160 | "dimensions": [1], | 160 | "dimensions": [1], |
| 161 | "indices": [0] | 161 | "indices": [0] |
| 162 | } | 162 | } |
| 163 | ] | 163 | ] |
| 164 | shape_handling = torch_npu._inductor.NPUShapeHandling(configs) | 164 | shape_handling = torch_npu._inductor.NPUShapeHandling(configs) |
| 165 | input_tensor1 = torch.randn(48, 96) | 165 | input_tensor1 = torch.randn(48, 96) |
| 166 | input_tensor2 = torch.randn(48, 96) | 166 | input_tensor2 = torch.randn(48, 96) |
| 167 | outputs = shape_handling.transform([input_tensor1, input_tensor2]) | 167 | outputs = shape_handling.transform([input_tensor1, input_tensor2]) |
| 168 | self.assertEqual(outputs[0][0].shape, (64, 128)) | 168 | self.assertEqual(outputs[0][0].shape, (64, 128)) |
| 169 | self.assertEqual(outputs[0][1].shape, (48, 96)) | 169 | self.assertEqual(outputs[0][1].shape, (48, 96)) |
| 170 | 170 | ||
| 171 | def test_transform_split(self): | 171 | def test_transform_split(self): |
| 172 | """测试分割操作""" | 172 | """测试分割操作""" |
| 173 | # 设置max_size为128,那么超过128的会被分割 | 173 | # 设置max_size为128,那么超过128的会被分割 |
| 174 | configs = [ | 174 | configs = [ |
| 175 | { | 175 | { |
| 176 | "type": "BATCHSIZE", | 176 | "type": "BATCHSIZE", |
| 177 | "gears": [64, 128], | 177 | "gears": [64, 128], |
| 178 | "dimensions": 0 | 178 | "dimensions": 0 |
| 179 | }, | 179 | }, |
| 180 | { | 180 | { |
| 181 | "type": "SEQLEN", | 181 | "type": "SEQLEN", |
| 182 | "gears": [64, 128], | 182 | "gears": [64, 128], |
| 183 | "dimensions": [1] | 183 | "dimensions": [1] |
| 184 | } | 184 | } |
| 185 | ] | 185 | ] |
| 186 | shape_handling = torch_npu._inductor.NPUShapeHandling(configs) | 186 | shape_handling = torch_npu._inductor.NPUShapeHandling(configs) |
| 187 | input_tensor = torch.randn(200, 96) # 超过max_size | 187 | input_tensor = torch.randn(200, 96) # 超过max_size |
| 188 | outputs = shape_handling.transform([input_tensor]) | 188 | outputs = shape_handling.transform([input_tensor]) |
| 189 | # 验证分割结果:分割为两个组,第一段128,第二段72,再填充为128 | 189 | # 验证分割结果:分割为两个组,第一段128,第二段72,再填充为128 |
| 190 | self.assertEqual(len(outputs), 2) # 分割为两个组 | 190 | self.assertEqual(len(outputs), 2) # 分割为两个组 |
| 191 | self.assertEqual(outputs[0][0].shape, (128, 128)) # 第一段 | 191 | self.assertEqual(outputs[0][0].shape, (128, 128)) # 第一段 |
| 192 | self.assertEqual(outputs[1][0].shape, (128, 128)) # 第二段 | 192 | self.assertEqual(outputs[1][0].shape, (128, 128)) # 第二段 |
| 193 | 193 | ||
| 194 | def test_recover_padding(self): | 194 | def test_recover_padding(self): |
| 195 | """测试恢复填充的张量""" | 195 | """测试恢复填充的张量""" |
| 196 | configs = [ | 196 | configs = [ |
| 197 | { | 197 | { |
| 198 | "type": "BATCHSIZE", | 198 | "type": "BATCHSIZE", |
| 199 | "min_size": 16, | 199 | "min_size": 16, |
| 200 | "max_size": 256, | 200 | "max_size": 256, |
| 201 | "policy": "TIMES", | 201 | "policy": "TIMES", |
| 202 | "dimensions": 0, | 202 | "dimensions": 0, |
| 203 | "indices": [0] | 203 | "indices": [0] |
| 204 | }, | 204 | }, |
| 205 | { | 205 | { |
| 206 | "type": "SEQLEN", | 206 | "type": "SEQLEN", |
| 207 | "min_size": 16, | 207 | "min_size": 16, |
| 208 | "max_size": 256, | 208 | "max_size": 256, |
| 209 | "policy": "TIMES", | 209 | "policy": "TIMES", |
| 210 | "dimensions": [1], | 210 | "dimensions": [1], |
| 211 | "indices": [0] | 211 | "indices": [0] |
| 212 | } | 212 | } |
| 213 | ] | 213 | ] |
| 214 | shape_handling = torch_npu._inductor.NPUShapeHandling(configs) | 214 | shape_handling = torch_npu._inductor.NPUShapeHandling(configs) |
| 215 | orig_tensor = torch.randn(48, 128) | 215 | orig_tensor = torch.randn(48, 128) |
| 216 | padded_group = shape_handling.transform([orig_tensor]) | 216 | padded_group = shape_handling.transform([orig_tensor]) |
| 217 | # 执行恢复 | 217 | # 执行恢复 |
| 218 | recovered = shape_handling.recover(padded_group) | 218 | recovered = shape_handling.recover(padded_group) |
| 219 | # 验证尺寸还原 | 219 | # 验证尺寸还原 |
| 220 | self.assertEqual(recovered[0].shape, orig_tensor.shape) | 220 | self.assertEqual(recovered[0].shape, orig_tensor.shape) |
| 221 | # 验证数据一致性 | 221 | # 验证数据一致性 |
| 222 | self.assertTrue(torch.allclose(recovered[0], orig_tensor)) | 222 | self.assertTrue(torch.allclose(recovered[0], orig_tensor)) |
| 223 | 223 | ||
| 224 | def test_recover_split(self): | 224 | def test_recover_split(self): |
| 225 | """测试恢复分割的张量组""" | 225 | """测试恢复分割的张量组""" |
| 226 | configs = [ | 226 | configs = [ |
| 227 | { | 227 | { |
| 228 | "type": "BATCHSIZE", | 228 | "type": "BATCHSIZE", |
| 229 | "min_size": 16, | 229 | "min_size": 16, |
| 230 | "max_size": 128, | 230 | "max_size": 128, |
| 231 | "policy": "TIMES", | 231 | "policy": "TIMES", |
| 232 | "dimensions": 0, | 232 | "dimensions": 0, |
| 233 | "indices": [0] | 233 | "indices": [0] |
| 234 | }, | 234 | }, |
| 235 | { | 235 | { |
| 236 | "type": "SEQLEN", | 236 | "type": "SEQLEN", |
| 237 | "min_size": 16, | 237 | "min_size": 16, |
| 238 | "max_size": 256, | 238 | "max_size": 256, |
| 239 | "policy": "TIMES", | 239 | "policy": "TIMES", |
| 240 | "dimensions": [1], | 240 | "dimensions": [1], |
| 241 | "indices": [0] | 241 | "indices": [0] |
| 242 | } | 242 | } |
| 243 | ] | 243 | ] |
| 244 | shape_handling = torch_npu._inductor.NPUShapeHandling(configs) | 244 | shape_handling = torch_npu._inductor.NPUShapeHandling(configs) |
| 245 | orig_tensor = torch.randn(200, 128) | 245 | orig_tensor = torch.randn(200, 128) |
| 246 | split_groups = shape_handling.transform([orig_tensor]) | 246 | split_groups = shape_handling.transform([orig_tensor]) |
| 247 | # 执行恢复 | 247 | # 执行恢复 |
| 248 | recovered = shape_handling.recover(split_groups) | 248 | recovered = shape_handling.recover(split_groups) |
| 249 | # 验证拼接还原 | 249 | # 验证拼接还原 |
| 250 | self.assertEqual(recovered[0].shape, orig_tensor.shape) | 250 | self.assertEqual(recovered[0].shape, orig_tensor.shape) |
| 251 | self.assertTrue(torch.allclose(recovered[0], orig_tensor)) | 251 | self.assertTrue(torch.allclose(recovered[0], orig_tensor)) |
| 252 | 252 | ||
| 253 | def test_invalid_dim(self): | 253 | def test_invalid_dim(self): |
| 254 | """测试无效维度异常""" | 254 | """测试无效维度异常""" |
| 255 | configs = [ | 255 | configs = [ |
| 256 | { | 256 | { |
| 257 | "type": "BATCHSIZE", | 257 | "type": "BATCHSIZE", |
| 258 | "min_size": 16, | 258 | "min_size": 16, |
| 259 | "max_size": 128, | 259 | "max_size": 128, |
| 260 | "policy": "TIMES", | 260 | "policy": "TIMES", |
| 261 | "dimensions": 3, | 261 | "dimensions": 3, |
| 262 | "indices": [0] | 262 | "indices": [0] |
| 263 | } | 263 | } |
| 264 | ] | 264 | ] |
| 265 | shape_handling = torch_npu._inductor.NPUShapeHandling(configs) | 265 | shape_handling = torch_npu._inductor.NPUShapeHandling(configs) |
| 266 | input_tensor = torch.randn(32, 128) | 266 | input_tensor = torch.randn(32, 128) |
| 267 | # 验证越界维度触发异常 | 267 | # 验证越界维度触发异常 |
| 268 | with self.assertRaises(RuntimeError): | 268 | with self.assertRaises(RuntimeError): |
| 269 | shape_handling.transform([input_tensor]) # 有效维度应为0或1 | 269 | shape_handling.transform([input_tensor]) # 有效维度应为0或1 |
| 270 | 270 | ||
| 271 | def test_register_custom_strategy(self): | 271 | def test_register_custom_strategy(self): |
| 272 | """测试注册自定义策略""" | 272 | """测试注册自定义策略""" |
| 273 | # 创建自定义策略类 | 273 | # 创建自定义策略类 |
| 274 | class CustomBsShapeOp(torch_npu._C._BSShapeOpStrategy): | 274 | class CustomBsShapeOp(torch_npu._C._BSShapeOpStrategy): |
| 275 | def __init__(self): | 275 | def __init__(self): |
| 276 | super().__init__() | 276 | super().__init__() |
| 277 | self.transform_called = False | 277 | self.transform_called = False |
| 278 | self.recover_called = False | 278 | self.recover_called = False |
| 279 | 279 | ||
| 280 | def Transform(self, inputs, outputs): | 280 | def Transform(self, inputs, outputs): |
| 281 | self.transform_called = True | 281 | self.transform_called = True |
| 282 | # 简单实现:直接复制输入到输出 | 282 | # 简单实现:直接复制输入到输出 |
| 283 | if inputs: | 283 | if inputs: |
| 284 | outputs.append([tensor.clone() for tensor in inputs]) | 284 | outputs.append([tensor.clone() for tensor in inputs]) |
| 285 | 285 | ||
| 286 | def Recover(self, inputs, outputs): | 286 | def Recover(self, inputs, outputs): |
| 287 | self.recover_called = True | 287 | self.recover_called = True |
| 288 | # 简单实现:直接复制第一个组的第一个张量 | 288 | # 简单实现:直接复制第一个组的第一个张量 |
| 289 | if inputs and inputs[0]: | 289 | if inputs and inputs[0]: |
| 290 | outputs.append(inputs[0][0].clone()) | 290 | outputs.append(inputs[0][0].clone()) |
| 291 | 291 | ||
| 292 | class CustomSeqShapeOp(torch_npu._C._SeqShapeOpStrategy): | 292 | class CustomSeqShapeOp(torch_npu._C._SeqShapeOpStrategy): |
| 293 | def __init__(self): | 293 | def __init__(self): |
| 294 | super().__init__() | 294 | super().__init__() |
| 295 | self.transform_called = False | 295 | self.transform_called = False |
| 296 | self.recover_called = False | 296 | self.recover_called = False |
| 297 | 297 | ||
| 298 | def Transform(self, inputs, outputs): | 298 | def Transform(self, inputs, outputs): |
| 299 | self.transform_called = True | 299 | self.transform_called = True |
| 300 | # 简单实现:直接复制输入到输出 | 300 | # 简单实现:直接复制输入到输出 |
| 301 | if inputs: | 301 | if inputs: |
| 302 | outputs.append([tensor.clone() for tensor in inputs]) | 302 | outputs.append([tensor.clone() for tensor in inputs]) |
| 303 | 303 | ||
| 304 | configs = [ | 304 | configs = [ |
| 305 | { | 305 | { |
| 306 | "type": "BATCHSIZE", | 306 | "type": "BATCHSIZE", |
| 307 | "min_size": 16, | 307 | "min_size": 16, |
| 308 | "max_size": 128, | 308 | "max_size": 128, |
| 309 | "policy": "TIMES", | 309 | "policy": "TIMES", |
| 310 | "dimensions": 0, | 310 | "dimensions": 0, |
| 311 | "indices": [0] | 311 | "indices": [0] |
| 312 | }, | 312 | }, |
| 313 | { | 313 | { |
| 314 | "type": "SEQLEN", | 314 | "type": "SEQLEN", |
| 315 | "min_size": 16, | 315 | "min_size": 16, |
| 316 | "max_size": 256, | 316 | "max_size": 256, |
| 317 | "policy": "TIMES", | 317 | "policy": "TIMES", |
| 318 | "dimensions": [1], | 318 | "dimensions": [1], |
| 319 | "indices": [0] | 319 | "indices": [0] |
| 320 | } | 320 | } |
| 321 | ] | 321 | ] |
| 322 | shape_handling = torch_npu._inductor.NPUShapeHandling(configs) | 322 | shape_handling = torch_npu._inductor.NPUShapeHandling(configs) |
| 323 | custom_bs_strategy = CustomBsShapeOp() | 323 | custom_bs_strategy = CustomBsShapeOp() |
| 324 | custom_seq_strategy = CustomSeqShapeOp() | 324 | custom_seq_strategy = CustomSeqShapeOp() |
| 325 | 325 | ||
| 326 | # 注册自定义策略 | 326 | # 注册自定义策略 |
| 327 | shape_handling.register_batch_size_strategy(custom_bs_strategy) | 327 | shape_handling.register_batch_size_strategy(custom_bs_strategy) |
| 328 | shape_handling.register_sequence_strategy(custom_seq_strategy) | 328 | shape_handling.register_sequence_strategy(custom_seq_strategy) |
| 329 | 329 | ||
| 330 | # 验证自定义策略被调用 | 330 | # 验证自定义策略被调用 |
| 331 | input_tensor = torch.randn(32, 128) | 331 | input_tensor = torch.randn(32, 128) |
| 332 | shape_handling.transform([input_tensor]) | 332 | shape_handling.transform([input_tensor]) |
| 333 | 333 | ||
| 334 | self.assertTrue(custom_bs_strategy.transform_called) | 334 | self.assertTrue(custom_bs_strategy.transform_called) |
| 335 | self.assertTrue(custom_seq_strategy.transform_called) | 335 | self.assertTrue(custom_seq_strategy.transform_called) |
| 336 | 336 | ||
| 337 | 337 | ||
| 338 | 338 | ||
| 339 | class TestShapeHandlingBranchCoverage(TestCase): | 339 | class TestShapeHandlingBranchCoverage(TestCase): |
| 340 | def test_validate_configs_error_branches(self): | 340 | def test_validate_configs_error_branches(self): |
| 341 | shape_handling = torch_npu._inductor.NPUShapeHandling() | 341 | shape_handling = torch_npu._inductor.NPUShapeHandling() |
| 342 | 342 | ||
| 343 | with self.assertRaises(ValueError): | 343 | with self.assertRaises(ValueError): |
| 344 | shape_handling._validate_configs([{}]) | 344 | shape_handling._validate_configs([{}]) |
| 345 | 345 | ||
| 346 | with self.assertRaises(ValueError): | 346 | with self.assertRaises(ValueError): |
| 347 | shape_handling._validate_configs([{"type": 1}]) | 347 | shape_handling._validate_configs([{"type": 1}]) |
| 348 | 348 | ||
| 349 | with self.assertRaises(ValueError): | 349 | with self.assertRaises(ValueError): |
| 350 | shape_handling._validate_configs([{"type": "UNKNOWN"}]) | 350 | shape_handling._validate_configs([{"type": "UNKNOWN"}]) |
| 351 | 351 | ||
| 352 | with self.assertRaises(ValueError): | 352 | with self.assertRaises(ValueError): |
| 353 | shape_handling._validate_configs([{"type": "BATCHSIZE", "dimensions": "0"}]) | 353 | shape_handling._validate_configs([{"type": "BATCHSIZE", "dimensions": "0"}]) |
| 354 | 354 | ||
| 355 | with self.assertRaises(ValueError): | 355 | with self.assertRaises(ValueError): |
| 356 | shape_handling._validate_configs([{"type": "BATCHSIZE", "dimensions": [0, "1"]}]) | 356 | shape_handling._validate_configs([{"type": "BATCHSIZE", "dimensions": [0, "1"]}]) |
| 357 | 357 | ||
| 358 | with self.assertRaises(ValueError): | 358 | with self.assertRaises(ValueError): |
| 359 | shape_handling._validate_configs([{"type": "BATCHSIZE", "min_size": 1.5}]) | 359 | shape_handling._validate_configs([{"type": "BATCHSIZE", "min_size": 1.5}]) |
| 360 | 360 | ||
| 361 | with self.assertRaises(ValueError): | 361 | with self.assertRaises(ValueError): |
| 362 | shape_handling._validate_configs([{"type": "BATCHSIZE", "value": "bad"}]) | 362 | shape_handling._validate_configs([{"type": "BATCHSIZE", "value": "bad"}]) |
| 363 | 363 | ||
| 364 | with self.assertRaises(ValueError): | 364 | with self.assertRaises(ValueError): |
| 365 | shape_handling._validate_configs([{"type": "BATCHSIZE", "policy": 1}]) | 365 | shape_handling._validate_configs([{"type": "BATCHSIZE", "policy": 1}]) |
| 366 | 366 | ||
| 367 | with self.assertRaises(ValueError): | 367 | with self.assertRaises(ValueError): |
| 368 | shape_handling._validate_configs([{"type": "BATCHSIZE", "policy": "BAD"}]) | 368 | shape_handling._validate_configs([{"type": "BATCHSIZE", "policy": "BAD"}]) |
| 369 | 369 | ||
| 370 | with self.assertRaises(ValueError): | 370 | with self.assertRaises(ValueError): |
| 371 | shape_handling._validate_configs([{"type": "BATCHSIZE"}, {"type": "BATCHSIZE"}]) | 371 | shape_handling._validate_configs([{"type": "BATCHSIZE"}, {"type": "BATCHSIZE"}]) |
| 372 | 372 | ||
| 373 | with self.assertRaises(ValueError): | 373 | with self.assertRaises(ValueError): |
| 374 | shape_handling._validate_configs( | 374 | shape_handling._validate_configs( |
| 375 | [{"type": "BATCHSIZE"}, {"type": "SEQLEN"}, {"type": "BATCHSIZE"}] | 375 | [{"type": "BATCHSIZE"}, {"type": "SEQLEN"}, {"type": "BATCHSIZE"}] |
| 376 | ) | 376 | ) |
| 377 | 377 | ||
| 378 | def test_construct_indices_branches(self): | 378 | def test_construct_indices_branches(self): |
| 379 | shape_handling = torch_npu._inductor.NPUShapeHandling() | 379 | shape_handling = torch_npu._inductor.NPUShapeHandling() |
| 380 | tensors = [torch.randn(2, 3), torch.randn(2)] | 380 | tensors = [torch.randn(2, 3), torch.randn(2)] |
| 381 | 381 | ||
| 382 | bs_indices = shape_handling._construct_indices(tensors, [], "BATCHSIZE") | 382 | bs_indices = shape_handling._construct_indices(tensors, [], "BATCHSIZE") |
| 383 | self.assertEqual(bs_indices, [0, 1]) | 383 | self.assertEqual(bs_indices, [0, 1]) |
| 384 | 384 | ||
| 385 | seq_indices_default = shape_handling._construct_indices(tensors, [], "SEQLEN") | 385 | seq_indices_default = shape_handling._construct_indices(tensors, [], "SEQLEN") |
| 386 | self.assertEqual(seq_indices_default, [0]) | 386 | self.assertEqual(seq_indices_default, [0]) |
| 387 | 387 | ||
| 388 | seq_indices_dim0 = shape_handling._construct_indices(tensors, [0], "SEQLEN") | 388 | seq_indices_dim0 = shape_handling._construct_indices(tensors, [0], "SEQLEN") |
| 389 | self.assertEqual(seq_indices_dim0, [0, 1]) | 389 | self.assertEqual(seq_indices_dim0, [0, 1]) |
| 390 | 390 | ||
| 391 | def test_delay_initialize_builds_missing_indices(self): | 391 | def test_delay_initialize_builds_missing_indices(self): |
| 392 | configs = [ | 392 | configs = [ |
| 393 | { | 393 | { |
| 394 | "type": "BATCHSIZE", | 394 | "type": "BATCHSIZE", |
| 395 | "dimensions": [0], | 395 | "dimensions": [0], |
| 396 | "indices": [], | 396 | "indices": [], |
| 397 | "gears": [8] | 397 | "gears": [8] |
| 398 | }, | 398 | }, |
| 399 | { | 399 | { |
| 400 | "type": "SEQLEN", | 400 | "type": "SEQLEN", |
| 401 | "dimensions": [1], | 401 | "dimensions": [1], |
| 402 | "indices": [0], | 402 | "indices": [0], |
| 403 | "gears": [8] | 403 | "gears": [8] |
| 404 | } | 404 | } |
| 405 | ] | 405 | ] |
| 406 | shape_handling = torch_npu._inductor.NPUShapeHandling(configs) | 406 | shape_handling = torch_npu._inductor.NPUShapeHandling(configs) |
| 407 | self.assertTrue(shape_handling.delay_init) | 407 | self.assertTrue(shape_handling.delay_init) |
| 408 | 408 | ||
| 409 | shape_handling.delay_initialize([torch.randn(4, 5)]) | 409 | shape_handling.delay_initialize([torch.randn(4, 5)]) |
| 410 | self.assertEqual(shape_handling.configs[0]["indices"], [0]) | 410 | self.assertEqual(shape_handling.configs[0]["indices"], [0]) |
| 411 | self.assertEqual(shape_handling.configs[1]["indices"], [0]) | 411 | self.assertEqual(shape_handling.configs[1]["indices"], [0]) |
| 412 | self.assertFalse(shape_handling.delay_init) | 412 | self.assertFalse(shape_handling.delay_init) |
| 413 | 413 | ||
| 414 | def test_flatten_and_unflatten_with_and_without_tensor(self): | 414 | def test_flatten_and_unflatten_with_and_without_tensor(self): |
| 415 | shape_handling = torch_npu._inductor.NPUShapeHandling() | 415 | shape_handling = torch_npu._inductor.NPUShapeHandling() |
| 416 | 416 | ||
| 417 | tensors, indices, leaves, spec = shape_handling.flatten_to_tensors({"a": 1, "b": "x"}) | 417 | tensors, indices, leaves, spec = shape_handling.flatten_to_tensors({"a": 1, "b": "x"}) |
| 418 | self.assertEqual(list(tensors), []) | 418 | self.assertEqual(list(tensors), []) |
| 419 | self.assertEqual(list(indices), []) | 419 | self.assertEqual(list(indices), []) |
| 420 | self.assertEqual(len(leaves), 2) | 420 | self.assertEqual(len(leaves), 2) |
| 421 | self.assertIsNotNone(spec) | 421 | self.assertIsNotNone(spec) |
| 422 | 422 | ||
| 423 | structure = ((torch.tensor([1.0]), "k"), {"v": torch.tensor([2.0])}) | 423 | structure = ((torch.tensor([1.0]), "k"), {"v": torch.tensor([2.0])}) |
| 424 | tensors, indices, leaves, spec = shape_handling.flatten_to_tensors(structure) | 424 | tensors, indices, leaves, spec = shape_handling.flatten_to_tensors(structure) |
| 425 | rebuilt = shape_handling.unflatten_from_tensors( | 425 | rebuilt = shape_handling.unflatten_from_tensors( |
| 426 | [torch.tensor([10.0]), torch.tensor([20.0])], indices, leaves, spec | 426 | [torch.tensor([10.0]), torch.tensor([20.0])], indices, leaves, spec |
| 427 | ) | 427 | ) |
| 428 | self.assertEqual(rebuilt[0][1], "k") | 428 | self.assertEqual(rebuilt[0][1], "k") |
| 429 | self.assertTrue(torch.equal(rebuilt[0][0], torch.tensor([10.0]))) | 429 | self.assertTrue(torch.equal(rebuilt[0][0], torch.tensor([10.0]))) |
| 430 | self.assertTrue(torch.equal(rebuilt[1]["v"], torch.tensor([20.0]))) | 430 | self.assertTrue(torch.equal(rebuilt[1]["v"], torch.tensor([20.0]))) |
| 431 | 431 | ||
| 432 | def test_transform_hook_default_and_custom_paths(self): | 432 | def test_transform_hook_default_and_custom_paths(self): |
| 433 | shape_handling = torch_npu._inductor.NPUShapeHandling() | 433 | shape_handling = torch_npu._inductor.NPUShapeHandling() |
| 434 | input_a = torch.tensor([1.0]) | 434 | input_a = torch.tensor([1.0]) |
| 435 | input_b = torch.tensor([2.0]) | 435 | input_b = torch.tensor([2.0]) |
| 436 | transform_res = [ | 436 | transform_res = [ |
| 437 | [torch.tensor([3.0]), torch.tensor([4.0])], | 437 | [torch.tensor([3.0]), torch.tensor([4.0])], |
| 438 | [torch.tensor([5.0]), torch.tensor([6.0])] | 438 | [torch.tensor([5.0]), torch.tensor([6.0])] |
| 439 | ] | 439 | ] |
| 440 | 440 | ||
| 441 | with mock.patch.object( | 441 | with mock.patch.object( |
| 442 | torch_npu._inductor.NPUShapeHandling, "transform", return_value=transform_res | 442 | torch_npu._inductor.NPUShapeHandling, "transform", return_value=transform_res |
| 443 | ): | 443 | ): |
| 444 | args_list, kwargs_list = shape_handling.transform_hook(input_a, extra=input_b) | 444 | args_list, kwargs_list = shape_handling.transform_hook(input_a, extra=input_b) |
| 445 | self.assertEqual(len(args_list), 2) | 445 | self.assertEqual(len(args_list), 2) |
| 446 | self.assertEqual(len(kwargs_list), 2) | 446 | self.assertEqual(len(kwargs_list), 2) |
| 447 | self.assertTrue(torch.equal(args_list[0][0], torch.tensor([3.0]))) | 447 | self.assertTrue(torch.equal(args_list[0][0], torch.tensor([3.0]))) |
| 448 | self.assertTrue(torch.equal(kwargs_list[1]["extra"], torch.tensor([6.0]))) | 448 | self.assertTrue(torch.equal(kwargs_list[1]["extra"], torch.tensor([6.0]))) |
| 449 | 449 | ||
| 450 | recorded = {} | 450 | recorded = {} |
| 451 | 451 | ||
| 452 | def pre_fn(*args, **kwargs): | 452 | def pre_fn(*args, **kwargs): |
| 453 | recorded["pre"] = (args, kwargs) | 453 | recorded["pre"] = (args, kwargs) |
| 454 | return [args[0]] | 454 | return [args[0]] |
| 455 | 455 | ||
| 456 | def post_fn(outputs): | 456 | def post_fn(outputs): |
| 457 | recorded["post"] = outputs | 457 | recorded["post"] = outputs |
| 458 | return [("ok",)], [{"done": True}] | 458 | return [("ok",)], [{"done": True}] |
| 459 | 459 | ||
| 460 | shape_handling_custom = torch_npu._inductor.NPUShapeHandling( | 460 | shape_handling_custom = torch_npu._inductor.NPUShapeHandling( |
| 461 | transform_pre_fn=pre_fn, | 461 | transform_pre_fn=pre_fn, |
| 462 | transform_post_fn=post_fn, | 462 | transform_post_fn=post_fn, |
| 463 | ) | 463 | ) |
| 464 | with mock.patch.object( | 464 | with mock.patch.object( |
| 465 | torch_npu._inductor.NPUShapeHandling, "transform", return_value=[[torch.tensor([9.0])]] | 465 | torch_npu._inductor.NPUShapeHandling, "transform", return_value=[[torch.tensor([9.0])]] |
| 466 | ): | 466 | ): |
| 467 | out_args, out_kwargs = shape_handling_custom.transform_hook(torch.tensor([7.0])) | 467 | out_args, out_kwargs = shape_handling_custom.transform_hook(torch.tensor([7.0])) |
| 468 | self.assertIn("pre", recorded) | 468 | self.assertIn("pre", recorded) |
| 469 | self.assertIn("post", recorded) | 469 | self.assertIn("post", recorded) |
| 470 | self.assertEqual(out_args, [("ok",)]) | 470 | self.assertEqual(out_args, [("ok",)]) |
| 471 | self.assertEqual(out_kwargs, [{"done": True}]) | 471 | self.assertEqual(out_kwargs, [{"done": True}]) |
| 472 | 472 | ||
| 473 | shape_handling_none = torch_npu._inductor.NPUShapeHandling(transform_post_fn=lambda _: None) | 473 | shape_handling_none = torch_npu._inductor.NPUShapeHandling(transform_post_fn=lambda _: None) |
| 474 | with mock.patch.object( | 474 | with mock.patch.object( |
| 475 | torch_npu._inductor.NPUShapeHandling, "transform", return_value=[[torch.tensor([1.0])]] | 475 | torch_npu._inductor.NPUShapeHandling, "transform", return_value=[[torch.tensor([1.0])]] |
| 476 | ): | 476 | ): |
| 477 | self.assertIsNone(shape_handling_none.transform_hook(torch.tensor([1.0]))) | 477 | self.assertIsNone(shape_handling_none.transform_hook(torch.tensor([1.0]))) |
| 478 | 478 | ||
| 479 | def test_recover_hook_default_and_custom_paths(self): | 479 | def test_recover_hook_default_and_custom_paths(self): |
| 480 | shape_handling = torch_npu._inductor.NPUShapeHandling() | 480 | shape_handling = torch_npu._inductor.NPUShapeHandling() |
| 481 | groups = [ | 481 | groups = [ |
| 482 | ((torch.tensor([1.0]), "x"), {"y": torch.tensor([2.0])}) | 482 | ((torch.tensor([1.0]), "x"), {"y": torch.tensor([2.0])}) |
| 483 | ] | 483 | ] |
| 484 | 484 | ||
| 485 | with mock.patch.object( | 485 | with mock.patch.object( |
| 486 | torch_npu._inductor.NPUShapeHandling, | 486 | torch_npu._inductor.NPUShapeHandling, |
| 487 | "recover", | 487 | "recover", |
| 488 | side_effect=lambda tensor_groups: list(tensor_groups[0]), | 488 | side_effect=lambda tensor_groups: list(tensor_groups[0]), |
| 489 | ): | 489 | ): |
| 490 | outputs = shape_handling.recover_hook(groups) | 490 | outputs = shape_handling.recover_hook(groups) |
| 491 | self.assertEqual(outputs[0][1], "x") | 491 | self.assertEqual(outputs[0][1], "x") |
| 492 | self.assertTrue(torch.equal(outputs[0][0], torch.tensor([1.0]))) | 492 | self.assertTrue(torch.equal(outputs[0][0], torch.tensor([1.0]))) |
| 493 | self.assertTrue(torch.equal(outputs[1]["y"], torch.tensor([2.0]))) | 493 | self.assertTrue(torch.equal(outputs[1]["y"], torch.tensor([2.0]))) |
| 494 | 494 | ||
| 495 | custom = torch_npu._inductor.NPUShapeHandling( | 495 | custom = torch_npu._inductor.NPUShapeHandling( |
| 496 | recover_pre_fn=lambda groups: [[torch.tensor([5.0])]], | 496 | recover_pre_fn=lambda groups: [[torch.tensor([5.0])]], |
| 497 | recover_post_fn=lambda recover_res: {"result": recover_res}, | 497 | recover_post_fn=lambda recover_res: {"result": recover_res}, |
| 498 | ) | 498 | ) |
| 499 | with mock.patch.object( | 499 | with mock.patch.object( |
| 500 | torch_npu._inductor.NPUShapeHandling, "recover", return_value=[torch.tensor([8.0])] | 500 | torch_npu._inductor.NPUShapeHandling, "recover", return_value=[torch.tensor([8.0])] |
| 501 | ): | 501 | ): |
| 502 | outputs = custom.recover_hook(groups) | 502 | outputs = custom.recover_hook(groups) |
| 503 | self.assertEqual(list(outputs.keys()), ["result"]) | 503 | self.assertEqual(list(outputs.keys()), ["result"]) |
| 504 | self.assertTrue(torch.equal(outputs["result"][0], torch.tensor([8.0]))) | 504 | self.assertTrue(torch.equal(outputs["result"][0], torch.tensor([8.0]))) |
| 505 | 505 | ||
| 506 | def test_get_shape_safe_and_patch_shape_handling(self): | 506 | def test_get_shape_safe_and_patch_shape_handling(self): |
| 507 | shape_handling = torch_npu._inductor.NPUShapeHandling() | 507 | shape_handling = torch_npu._inductor.NPUShapeHandling() |
| 508 | shape_info = shape_handling.get_shape_safe((torch.randn(2, 3), [torch.randn(1)])) | 508 | shape_info = shape_handling.get_shape_safe((torch.randn(2, 3), [torch.randn(1)])) |
| 509 | self.assertEqual(shape_info[0], [2, 3]) | 509 | self.assertEqual(shape_info[0], [2, 3]) |
| 510 | self.assertEqual(shape_info[1][0], [1]) | 510 | self.assertEqual(shape_info[1][0], [1]) |
| 511 | self.assertIs(shape_handling.get_shape_safe(1), int) | 511 | self.assertIs(shape_handling.get_shape_safe(1), int) |
| 512 | 512 | ||
| 513 | if hasattr(shape_handling_module.patch_shape_handling, "_is_patched"): | 513 | if hasattr(shape_handling_module.patch_shape_handling, "_is_patched"): |
| 514 | delattr(shape_handling_module.patch_shape_handling, "_is_patched") | 514 | delattr(shape_handling_module.patch_shape_handling, "_is_patched") |
| 515 | called = [] | 515 | called = [] |
| 516 | with mock.patch.object(shape_handling_module, "patch_dynamo_context", side_effect=lambda: called.append(1)): | 516 | with mock.patch.object(shape_handling_module, "patch_dynamo_context", side_effect=lambda: called.append(1)): |
| 517 | shape_handling_module.patch_shape_handling() | 517 | shape_handling_module.patch_shape_handling() |
| 518 | shape_handling_module.patch_shape_handling() | 518 | shape_handling_module.patch_shape_handling() |
| 519 | self.assertEqual(len(called), 1) | 519 | self.assertEqual(len(called), 1) |
| 520 | if hasattr(shape_handling_module.patch_shape_handling, "_is_patched"): | 520 | if hasattr(shape_handling_module.patch_shape_handling, "_is_patched"): |
| 521 | delattr(shape_handling_module.patch_shape_handling, "_is_patched") | 521 | delattr(shape_handling_module.patch_shape_handling, "_is_patched") |
| 522 | 522 | ||
| 523 | 523 | ||
| 524 | class TestDynamicShapeCompile(TestCase): | 524 | class TestDynamicShapeCompile(TestCase): |
| 525 | def test_npu_dynamic_shape_reuse_with_no_bucket(self): | 525 | def test_npu_dynamic_shape_reuse_with_no_bucket(self): |
| 526 | 526 | ||
| 527 | compiled_fn = torch.compile( | 527 | compiled_fn = torch.compile( |
| 528 | model_fn, | 528 | model_fn, |
| 529 | backend='inductor', | 529 | backend='inductor', |
| 530 | dynamic=False, | 530 | dynamic=False, |
| 531 | ) | 531 | ) |
| 532 | 532 | ||
| 533 | # 运行不同形状,验证是否只触发 4 次编译 | 533 | # 运行不同形状,验证是否只触发 4 次编译 |
| 534 | test_shapes = [(3, 20), (4, 20), (5, 20), (6, 20)] | 534 | test_shapes = [(3, 20), (4, 20), (5, 20), (6, 20)] |
| 535 | 535 | ||
| 536 | 536 | ||
| 537 | if hasattr(torch._inductor.metrics.generated_kernel_count, 'reset'): | 537 | if hasattr(torch._inductor.metrics.generated_kernel_count, 'reset'): |
| 538 | torch._inductor.metrics.generated_kernel_count.reset() | 538 | torch._inductor.metrics.generated_kernel_count.reset() |
| 539 | else: | 539 | else: |
| 540 | torch._inductor.metrics.generated_kernel_count = 0 | 540 | torch._inductor.metrics.generated_kernel_count = 0 |
| 541 | 541 | ||
| 542 | for shape in test_shapes: | 542 | for shape in test_shapes: |
| 543 | A = torch.randn(shape, device=device) | 543 | A = torch.randn(shape, device=device) |
| 544 | B = torch.randn(shape, device=device) | 544 | B = torch.randn(shape, device=device) |
| 545 | out = compiled_fn(A, B) | 545 | out = compiled_fn(A, B) |
| 546 | self.assertTrue(torch.allclose(out, A + B)) | 546 | self.assertTrue(torch.allclose(out, A + B)) |
| 547 | 547 | ||
| 548 | 548 | ||
| 549 | compile_count = torch._inductor.metrics.generated_kernel_count | 549 | compile_count = torch._inductor.metrics.generated_kernel_count |
| 550 | 550 | ||
| 551 | # 获取 Inductor 编译的总次数(生成的 kernel 数量相关) | 551 | # 获取 Inductor 编译的总次数(生成的 kernel 数量相关) |
| 552 | print(f"\n[结果] Inductor 编译次数 (generated_kernel_count): {compile_count}") | 552 | print(f"\n[结果] Inductor 编译次数 (generated_kernel_count): {compile_count}") |
| 553 | 553 | ||
| 554 | # 如果动态形状分档不生效,编译次数应为 4 | 554 | # 如果动态形状分档不生效,编译次数应为 4 |
| 555 | self.assertEqual(compile_count, 4) | 555 | self.assertEqual(compile_count, 4) |
| 556 | 556 | ||
| 557 | def test_npu_dynamic_shape_reuse_with_bucket(self): | 557 | def test_npu_dynamic_shape_reuse_with_bucket(self): |
| 558 | 558 | ||
| 559 | compiled_fn = torch.compile( | 559 | compiled_fn = torch.compile( |
| 560 | model_fn, | 560 | model_fn, |
| 561 | backend='inductor', | 561 | backend='inductor', |
| 562 | dynamic=False, | 562 | dynamic=False, |
| 563 | options=shape_options | 563 | options=shape_options |
| 564 | ) | 564 | ) |
| 565 | 565 | ||
| 566 | # 运行不同形状,验证是否只触发 2 次编译 | 566 | # 运行不同形状,验证是否只触发 2 次编译 |
| 567 | test_shapes = [(3, 32), (4, 32), (5, 32), (6, 32)] | 567 | test_shapes = [(3, 32), (4, 32), (5, 32), (6, 32)] |
| 568 | if hasattr(torch._inductor.metrics.generated_kernel_count, 'reset'): | 568 | if hasattr(torch._inductor.metrics.generated_kernel_count, 'reset'): |
| 569 | torch._inductor.metrics.generated_kernel_count.reset() | 569 | torch._inductor.metrics.generated_kernel_count.reset() |
| 570 | else: | 570 | else: |
| 571 | torch._inductor.metrics.generated_kernel_count = 0 | 571 | torch._inductor.metrics.generated_kernel_count = 0 |
| 572 | 572 | ||
| 573 | for shape in test_shapes: | 573 | for shape in test_shapes: |
| 574 | A = torch.randn(shape, device=device) | 574 | A = torch.randn(shape, device=device) |
| 575 | B = torch.randn(shape, device=device) | 575 | B = torch.randn(shape, device=device) |
| 576 | out = compiled_fn(A, B) | 576 | out = compiled_fn(A, B) |
| 577 | self.assertTrue(torch.allclose(out, A + B)) | 577 | self.assertTrue(torch.allclose(out, A + B)) |
| 578 | 578 | ||
| 579 | 579 | ||
| 580 | compile_count = torch._inductor.metrics.generated_kernel_count | 580 | compile_count = torch._inductor.metrics.generated_kernel_count |
| 581 | 581 | ||
| 582 | # 获取 Inductor 编译的总次数(生成的 kernel 数量相关) | 582 | # 获取 Inductor 编译的总次数(生成的 kernel 数量相关) |
| 583 | print(f"\n[结果] Inductor 编译次数 (generated_kernel_count): {compile_count}") | 583 | print(f"\n[结果] Inductor 编译次数 (generated_kernel_count): {compile_count}") |
| 584 | 584 | ||
| 585 | # 如果动态形状分档生效,编译次数应为 2 | 585 | # 如果动态形状分档生效,编译次数应为 2 |
| 586 | self.assertEqual(compile_count, 2) | 586 | self.assertEqual(compile_count, 2) |
| 587 | 587 | ||
| 588 | def test_npu_dynamic_shape_reuse_with_symbolic_shape(self): | 588 | def test_npu_dynamic_shape_reuse_with_symbolic_shape(self): |
| 589 | 589 | ||
| 590 | compiled_fn = torch.compile( | 590 | compiled_fn = torch.compile( |
| 591 | model_fn, | 591 | model_fn, |
| 592 | backend='inductor', | 592 | backend='inductor', |
| 593 | dynamic=True, | 593 | dynamic=True, |
| 594 | ) | 594 | ) |
| 595 | 595 | ||
| 596 | # 运行不同形状,验证是否只触发 1 次编译 | 596 | # 运行不同形状,验证是否只触发 1 次编译 |
| 597 | test_shapes = [(3, 32), (4, 32), (5, 32), (6, 32)] | 597 | test_shapes = [(3, 32), (4, 32), (5, 32), (6, 32)] |
| 598 | 598 | ||
| 599 | if hasattr(torch._inductor.metrics.generated_kernel_count, 'reset'): | 599 | if hasattr(torch._inductor.metrics.generated_kernel_count, 'reset'): |
| 600 | torch._inductor.metrics.generated_kernel_count.reset() | 600 | torch._inductor.metrics.generated_kernel_count.reset() |
| 601 | else: | 601 | else: |
| 602 | torch._inductor.metrics.generated_kernel_count = 0 | 602 | torch._inductor.metrics.generated_kernel_count = 0 |
| 603 | 603 | ||
| 604 | for shape in test_shapes: | 604 | for shape in test_shapes: |
| 605 | A = torch.randn(shape, device=device) | 605 | A = torch.randn(shape, device=device) |
| 606 | B = torch.randn(shape, device=device) | 606 | B = torch.randn(shape, device=device) |
| 607 | out = compiled_fn(A, B) | 607 | out = compiled_fn(A, B) |
| 608 | self.assertTrue(torch.allclose(out, A + B)) | 608 | self.assertTrue(torch.allclose(out, A + B)) |
| 609 | 609 | ||
| 610 | compile_count = torch._inductor.metrics.generated_kernel_count | 610 | compile_count = torch._inductor.metrics.generated_kernel_count |
| 611 | 611 | ||
| 612 | 612 | ||
| 613 | 613 | ||
| 614 | # 获取 Inductor 编译的总次数(生成的 kernel 数量相关) | 614 | # 获取 Inductor 编译的总次数(生成的 kernel 数量相关) |
| 615 | print(f"\n[结果] Inductor 编译次数 (generated_kernel_count): {compile_count}") | 615 | print(f"\n[结果] Inductor 编译次数 (generated_kernel_count): {compile_count}") |
| 616 | 616 | ||
| 617 | # 如果动态形状符号化生效,编译次数应为 1 | 617 | # 如果动态形状符号化生效,编译次数应为 1 |
| 618 | self.assertEqual(compile_count, 1) | 618 | self.assertEqual(compile_count, 1) |
| 619 | 619 | ||
| 620 | def test_npu_dynamic_shape_reuse_with_symbolic_shape_and_bucket(self): | 620 | def test_npu_dynamic_shape_reuse_with_symbolic_shape_and_bucket(self): |
| 621 | 621 | ||
| 622 | compiled_fn = torch.compile( | 622 | compiled_fn = torch.compile( |
| 623 | model_fn, | 623 | model_fn, |
| 624 | backend='inductor', | 624 | backend='inductor', |
| 625 | dynamic=True, | 625 | dynamic=True, |
| 626 | options=shape_options | 626 | options=shape_options |
| 627 | ) | 627 | ) |
| 628 | 628 | ||
| 629 | # 运行不同形状,验证是否只触发 1 次编译 | 629 | # 运行不同形状,验证是否只触发 1 次编译 |
| 630 | test_shapes = [(3, 32), (4, 32), (5, 32), (6, 32)] | 630 | test_shapes = [(3, 32), (4, 32), (5, 32), (6, 32)] |
| 631 | 631 | ||
| 632 | if hasattr(torch._inductor.metrics.generated_kernel_count, 'reset'): | 632 | if hasattr(torch._inductor.metrics.generated_kernel_count, 'reset'): |
| 633 | torch._inductor.metrics.generated_kernel_count.reset() | 633 | torch._inductor.metrics.generated_kernel_count.reset() |
| 634 | else: | 634 | else: |
| 635 | torch._inductor.metrics.generated_kernel_count = 0 | 635 | torch._inductor.metrics.generated_kernel_count = 0 |
| 636 | 636 | ||
| 637 | for shape in test_shapes: | 637 | for shape in test_shapes: |
| 638 | A = torch.randn(shape, device=device) | 638 | A = torch.randn(shape, device=device) |
| 639 | B = torch.randn(shape, device=device) | 639 | B = torch.randn(shape, device=device) |
| 640 | out = compiled_fn(A, B) | 640 | out = compiled_fn(A, B) |
| 641 | self.assertTrue(torch.allclose(out, A + B)) | 641 | self.assertTrue(torch.allclose(out, A + B)) |
| 642 | 642 | ||
| 643 | compile_count = torch._inductor.metrics.generated_kernel_count | 643 | compile_count = torch._inductor.metrics.generated_kernel_count |
| 644 | 644 | ||
| 645 | 645 | ||
| 646 | # 获取 Inductor 编译的总次数(生成的 kernel 数量相关) | 646 | # 获取 Inductor 编译的总次数(生成的 kernel 数量相关) |
| 647 | print(f"\n[结果] Inductor 编译次数 (generated_kernel_count): {compile_count}") | 647 | print(f"\n[结果] Inductor 编译次数 (generated_kernel_count): {compile_count}") |
| 648 | 648 | ||
| 649 | # 如果动态形状符号化生效,编译次数应为 1 | 649 | # 如果动态形状符号化生效,编译次数应为 1 |
| 650 | self.assertEqual(compile_count, 1) | 650 | self.assertEqual(compile_count, 1) |
| 651 | 651 | ||
| 652 | def test_npu_invalid_shape_options_key_error(self): | 652 | def test_npu_invalid_shape_options_key_error(self): |
| 653 | 653 | ||
| 654 | # 构造一个非法的配置:最小尺寸大于最大尺寸 | 654 | # 构造一个非法的配置:最小尺寸大于最大尺寸 |
| 655 | invalid_shape_options = { | 655 | invalid_shape_options = { |
| 656 | "enable_shape_handling": True, | 656 | "enable_shape_handling": True, |
| 657 | "shape_handling_configs": [ | 657 | "shape_handling_configs": [ |
| 658 | { | 658 | { |
| 659 | "type": "BATCHSIZE", | 659 | "type": "BATCHSIZE", |
| 660 | "min_size": 1024, # 错误:min > max | 660 | "min_size": 1024, # 错误:min > max |
| 661 | "max_size": 1, | 661 | "max_size": 1, |
| 662 | "policy": "TIMES", | 662 | "policy": "TIMES", |
| 663 | } | 663 | } |
| 664 | ] | 664 | ] |
| 665 | } | 665 | } |
| 666 | 666 | ||
| 667 | # 尝试编译,预期抛出 ValueError 或相关配置错误 | 667 | # 尝试编译,预期抛出 ValueError 或相关配置错误 |
| 668 | # 注意:有些错误可能在编译阶段 (compile) 触发,有些可能在首次运行 (call) 时触发 | 668 | # 注意:有些错误可能在编译阶段 (compile) 触发,有些可能在首次运行 (call) 时触发 |
| 669 | try: | 669 | try: |
| 670 | compiled_fn = torch.compile( | 670 | compiled_fn = torch.compile( |
| 671 | model_fn, | 671 | model_fn, |
| 672 | backend='inductor', | 672 | backend='inductor', |
| 673 | dynamic=False, | 673 | dynamic=False, |
| 674 | options=invalid_shape_options | 674 | options=invalid_shape_options |
| 675 | ) | 675 | ) |
| 676 | 676 | ||
| 677 | # 准备输入数据触发编译 | 677 | # 准备输入数据触发编译 |
| 678 | A = torch.randn((2, 32), device=device) | 678 | A = torch.randn((2, 32), device=device) |
| 679 | B = torch.randn((2, 32), device=device) | 679 | B = torch.randn((2, 32), device=device) |
| 680 | 680 | ||
| 681 | with self.assertRaises((ValueError, RuntimeError, TypeError)) as cm: | 681 | with self.assertRaises((ValueError, RuntimeError, TypeError)) as cm: |
| 682 | compiled_fn(A, B) | 682 | compiled_fn(A, B) |
| 683 | 683 | ||
| 684 | print(f"\n[成功捕获异常]: {cm.exception}") | 684 | print(f"\n[成功捕获异常]: {cm.exception}") |
| 685 | 685 | ||
| 686 | except Exception as e: | 686 | except Exception as e: |
| 687 | # 如果在 torch.compile 阶段就直接崩溃,也算捕获成功 | 687 | # 如果在 torch.compile 阶段就直接崩溃,也算捕获成功 |
| 688 | print(f"\n[编译阶段直接报错]: {e}") | 688 | print(f"\n[编译阶段直接报错]: {e}") |
| 689 | self.assertIsInstance(e, (ValueError, RuntimeError, TypeError)) | 689 | self.assertIsInstance(e, (ValueError, RuntimeError, TypeError)) |
| 690 | 690 | ||
| 691 | def test_npu_malformed_option_type(self): | 691 | def test_npu_malformed_option_type(self): |
| 692 | 692 | ||
| 693 | # 构造格式完全错误的 options | 693 | # 构造格式完全错误的 options |
| 694 | malformed_options = { | 694 | malformed_options = { |
| 695 | "shape_handling_configs": "this should be a list, not a string" | 695 | "shape_handling_configs": "this should be a list, not a string" |
| 696 | } | 696 | } |
| 697 | 697 | ||
| 698 | with self.assertRaises(Exception): | 698 | with self.assertRaises(Exception): |
| 699 | compiled_fn = torch.compile(model_fn, options=malformed_options) | 699 | compiled_fn = torch.compile(model_fn, options=malformed_options) |
| 700 | compiled_fn(torch.randn(2, device="npu"), torch.randn(2, device="npu")) | 700 | compiled_fn(torch.randn(2, device="npu"), torch.randn(2, device="npu")) |
| 701 | 701 | ||
| 702 | def test_npu_shape_options_with_handling_disabled(self): | 702 | def test_npu_shape_options_with_handling_disabled(self): |
| 703 | # enable_shape_handling 设置为False, 同时保留主流的配置选项,测试是否会存在选项异常报错 | 703 | # enable_shape_handling 设置为False, 同时保留主流的配置选项,测试是否会存在选项异常报错 |
| 704 | 704 | ||
| 705 | 705 | ||
| 706 | shape_options_off = { | 706 | shape_options_off = { |
| 707 | "enable_shape_handling": False, | 707 | "enable_shape_handling": False, |
| 708 | "shape_handling_configs": [ | 708 | "shape_handling_configs": [ |
| 709 | { | 709 | { |
| 710 | "type": "BATCHSIZE", | 710 | "type": "BATCHSIZE", |
| 711 | "min_size": 1, | 711 | "min_size": 1, |
| 712 | "max_size": 1024, | 712 | "max_size": 1024, |
| 713 | "policy": "TIMES", | 713 | "policy": "TIMES", |
| 714 | } | 714 | } |
| 715 | ] | 715 | ] |
| 716 | } | 716 | } |
| 717 | 717 | ||
| 718 | # 尝试编译,预期可正确处理此类配置,则通过测试 | 718 | # 尝试编译,预期可正确处理此类配置,则通过测试 |
| 719 | try: | 719 | try: |
| 720 | compiled_fn = torch.compile( | 720 | compiled_fn = torch.compile( |
| 721 | model_fn, | 721 | model_fn, |
| 722 | backend='inductor', | 722 | backend='inductor', |
| 723 | dynamic=False, | 723 | dynamic=False, |
| 724 | options=shape_options_off | 724 | options=shape_options_off |
| 725 | ) | 725 | ) |
| 726 | 726 | ||
| 727 | # 准备输入数据触发编译 | 727 | # 准备输入数据触发编译 |
| 728 | A = torch.randn((2, 32), device=device) | 728 | A = torch.randn((2, 32), device=device) |
| 729 | B = torch.randn((2, 32), device=device) | 729 | B = torch.randn((2, 32), device=device) |
| 730 | compiled_fn(A, B) | 730 | compiled_fn(A, B) |
| 731 | 731 | ||
| 732 | except Exception as e: | 732 | except Exception as e: |
| 733 | self.fail(f"torch.compile raised {type(e).__name__} unexpectedly: {e}") | 733 | self.fail(f"torch.compile raised {type(e).__name__} unexpectedly: {e}") |
| 734 | 734 | ||
| 735 | def test_npu_shape_handling_whit_mutil_compile(self): | 735 | def test_npu_shape_handling_whit_mutil_compile(self): |
| 736 | # 多次编译同一个函数,验证 shape handling 是否正常工作 | 736 | # 多次编译同一个函数,验证 shape handling 是否正常工作 |
| 737 | # 1. 首次编译触发 shape handling | 737 | # 1. 首次编译触发 shape handling |
| 738 | # 2. 后续编译复用已有的 shape handling 逻辑 | 738 | # 2. 后续编译复用已有的 shape handling 逻辑 |
| 739 | try: | 739 | try: |
| 740 | compiled_fn = torch.compile( | 740 | compiled_fn = torch.compile( |
| 741 | model_fn, | 741 | model_fn, |
| 742 | backend='inductor', | 742 | backend='inductor', |
| 743 | dynamic=False, | 743 | dynamic=False, |
| 744 | options=shape_options | 744 | options=shape_options |
| 745 | ) | 745 | ) |
| 746 | # 第一次spilt | 746 | # 第一次spilt |
| 747 | compiled_fn(torch.randn((1025, 32), device=device), torch.randn((1025, 32), device=device)) | 747 | compiled_fn(torch.randn((1025, 32), device=device), torch.randn((1025, 32), device=device)) |
| 748 | 748 | ||
| 749 | # 第二次spilt | 749 | # 第二次spilt |
| 750 | compiled_fn(torch.randn((2048, 32), device=device), torch.randn((2048, 32), device=device)) | 750 | compiled_fn(torch.randn((2048, 32), device=device), torch.randn((2048, 32), device=device)) |
| 751 | 751 | ||
| 752 | # 第三次spilt | 752 | # 第三次spilt |
| 753 | compiled_fn(torch.randn((10240, 32), device=device), torch.randn((10240, 32), device=device)) | 753 | compiled_fn(torch.randn((10240, 32), device=device), torch.randn((10240, 32), device=device)) |
| 754 | except Exception as e: | 754 | except Exception as e: |
| 755 | self.fail(f"torch.compile raised {type(e).__name__} unexpectedly: ") | 755 | self.fail(f"torch.compile raised {type(e).__name__} unexpectedly: ") |
| 756 | 756 | ||
| 757 | 757 | ||
| 758 | class TestUnifiedCopy(TestCase): | 758 | class TestUnifiedCopy(TestCase): |
| 759 | def test_none_and_simple_types(self): | 759 | def test_none_and_simple_types(self): |
| 760 | """Test unified_copy with None, list, dict, and tuple.""" | 760 | """Test unified_copy with None, list, dict, and tuple.""" |
| 761 | # None | 761 | # None |
| 762 | self.assertIsNone(unified_copy(None)) | 762 | self.assertIsNone(unified_copy(None)) |
| 763 | 763 | ||
| 764 | # list | 764 | # list |
| 765 | lst = [1, 2, 3] | 765 | lst = [1, 2, 3] |
| 766 | copied_lst = unified_copy(lst) | 766 | copied_lst = unified_copy(lst) |
| 767 | self.assertEqual(lst, copied_lst) | 767 | self.assertEqual(lst, copied_lst) |
| 768 | self.assertIsNot(lst, copied_lst) | 768 | self.assertIsNot(lst, copied_lst) |
| 769 | 769 | ||
| 770 | # dict | 770 | # dict |
| 771 | d = {"a": 1, "b": [2, 3]} | 771 | d = {"a": 1, "b": [2, 3]} |
| 772 | copied_d = unified_copy(d) | 772 | copied_d = unified_copy(d) |
| 773 | self.assertEqual(d, copied_d) | 773 | self.assertEqual(d, copied_d) |
| 774 | self.assertIsNot(d, copied_d) | 774 | self.assertIsNot(d, copied_d) |
| 775 | 775 | ||
| 776 | # tuple | 776 | # tuple |
| 777 | t = (1, [2, 3]) | 777 | t = (1, [2, 3]) |
| 778 | copied_t = unified_copy(t) | 778 | copied_t = unified_copy(t) |
| 779 | self.assertEqual(t, copied_t) | 779 | self.assertEqual(t, copied_t) |
| 780 | self.assertIsInstance(copied_t, tuple) | 780 | self.assertIsInstance(copied_t, tuple) |
| 781 | 781 | ||
| 782 | def test_nested_structure_with_tensor(self): | 782 | def test_nested_structure_with_tensor(self): |
| 783 | """Test nested structure containing NPU tensor.""" | 783 | """Test nested structure containing NPU tensor.""" |
| 784 | original = { | 784 | original = { |
| 785 | "data": [torch.tensor([1.0, 2.0]), 42], | 785 | "data": [torch.tensor([1.0, 2.0]), 42], |
| 786 | "meta": {"shape": (2,)} | 786 | "meta": {"shape": (2,)} |
| 787 | } | 787 | } |
| 788 | copied = unified_copy(original) | 788 | copied = unified_copy(original) |
| 789 | 789 | ||
| 790 | # 结构和值一致 | 790 | # 结构和值一致 |
| 791 | self.assertEqual(original["meta"], copied["meta"]) | 791 | self.assertEqual(original["meta"], copied["meta"]) |
| 792 | self.assertTrue(torch.equal(original["data"][0], copied["data"][0])) | 792 | self.assertTrue(torch.equal(original["data"][0], copied["data"][0])) |
| 793 | self.assertEqual(original["data"][1], copied["data"][1]) | 793 | self.assertEqual(original["data"][1], copied["data"][1]) |
| 794 | 794 | ||
| 795 | # 验证独立副本 | 795 | # 验证独立副本 |
| 796 | original["data"][0][0] = 888.0 | 796 | original["data"][0][0] = 888.0 |
| 797 | self.assertNotEqual(copied["data"][0][0].item(), 888.0) | 797 | self.assertNotEqual(copied["data"][0][0].item(), 888.0) |
| 798 | 798 | ||
| 799 | 799 | ||
| 800 | def test_deepcopy_failure_returns_original(self): | 800 | def test_deepcopy_failure_returns_original(self): |
| 801 | class NonCopyable: | 801 | class NonCopyable: |
| 802 | def __deepcopy__(self, memo): | 802 | def __deepcopy__(self, memo): |
| 803 | raise TypeError("deepcopy not supported") | 803 | raise TypeError("deepcopy not supported") |
| 804 | 804 | ||
| 805 | obj = NonCopyable() | 805 | obj = NonCopyable() |
| 806 | copied = unified_copy(obj) | 806 | copied = unified_copy(obj) |
| 807 | self.assertIs(copied, obj) | 807 | self.assertIs(copied, obj) |
| 808 | 808 | ||
| 809 | 809 | ||
| 810 | instantiate_parametrized_tests(TestShapeHandling) | 810 | instantiate_parametrized_tests(TestShapeHandling) |
| 811 | instantiate_parametrized_tests(TestShapeHandlingBranchCoverage) | 811 | instantiate_parametrized_tests(TestShapeHandlingBranchCoverage) |
| 812 | instantiate_parametrized_tests(TestUnifiedCopy) | 812 | instantiate_parametrized_tests(TestUnifiedCopy) |
| 813 | instantiate_parametrized_tests(TestDynamicShapeCompile) | 813 | instantiate_parametrized_tests(TestDynamicShapeCompile) |
| 814 | 814 | ||
| 815 | if __name__ == '__main__': | 815 | if __name__ == '__main__': |
| 816 | run_tests() | 816 | run_tests() |
| @@ -1,97 +1,97 @@ | |||
| 1 | import torch | 1 | import torch |
| 2 | from torch.testing._internal.common_utils import run_tests | 2 | from torch.testing._internal.common_utils import run_tests |
| 3 | from testutils import TestUtils | 3 | from testutils import TestUtils |
| 4 | 4 | ||
| 5 | from torch_npu._inductor.codegen.tile_generator import TileGenerator, aligned_numel_32byte | 5 | from torch_npu._inductor.codegen.tile_generator import TileGenerator, aligned_numel_32byte |
| 6 | from torch_npu._inductor.codegen.triton_utils import NPUKernelType | 6 | from torch_npu._inductor.codegen.triton_utils import NPUKernelType |
| 7 | 7 | ||
| 8 | 8 | ||
| 9 | class TestTileGenerator32ByteAlignment(TestUtils): | 9 | class TestTileGenerator32ByteAlignment(TestUtils): |
| 10 | """ | 10 | """ |
| 11 | Test class for TileGenerator 32Byte alignment functionality | 11 | Test class for TileGenerator 32Byte alignment functionality |
| 12 | """ | 12 | """ |
| 13 | 13 | ||
| 14 | def test_32byte_alignment(self): | 14 | def test_32byte_alignment(self): |
| 15 | """ | 15 | """ |
| 16 | Test that block_size and sub_block_size are 32Byte aligned | 16 | Test that block_size and sub_block_size are 32Byte aligned |
| 17 | """ | 17 | """ |
| 18 | # Test with different data types | 18 | # Test with different data types |
| 19 | dtypes = [torch.float32, torch.float16, torch.bfloat16, torch.int32] | 19 | dtypes = [torch.float32, torch.float16, torch.bfloat16, torch.int32] |
| 20 | test_cases = [ | 20 | test_cases = [ |
| 21 | # (numels, axis_names, tiling_axis, no_loop_axis, split_axis, low_dims) | 21 | # (numels, axis_names, tiling_axis, no_loop_axis, split_axis, low_dims) |
| 22 | ((1024, 512), ["h", "w"], [0, 1], [], [0, 1], []), | 22 | ((1024, 512), ["h", "w"], [0, 1], [], [0, 1], []), |
| 23 | ((2048, 2048), ["h", "w"], [0, 1], [], [0, 1], []), | 23 | ((2048, 2048), ["h", "w"], [0, 1], [], [0, 1], []), |
| 24 | ((100, 200), ["h", "w"], [0, 1], [], [0, 1], []), # Non-power-of-2 sizes | 24 | ((100, 200), ["h", "w"], [0, 1], [], [0, 1], []), # Non-power-of-2 sizes |
| 25 | ] | 25 | ] |
| 26 | 26 | ||
| 27 | for dtype in dtypes: | 27 | for dtype in dtypes: |
| 28 | for numels, axis_names, tiling_axis, no_loop_axis, split_axis, low_dims in test_cases: | 28 | for numels, axis_names, tiling_axis, no_loop_axis, split_axis, low_dims in test_cases: |
| 29 | # Create TileGenerator instance | 29 | # Create TileGenerator instance |
| 30 | tile_gen = TileGenerator( | 30 | tile_gen = TileGenerator( |
| 31 | numels=numels, | 31 | numels=numels, |
| 32 | axis_names=axis_names, | 32 | axis_names=axis_names, |
| 33 | tiling_axis=tiling_axis, | 33 | tiling_axis=tiling_axis, |
| 34 | no_loop_axis=no_loop_axis, | 34 | no_loop_axis=no_loop_axis, |
| 35 | split_axis=split_axis, | 35 | split_axis=split_axis, |
| 36 | low_dims=low_dims, | 36 | low_dims=low_dims, |
| 37 | persistent_reduction=False, | 37 | persistent_reduction=False, |
| 38 | dtype=dtype, | 38 | dtype=dtype, |
| 39 | npu_kernel_type=NPUKernelType.SIMD | 39 | npu_kernel_type=NPUKernelType.SIMD |
| 40 | ) | 40 | ) |
| 41 | 41 | ||
| 42 | # Generate configs | 42 | # Generate configs |
| 43 | configs = tile_gen.descend_split_tiling() | 43 | configs = tile_gen.descend_split_tiling() |
| 44 | 44 | ||
| 45 | # Check each config for 32Byte alignment | 45 | # Check each config for 32Byte alignment |
| 46 | for cfg in configs: | 46 | for cfg in configs: |
| 47 | kwargs = cfg.kwargs | 47 | kwargs = cfg.kwargs |
| 48 | dtype_bytes = torch.tensor([], dtype=dtype).element_size() | 48 | dtype_bytes = torch.tensor([], dtype=dtype).element_size() |
| 49 | min_numel = 32 // dtype_bytes | 49 | min_numel = 32 // dtype_bytes |
| 50 | 50 | ||
| 51 | # Check split axis block sizes | 51 | # Check split axis block sizes |
| 52 | for axis in split_axis: | 52 | for axis in split_axis: |
| 53 | block_name = f"{axis_names[axis].upper()}BLOCK" | 53 | block_name = f"{axis_names[axis].upper()}BLOCK" |
| 54 | if block_name in kwargs: | 54 | if block_name in kwargs: |
| 55 | block_size = kwargs[block_name] | 55 | block_size = kwargs[block_name] |
| 56 | if block_size > min_numel: | 56 | if block_size > min_numel: |
| 57 | self.assertEqual(block_size % min_numel, 0, | 57 | self.assertEqual(block_size % min_numel, 0, |
| 58 | msg=f"Block size {block_size} for {block_name} " | 58 | msg=f"Block size {block_size} for {block_name} " |
| 59 | f"with dtype {dtype} is not 32Byte aligned") | 59 | f"with dtype {dtype} is not 32Byte aligned") |
| 60 | 60 | ||
| 61 | # Check tiling axis sub-block sizes | 61 | # Check tiling axis sub-block sizes |
| 62 | for axis in tiling_axis: | 62 | for axis in tiling_axis: |
| 63 | sub_block_name = f"{axis_names[axis].upper()}BLOCK_SUB" | 63 | sub_block_name = f"{axis_names[axis].upper()}BLOCK_SUB" |
| 64 | if sub_block_name in kwargs: | 64 | if sub_block_name in kwargs: |
| 65 | sub_block_size = kwargs[sub_block_name] | 65 | sub_block_size = kwargs[sub_block_name] |
| 66 | if sub_block_size > min_numel: | 66 | if sub_block_size > min_numel: |
| 67 | self.assertEqual(sub_block_size % min_numel, 0, | 67 | self.assertEqual(sub_block_size % min_numel, 0, |
| 68 | msg=f"Sub-block size {sub_block_size} for {sub_block_name} " | 68 | msg=f"Sub-block size {sub_block_size} for {sub_block_name} " |
| 69 | f"with dtype {dtype} is not 32Byte aligned") | 69 | f"with dtype {dtype} is not 32Byte aligned") |
| 70 | 70 | ||
| 71 | def test_aligned_numel_method(self): | 71 | def test_aligned_numel_method(self): |
| 72 | """ | 72 | """ |
| 73 | Test the aligned_numel_32byte method directly | 73 | Test the aligned_numel_32byte method directly |
| 74 | """ | 74 | """ |
| 75 | # Test with different data types | 75 | # Test with different data types |
| 76 | test_cases = [ | 76 | test_cases = [ |
| 77 | (torch.float32, 32, 32), # 32 elements = 128 bytes, already aligned | 77 | (torch.float32, 32, 32), # 32 elements = 128 bytes, already aligned |
| 78 | (torch.float32, 33, 40), # 33 elements = 132 bytes, should align to 40 elements (160 bytes) | 78 | (torch.float32, 33, 40), # 33 elements = 132 bytes, should align to 40 elements (160 bytes) |
| 79 | (torch.float16, 32, 32), # 32 elements = 64 bytes, already aligned | 79 | (torch.float16, 32, 32), # 32 elements = 64 bytes, already aligned |
| 80 | (torch.float16, 33, 48), # 33 elements = 66 bytes, should align to 48 elements (96 bytes) | 80 | (torch.float16, 33, 48), # 33 elements = 66 bytes, should align to 48 elements (96 bytes) |
| 81 | (torch.bfloat16, 32, 32), # 32 elements = 64 bytes, already aligned | 81 | (torch.bfloat16, 32, 32), # 32 elements = 64 bytes, already aligned |
| 82 | (torch.bfloat16, 33, 48), # 33 elements = 66 bytes, should align to 48 elements (96 bytes) | 82 | (torch.bfloat16, 33, 48), # 33 elements = 66 bytes, should align to 48 elements (96 bytes) |
| 83 | (torch.int32, 32, 32), # 32 elements = 128 bytes, already aligned | 83 | (torch.int32, 32, 32), # 32 elements = 128 bytes, already aligned |
| 84 | (torch.int32, 33, 40), # 33 elements = 132 bytes, should align to 40 elements (160 bytes) | 84 | (torch.int32, 33, 40), # 33 elements = 132 bytes, should align to 40 elements (160 bytes) |
| 85 | ] | 85 | ] |
| 86 | 86 | ||
| 87 | for dtype, input_numel, expected_numel in test_cases: | 87 | for dtype, input_numel, expected_numel in test_cases: |
| 88 | # Get dtype_bytes for the test | 88 | # Get dtype_bytes for the test |
| 89 | dtype_bytes = torch.tensor([], dtype=dtype).element_size() | 89 | dtype_bytes = torch.tensor([], dtype=dtype).element_size() |
| 90 | result = aligned_numel_32byte(input_numel, dtype_bytes) | 90 | result = aligned_numel_32byte(input_numel, dtype_bytes) |
| 91 | self.assertEqual(result, expected_numel, | 91 | self.assertEqual(result, expected_numel, |
| 92 | msg=f"aligned_numel_32byte({input_numel}) for {dtype} " | 92 | msg=f"aligned_numel_32byte({input_numel}) for {dtype} " |
| 93 | f"returned {result}, expected {expected_numel}") | 93 | f"returned {result}, expected {expected_numel}") |
| 94 | 94 | ||
| 95 | 95 | ||
| 96 | if __name__ == "__main__": | 96 | if __name__ == "__main__": |
| 97 | run_tests() | 97 | run_tests() |
| @@ -1,35 +1,35 @@ | |||
| 1 | import unittest | 1 | import unittest |
| 2 | import torch | 2 | import torch |
| 3 | 3 | ||
| 4 | from testutils import TestUtils | 4 | from testutils import TestUtils |
| 5 | from torch.testing._internal.common_utils import run_tests, parametrize, instantiate_parametrized_tests | 5 | from torch.testing._internal.common_utils import run_tests, parametrize, instantiate_parametrized_tests |
| 6 | from torch._inductor import config | 6 | from torch._inductor import config |
| 7 | from torch._inductor.utils import run_and_get_code | 7 | from torch._inductor.utils import run_and_get_code |
| 8 | 8 | ||
| 9 | import torch_npu | 9 | import torch_npu |
| 10 | import torch_npu._inductor | 10 | import torch_npu._inductor |
| 11 | 11 | ||
| 12 | DEVICE = "npu" | 12 | DEVICE = "npu" |
| 13 | 13 | ||
| 14 | 14 | ||
| 15 | class TestCodegenUpcastToFP32(TestUtils): | 15 | class TestCodegenUpcastToFP32(TestUtils): |
| 16 | @parametrize("dtype", [torch.float16, torch.bfloat16]) | 16 | @parametrize("dtype", [torch.float16, torch.bfloat16]) |
| 17 | @parametrize("upcast_flag", [True, False]) | 17 | @parametrize("upcast_flag", [True, False]) |
| 18 | def test_codegen_upcast_to_fp32_emits_cast(self, dtype, upcast_flag): | 18 | def test_codegen_upcast_to_fp32_emits_cast(self, dtype, upcast_flag): |
| 19 | @torch.compile(backend="inductor") | 19 | @torch.compile(backend="inductor") |
| 20 | def func(x): | 20 | def func(x): |
| 21 | return torch.abs(x) | 21 | return torch.abs(x) |
| 22 | 22 | ||
| 23 | x = torch.randn((1024, 1024), device=DEVICE, dtype=dtype) | 23 | x = torch.randn((1024, 1024), device=DEVICE, dtype=dtype) |
| 24 | 24 | ||
| 25 | with config.patch("triton.codegen_upcast_to_fp32", upcast_flag): | 25 | with config.patch("triton.codegen_upcast_to_fp32", upcast_flag): |
| 26 | opt_func = torch._dynamo.optimize("inductor")(func) | 26 | opt_func = torch._dynamo.optimize("inductor")(func) |
| 27 | out, code = run_and_get_code(opt_func, x) | 27 | out, code = run_and_get_code(opt_func, x) |
| 28 | 28 | ||
| 29 | self.assertTrue(".to(tl.float32)" in code[0]) | 29 | self.assertTrue(".to(tl.float32)" in code[0]) |
| 30 | self.assertEqual(func(x), opt_func(x)) | 30 | self.assertEqual(func(x), opt_func(x)) |
| 31 | 31 | ||
| 32 | instantiate_parametrized_tests(TestCodegenUpcastToFP32) | 32 | instantiate_parametrized_tests(TestCodegenUpcastToFP32) |
| 33 | 33 | ||
| 34 | if __name__ == "__main__": | 34 | if __name__ == "__main__": |
| 35 | run_tests() | 35 | run_tests() |
| @@ -1,50 +1,50 @@ | |||
| 1 | import unittest | 1 | import unittest |
| 2 | import torch | 2 | import torch |
| 3 | import torch_npu | 3 | import torch_npu |
| 4 | 4 | ||
| 5 | from torch.testing._internal.common_utils import ( | 5 | from torch.testing._internal.common_utils import ( |
| 6 | run_tests, | 6 | run_tests, |
| 7 | parametrize, | 7 | parametrize, |
| 8 | instantiate_parametrized_tests, | 8 | instantiate_parametrized_tests, |
| 9 | ) | 9 | ) |
| 10 | from testutils import TestUtils | 10 | from testutils import TestUtils |
| 11 | 11 | ||
| 12 | 12 | ||
| 13 | class TestInductorStaticKernel(TestUtils): | 13 | class TestInductorStaticKernel(TestUtils): |
| 14 | 14 | ||
| 15 | def simple_op(self, x): | 15 | def simple_op(self, x): |
| 16 | return torch.neg(x) | 16 | return torch.neg(x) |
| 17 | 17 | ||
| 18 | @parametrize("shape", [(1024, 1024), (4096,)]) | 18 | @parametrize("shape", [(1024, 1024), (4096,)]) |
| 19 | @parametrize("dtype", [torch.float16, torch.float32]) | 19 | @parametrize("dtype", [torch.float16, torch.float32]) |
| 20 | def test_inductor_static_kernel(self, shape, dtype): | 20 | def test_inductor_static_kernel(self, shape, dtype): |
| 21 | device = "npu" | 21 | device = "npu" |
| 22 | 22 | ||
| 23 | x = torch.randn(shape, dtype=dtype, device=device) | 23 | x = torch.randn(shape, dtype=dtype, device=device) |
| 24 | 24 | ||
| 25 | ref = self.simple_op(x) | 25 | ref = self.simple_op(x) |
| 26 | 26 | ||
| 27 | torch._inductor.config.triton.cudagraph_trees = False | 27 | torch._inductor.config.triton.cudagraph_trees = False |
| 28 | torch_npu.npu.aclnn._use_static_aclnn_kernel = True | 28 | torch_npu.npu.aclnn._use_static_aclnn_kernel = True |
| 29 | 29 | ||
| 30 | compiled_fn = torch.compile( | 30 | compiled_fn = torch.compile( |
| 31 | self.simple_op, | 31 | self.simple_op, |
| 32 | backend="inductor", | 32 | backend="inductor", |
| 33 | dynamic=False | 33 | dynamic=False |
| 34 | ) | 34 | ) |
| 35 | 35 | ||
| 36 | for _ in range(3): | 36 | for _ in range(3): |
| 37 | compiled_fn(x) | 37 | compiled_fn(x) |
| 38 | 38 | ||
| 39 | torch.npu.synchronize() | 39 | torch.npu.synchronize() |
| 40 | 40 | ||
| 41 | out = compiled_fn(x) | 41 | out = compiled_fn(x) |
| 42 | torch.npu.synchronize() | 42 | torch.npu.synchronize() |
| 43 | 43 | ||
| 44 | self.assertEqual(ref, out) | 44 | self.assertEqual(ref, out) |
| 45 | 45 | ||
| 46 | instantiate_parametrized_tests(TestInductorStaticKernel) | 46 | instantiate_parametrized_tests(TestInductorStaticKernel) |
| 47 | 47 | ||
| 48 | if __name__ == "__main__": | 48 | if __name__ == "__main__": |
| 49 | torch.npu.config.allow_internal_format = False | 49 | torch.npu.config.allow_internal_format = False |
| 50 | run_tests() | 50 | run_tests() |
| @@ -73,4 +73,3 @@ class BenchmarkTestUtils(TestCase): | |||
| 73 | writer.writeheader() | 73 | writer.writeheader() |
| 74 | 74 | ||
| 75 | writer.writerow(perf_info) | 75 | writer.writerow(perf_info) |
| 76 | |||
| @@ -1,30 +1,30 @@ | |||
| 1 | import torch | 1 | import torch |
| 2 | from torch.testing._internal.common_utils import ( | 2 | from torch.testing._internal.common_utils import ( |
| 3 | run_tests, | 3 | run_tests, |
| 4 | TestCase, | 4 | TestCase, |
| 5 | ) | 5 | ) |
| 6 | import torch_npu | 6 | import torch_npu |
| 7 | 7 | ||
| 8 | class TestAutogradFallback(TestCase): | 8 | class TestAutogradFallback(TestCase): |
| 9 | 9 | ||
| 10 | def test_pad_backward_warn(self): | 10 | def test_pad_backward_warn(self): |
| 11 | 11 | ||
| 12 | def _exec_npu_pad(): | 12 | def _exec_npu_pad(): |
| 13 | npu_input = torch.randn(2, 3).npu() | 13 | npu_input = torch.randn(2, 3).npu() |
| 14 | npu_input.requires_grad = True | 14 | npu_input.requires_grad = True |
| 15 | pads = (1, 1, 1, 1) | 15 | pads = (1, 1, 1, 1) |
| 16 | output = torch_npu.npu_pad(npu_input, pads) | 16 | output = torch_npu.npu_pad(npu_input, pads) |
| 17 | output.backward(torch.ones_like(output)) | 17 | output.backward(torch.ones_like(output)) |
| 18 | 18 | ||
| 19 | # When set to "nothing," calling the reverse function directly causes an error. | 19 | # When set to "nothing," calling the reverse function directly causes an error. |
| 20 | torch._C._set_autograd_fallback_mode("nothing") | 20 | torch._C._set_autograd_fallback_mode("nothing") |
| 21 | with self.assertRaisesRegex(RuntimeError, "does not require grad"): | 21 | with self.assertRaisesRegex(RuntimeError, "does not require grad"): |
| 22 | _exec_npu_pad() | 22 | _exec_npu_pad() |
| 23 | 23 | ||
| 24 | # When set to "warn," calling the print function emits a warning. | 24 | # When set to "warn," calling the print function emits a warning. |
| 25 | torch._C._set_autograd_fallback_mode("warn") | 25 | torch._C._set_autograd_fallback_mode("warn") |
| 26 | _exec_npu_pad() | 26 | _exec_npu_pad() |
| 27 | 27 | ||
| 28 | 28 | ||
| 29 | if __name__ == "__main__": | 29 | if __name__ == "__main__": |
| 30 | run_tests() | 30 | run_tests() |
| @@ -1,41 +1,41 @@ | |||
| 1 | import numpy as np | 1 | import numpy as np |
| 2 | import torch | 2 | import torch |
| 3 | 3 | ||
| 4 | import torch_npu | 4 | import torch_npu |
| 5 | from torch_npu.testing.testcase import TestCase, run_tests | 5 | from torch_npu.testing.testcase import TestCase, run_tests |
| 6 | from torch_npu.testing.common_utils import create_common_tensor | 6 | from torch_npu.testing.common_utils import create_common_tensor |
| 7 | 7 | ||
| 8 | 8 | ||
| 9 | class TestFastGelu(TestCase): | 9 | class TestFastGelu(TestCase): |
| 10 | 10 | ||
| 11 | def supported_op_exec(self, input1): | 11 | def supported_op_exec(self, input1): |
| 12 | attr = 1.702 | 12 | attr = 1.702 |
| 13 | attr_half = attr / 2 | 13 | attr_half = attr / 2 |
| 14 | abs_input1 = torch.abs(input1) | 14 | abs_input1 = torch.abs(input1) |
| 15 | numerator = input1 * torch.exp((attr_half * input1) * (input1 - abs_input1)) | 15 | numerator = input1 * torch.exp((attr_half * input1) * (input1 - abs_input1)) |
| 16 | denominator = 1.0 + torch.exp(- attr * abs_input1) | 16 | denominator = 1.0 + torch.exp(- attr * abs_input1) |
| 17 | output = numerator / denominator | 17 | output = numerator / denominator |
| 18 | return output.cpu().detach() | 18 | return output.cpu().detach() |
| 19 | 19 | ||
| 20 | def custom_op_exec(self, input1): | 20 | def custom_op_exec(self, input1): |
| 21 | output = torch_npu.fast_gelu(input1) | 21 | output = torch_npu.fast_gelu(input1) |
| 22 | return output.cpu().detach() | 22 | return output.cpu().detach() |
| 23 | 23 | ||
| 24 | def test_fast_gelu(self, device="npu"): | 24 | def test_fast_gelu(self, device="npu"): |
| 25 | item = [np.float32, 0, [3, 16, 32]] | 25 | item = [np.float32, 0, [3, 16, 32]] |
| 26 | _, npu_input = create_common_tensor(item, 0, 100) | 26 | _, npu_input = create_common_tensor(item, 0, 100) |
| 27 | 27 | ||
| 28 | supported_output = self.supported_op_exec(npu_input) | 28 | supported_output = self.supported_op_exec(npu_input) |
| 29 | custom_output = self.custom_op_exec(npu_input) | 29 | custom_output = self.custom_op_exec(npu_input) |
| 30 | self.assertRtolEqual(supported_output, custom_output) | 30 | self.assertRtolEqual(supported_output, custom_output) |
| 31 | 31 | ||
| 32 | def test_fast_gelu_input_arg(self): | 32 | def test_fast_gelu_input_arg(self): |
| 33 | item = [np.float32, 0, [3, 16, 32]] | 33 | item = [np.float32, 0, [3, 16, 32]] |
| 34 | _, npu_input = create_common_tensor(item, 0, 100) | 34 | _, npu_input = create_common_tensor(item, 0, 100) |
| 35 | supported_output = self.supported_op_exec(npu_input) | 35 | supported_output = self.supported_op_exec(npu_input) |
| 36 | custom_output = torch_npu.fast_gelu(input=npu_input) | 36 | custom_output = torch_npu.fast_gelu(input=npu_input) |
| 37 | self.assertRtolEqual(supported_output, custom_output) | 37 | self.assertRtolEqual(supported_output, custom_output) |
| 38 | 38 | ||
| 39 | 39 | ||
| 40 | if __name__ == "__main__": | 40 | if __name__ == "__main__": |
| 41 | run_tests() | 41 | run_tests() |
| @@ -1,35 +1,35 @@ | |||
| 1 | import numpy as np | 1 | import numpy as np |
| 2 | import torch | 2 | import torch |
| 3 | 3 | ||
| 4 | import torch_npu | 4 | import torch_npu |
| 5 | from torch_npu.testing.testcase import TestCase, run_tests | 5 | from torch_npu.testing.testcase import TestCase, run_tests |
| 6 | from torch_npu.testing.common_utils import create_common_tensor | 6 | from torch_npu.testing.common_utils import create_common_tensor |
| 7 | 7 | ||
| 8 | 8 | ||
| 9 | class TestConfusionTranspose(TestCase): | 9 | class TestConfusionTranspose(TestCase): |
| 10 | 10 | ||
| 11 | def supported_op_exec(self, input1, perm, shape, transpose_first): | 11 | def supported_op_exec(self, input1, perm, shape, transpose_first): |
| 12 | if transpose_first: | 12 | if transpose_first: |
| 13 | output = input1.permute(*perm).contiguous().view(shape) | 13 | output = input1.permute(*perm).contiguous().view(shape) |
| 14 | else: | 14 | else: |
| 15 | output = input1.view(shape).permute(*perm) | 15 | output = input1.view(shape).permute(*perm) |
| 16 | return output.cpu().detach() | 16 | return output.cpu().detach() |
| 17 | 17 | ||
| 18 | def custom_op_exec(self, input1, perm, shape, transpose_first): | 18 | def custom_op_exec(self, input1, perm, shape, transpose_first): |
| 19 | output = torch_npu.npu_confusion_transpose(input1, perm, shape, transpose_first) | 19 | output = torch_npu.npu_confusion_transpose(input1, perm, shape, transpose_first) |
| 20 | return output.cpu().detach() | 20 | return output.cpu().detach() |
| 21 | 21 | ||
| 22 | def test_npu_confusion_transpose(self, device="npu"): | 22 | def test_npu_confusion_transpose(self, device="npu"): |
| 23 | item = [np.float32, 0, [1, 576, 2560]] | 23 | item = [np.float32, 0, [1, 576, 2560]] |
| 24 | _, npu_input = create_common_tensor(item, 0, 100) | 24 | _, npu_input = create_common_tensor(item, 0, 100) |
| 25 | perm = (0, 2, 1, 3) | 25 | perm = (0, 2, 1, 3) |
| 26 | shape = [1, 576, 32, 80] | 26 | shape = [1, 576, 32, 80] |
| 27 | transpose_first = False | 27 | transpose_first = False |
| 28 | 28 | ||
| 29 | supported_output = self.supported_op_exec(npu_input, perm, shape, transpose_first) | 29 | supported_output = self.supported_op_exec(npu_input, perm, shape, transpose_first) |
| 30 | custom_output = self.custom_op_exec(npu_input, perm, shape, transpose_first) | 30 | custom_output = self.custom_op_exec(npu_input, perm, shape, transpose_first) |
| 31 | self.assertRtolEqual(supported_output, custom_output) | 31 | self.assertRtolEqual(supported_output, custom_output) |
| 32 | 32 | ||
| 33 | 33 | ||
| 34 | if __name__ == "__main__": | 34 | if __name__ == "__main__": |
| 35 | run_tests() | 35 | run_tests() |
| @@ -1,52 +1,52 @@ | |||
| 1 | # | 1 | # |
| 2 | 2 | ||
| 3 | import numpy as np | 3 | import numpy as np |
| 4 | import torch | 4 | import torch |
| 5 | 5 | ||
| 6 | import torch_npu | 6 | import torch_npu |
| 7 | from torch_npu.testing.testcase import TestCase, run_tests | 7 | from torch_npu.testing.testcase import TestCase, run_tests |
| 8 | from torch_npu.testing.common_utils import create_common_tensor | 8 | from torch_npu.testing.common_utils import create_common_tensor |
| 9 | 9 | ||
| 10 | 10 | ||
| 11 | class TestConvolution(TestCase): | 11 | class TestConvolution(TestCase): |
| 12 | 12 | ||
| 13 | def supported_op_exec(self, input1, weight, bias, stride, padding, dilation, groups): | 13 | def supported_op_exec(self, input1, weight, bias, stride, padding, dilation, groups): |
| 14 | dim = input1.dim() | 14 | dim = input1.dim() |
| 15 | if dim == 4: | 15 | if dim == 4: |
| 16 | output = torch.nn.functional.conv2d(input1, weight, bias, stride, padding, dilation, groups) | 16 | output = torch.nn.functional.conv2d(input1, weight, bias, stride, padding, dilation, groups) |
| 17 | if dim == 5: | 17 | if dim == 5: |
| 18 | is_dilated = False | 18 | is_dilated = False |
| 19 | for d in dilation: | 19 | for d in dilation: |
| 20 | is_dilated |= (d != 1) | 20 | is_dilated |= (d != 1) |
| 21 | if groups == 1 and not is_dilated: | 21 | if groups == 1 and not is_dilated: |
| 22 | kernel_size = weight.size()[2] | 22 | kernel_size = weight.size()[2] |
| 23 | output = torch._C._nn.slow_conv3d(input1, weight, kernel_size, bias, stride, padding) | 23 | output = torch._C._nn.slow_conv3d(input1, weight, kernel_size, bias, stride, padding) |
| 24 | else: | 24 | else: |
| 25 | output = torch.nn.functional.conv3d(input1, weight, bias, stride, padding, dilation, groups) | 25 | output = torch.nn.functional.conv3d(input1, weight, bias, stride, padding, dilation, groups) |
| 26 | return output.cpu().detach() | 26 | return output.cpu().detach() |
| 27 | 27 | ||
| 28 | def custom_op_exec(self, input1, weight, bias, stride, padding, dilation, groups): | 28 | def custom_op_exec(self, input1, weight, bias, stride, padding, dilation, groups): |
| 29 | output = torch_npu.npu_convolution(input1, weight, bias, stride, padding, dilation, groups) | 29 | output = torch_npu.npu_convolution(input1, weight, bias, stride, padding, dilation, groups) |
| 30 | return output.cpu().detach() | 30 | return output.cpu().detach() |
| 31 | 31 | ||
| 32 | def test_npu_convolution(self, device="npu"): | 32 | def test_npu_convolution(self, device="npu"): |
| 33 | items = [[[np.float32, 0, [16, 128, 112, 112]], [np.float32, 0, [256, 128, 3, 3]], [np.float32, 2, [256]], | 33 | items = [[[np.float32, 0, [16, 128, 112, 112]], [np.float32, 0, [256, 128, 3, 3]], [np.float32, 2, [256]], |
| 34 | [1, 1], [1, 1], [1, 1], 1], | 34 | [1, 1], [1, 1], [1, 1], 1], |
| 35 | [[np.float16, 30, [1, 128, 4, 14, 14]], [np.float16, 30, [1, 128, 3, 3, 3]], None, | 35 | [[np.float16, 30, [1, 128, 4, 14, 14]], [np.float16, 30, [1, 128, 3, 3, 3]], None, |
| 36 | [1, 1, 1], [1, 1, 1], [1, 1, 1], 1]] | 36 | [1, 1, 1], [1, 1, 1], [1, 1, 1], 1]] |
| 37 | for item in items: | 37 | for item in items: |
| 38 | _, npu_input = create_common_tensor(item[0], -1, 1) | 38 | _, npu_input = create_common_tensor(item[0], -1, 1) |
| 39 | _, weight = create_common_tensor(item[1], -1, 1) | 39 | _, weight = create_common_tensor(item[1], -1, 1) |
| 40 | _, bias = create_common_tensor(item[2], -1, 1) if item[2] else _, None | 40 | _, bias = create_common_tensor(item[2], -1, 1) if item[2] else _, None |
| 41 | stride = item[3] | 41 | stride = item[3] |
| 42 | padding = item[4] | 42 | padding = item[4] |
| 43 | dilation = item[5] | 43 | dilation = item[5] |
| 44 | groups = item[6] | 44 | groups = item[6] |
| 45 | 45 | ||
| 46 | supported_output = self.supported_op_exec(npu_input, weight, bias, stride, padding, dilation, groups) | 46 | supported_output = self.supported_op_exec(npu_input, weight, bias, stride, padding, dilation, groups) |
| 47 | custom_output = self.custom_op_exec(npu_input, weight, bias, stride, padding, dilation, groups) | 47 | custom_output = self.custom_op_exec(npu_input, weight, bias, stride, padding, dilation, groups) |
| 48 | self.assertRtolEqual(supported_output, custom_output) | 48 | self.assertRtolEqual(supported_output, custom_output) |
| 49 | 49 | ||
| 50 | 50 | ||
| 51 | if __name__ == "__main__": | 51 | if __name__ == "__main__": |
| 52 | run_tests() | 52 | run_tests() |
| @@ -1,50 +1,50 @@ | |||
| 1 | import numpy as np | 1 | import numpy as np |
| 2 | import torch | 2 | import torch |
| 3 | 3 | ||
| 4 | import torch_npu | 4 | import torch_npu |
| 5 | from torch_npu.testing.testcase import TestCase, run_tests | 5 | from torch_npu.testing.testcase import TestCase, run_tests |
| 6 | from torch_npu.testing.common_utils import create_common_tensor | 6 | from torch_npu.testing.common_utils import create_common_tensor |
| 7 | from torch_npu.testing.common_utils import SupportedDevices | 7 | from torch_npu.testing.common_utils import SupportedDevices |
| 8 | 8 | ||
| 9 | 9 | ||
| 10 | class TestDtypeCast(TestCase): | 10 | class TestDtypeCast(TestCase): |
| 11 | 11 | ||
| 12 | def supported_op_exec(self, input1, dst_dtype): | 12 | def supported_op_exec(self, input1, dst_dtype): |
| 13 | output = input1.to(dst_dtype) | 13 | output = input1.to(dst_dtype) |
| 14 | return output.cpu().detach() | 14 | return output.cpu().detach() |
| 15 | 15 | ||
| 16 | def custom_op_exec(self, input1, dst_dtype): | 16 | def custom_op_exec(self, input1, dst_dtype): |
| 17 | output = torch_npu.npu_dtype_cast(input1, dst_dtype) | 17 | output = torch_npu.npu_dtype_cast(input1, dst_dtype) |
| 18 | return output.cpu().detach() | 18 | return output.cpu().detach() |
| 19 | 19 | ||
| 20 | def test_npu_dtype_cast(self): | 20 | def test_npu_dtype_cast(self): |
| 21 | item = [np.float32, 0, (64, 10)] | 21 | item = [np.float32, 0, (64, 10)] |
| 22 | _, npu_input = create_common_tensor(item, -1, 1) | 22 | _, npu_input = create_common_tensor(item, -1, 1) |
| 23 | dst_dtype = torch.float16 | 23 | dst_dtype = torch.float16 |
| 24 | 24 | ||
| 25 | supported_output = self.supported_op_exec(npu_input, dst_dtype) | 25 | supported_output = self.supported_op_exec(npu_input, dst_dtype) |
| 26 | custom_output = self.custom_op_exec(npu_input, dst_dtype) | 26 | custom_output = self.custom_op_exec(npu_input, dst_dtype) |
| 27 | self.assertRtolEqual(supported_output, custom_output) | 27 | self.assertRtolEqual(supported_output, custom_output) |
| 28 | 28 | ||
| 29 | def test_npu_dtype_cast_double_backward(self): | 29 | def test_npu_dtype_cast_double_backward(self): |
| 30 | x = torch.randn(3, 3, requires_grad=True).to("npu") | 30 | x = torch.randn(3, 3, requires_grad=True).to("npu") |
| 31 | y = torch_npu.npu_dtype_cast(x, torch.half) | 31 | y = torch_npu.npu_dtype_cast(x, torch.half) |
| 32 | z = torch.autograd.grad(outputs=y, inputs=x, grad_outputs=torch.ones_like(y)) | 32 | z = torch.autograd.grad(outputs=y, inputs=x, grad_outputs=torch.ones_like(y)) |
| 33 | self.assertIsNone(z[0].grad_fn) | 33 | self.assertIsNone(z[0].grad_fn) |
| 34 | z = torch.autograd.grad(outputs=y, inputs=x, grad_outputs=torch.ones_like(y), create_graph=True) | 34 | z = torch.autograd.grad(outputs=y, inputs=x, grad_outputs=torch.ones_like(y), create_graph=True) |
| 35 | self.assertIsNotNone(z[0].grad_fn) | 35 | self.assertIsNotNone(z[0].grad_fn) |
| 36 | 36 | ||
| 37 | @SupportedDevices(['Ascend910B']) | 37 | @SupportedDevices(['Ascend910B']) |
| 38 | def test_npu_dtype_cast_complex(self): | 38 | def test_npu_dtype_cast_complex(self): |
| 39 | x = torch.empty([2, 3], dtype=torch.complex64, device="npu") | 39 | x = torch.empty([2, 3], dtype=torch.complex64, device="npu") |
| 40 | x.requires_grad_() | 40 | x.requires_grad_() |
| 41 | y = torch_npu.npu_dtype_cast(x, torch.complex128) | 41 | y = torch_npu.npu_dtype_cast(x, torch.complex128) |
| 42 | grad_fn = str(y.grad_fn) | 42 | grad_fn = str(y.grad_fn) |
| 43 | self.assertTrue("NpuDtypeCastBackward" in grad_fn) | 43 | self.assertTrue("NpuDtypeCastBackward" in grad_fn) |
| 44 | 44 | ||
| 45 | with self.assertRaisesRegex(RuntimeError, r'grad can be implicitly created'): | 45 | with self.assertRaisesRegex(RuntimeError, r'grad can be implicitly created'): |
| 46 | y.sum().backward() | 46 | y.sum().backward() |
| 47 | 47 | ||
| 48 | 48 | ||
| 49 | if __name__ == "__main__": | 49 | if __name__ == "__main__": |
| 50 | run_tests() | 50 | run_tests() |
| @@ -1,31 +1,31 @@ | |||
| 1 | import numpy as np | 1 | import numpy as np |
| 2 | import torch | 2 | import torch |
| 3 | 3 | ||
| 4 | import torch_npu | 4 | import torch_npu |
| 5 | from torch_npu.testing.testcase import TestCase, run_tests | 5 | from torch_npu.testing.testcase import TestCase, run_tests |
| 6 | from torch_npu.testing.common_utils import create_common_tensor | 6 | from torch_npu.testing.common_utils import create_common_tensor |
| 7 | 7 | ||
| 8 | 8 | ||
| 9 | class TestFormatCast(TestCase): | 9 | class TestFormatCast(TestCase): |
| 10 | 10 | ||
| 11 | def supported_op_exec(self, input1): | 11 | def supported_op_exec(self, input1): |
| 12 | m = torch.nn.Identity(54, unused_argument1=0.1, unused_argument2=False) | 12 | m = torch.nn.Identity(54, unused_argument1=0.1, unused_argument2=False) |
| 13 | output = m(input1) | 13 | output = m(input1) |
| 14 | return output.cpu().detach() | 14 | return output.cpu().detach() |
| 15 | 15 | ||
| 16 | def custom_op_exec(self, input1, acl_format): | 16 | def custom_op_exec(self, input1, acl_format): |
| 17 | output = torch_npu.npu_format_cast(input1, acl_format) | 17 | output = torch_npu.npu_format_cast(input1, acl_format) |
| 18 | return output.cpu().detach() | 18 | return output.cpu().detach() |
| 19 | 19 | ||
| 20 | def test_npu_format_cast(self, device="npu"): | 20 | def test_npu_format_cast(self, device="npu"): |
| 21 | item = [np.float16, 0, (2, 2, 4, 4)] | 21 | item = [np.float16, 0, (2, 2, 4, 4)] |
| 22 | _, npu_input = create_common_tensor(item, -1, 1) | 22 | _, npu_input = create_common_tensor(item, -1, 1) |
| 23 | acl_format = 3 | 23 | acl_format = 3 |
| 24 | 24 | ||
| 25 | supported_output = self.supported_op_exec(npu_input) | 25 | supported_output = self.supported_op_exec(npu_input) |
| 26 | custom_output = self.custom_op_exec(npu_input, acl_format) | 26 | custom_output = self.custom_op_exec(npu_input, acl_format) |
| 27 | self.assertRtolEqual(supported_output, custom_output) | 27 | self.assertRtolEqual(supported_output, custom_output) |
| 28 | 28 | ||
| 29 | 29 | ||
| 30 | if __name__ == "__main__": | 30 | if __name__ == "__main__": |
| 31 | run_tests() | 31 | run_tests() |
| @@ -1,44 +1,44 @@ | |||
| 1 | import torch | 1 | import torch |
| 2 | 2 | ||
| 3 | import torch_npu | 3 | import torch_npu |
| 4 | from torch_npu.testing.testcase import TestCase, run_tests | 4 | from torch_npu.testing.testcase import TestCase, run_tests |
| 5 | 5 | ||
| 6 | 6 | ||
| 7 | class TestGridAssignPositive(TestCase): | 7 | class TestGridAssignPositive(TestCase): |
| 8 | 8 | ||
| 9 | def supported_op_exec(self, input1, box_responsible_flags, max_overlaps, argmax_overlaps, pos_iou_thr): | 9 | def supported_op_exec(self, input1, box_responsible_flags, max_overlaps, argmax_overlaps, pos_iou_thr): |
| 10 | pos_inds = (max_overlaps > pos_iou_thr) & box_responsible_flags.type(torch.bool) | 10 | pos_inds = (max_overlaps > pos_iou_thr) & box_responsible_flags.type(torch.bool) |
| 11 | argmax_overlaps = argmax_overlaps.to(input1.dtype) | 11 | argmax_overlaps = argmax_overlaps.to(input1.dtype) |
| 12 | input1[pos_inds] = argmax_overlaps[pos_inds] + 1 | 12 | input1[pos_inds] = argmax_overlaps[pos_inds] + 1 |
| 13 | return input1.cpu().detach() | 13 | return input1.cpu().detach() |
| 14 | 14 | ||
| 15 | def custom_op_exec(self, input1, overlaps, box_responsible_flags, max_overlaps, argmax_overlaps, | 15 | def custom_op_exec(self, input1, overlaps, box_responsible_flags, max_overlaps, argmax_overlaps, |
| 16 | gt_max_overlaps, gt_argmax_overlaps, num_gts, pos_iou_thr, min_pos_iou, gt_max_assign_all): | 16 | gt_max_overlaps, gt_argmax_overlaps, num_gts, pos_iou_thr, min_pos_iou, gt_max_assign_all): |
| 17 | output = torch_npu.npu_grid_assign_positive(input1, overlaps, box_responsible_flags, max_overlaps, | 17 | output = torch_npu.npu_grid_assign_positive(input1, overlaps, box_responsible_flags, max_overlaps, |
| 18 | argmax_overlaps, gt_max_overlaps, gt_argmax_overlaps, num_gts, | 18 | argmax_overlaps, gt_max_overlaps, gt_argmax_overlaps, num_gts, |
| 19 | pos_iou_thr, min_pos_iou, gt_max_assign_all) | 19 | pos_iou_thr, min_pos_iou, gt_max_assign_all) |
| 20 | return output.cpu().detach() | 20 | return output.cpu().detach() |
| 21 | 21 | ||
| 22 | def test_npu_grid_assign_positive(self): | 22 | def test_npu_grid_assign_positive(self): |
| 23 | npu_input = torch.rand((4,), dtype=torch.float32).to("npu") | 23 | npu_input = torch.rand((4,), dtype=torch.float32).to("npu") |
| 24 | overlaps = torch.rand((2, 4), dtype=torch.float32).to("npu") | 24 | overlaps = torch.rand((2, 4), dtype=torch.float32).to("npu") |
| 25 | box_responsible_flags = torch.tensor([1, 1, 1, 0], dtype=torch.uint8).to("npu") | 25 | box_responsible_flags = torch.tensor([1, 1, 1, 0], dtype=torch.uint8).to("npu") |
| 26 | max_overlaps = torch.rand((4,), dtype=torch.float32).to("npu") | 26 | max_overlaps = torch.rand((4,), dtype=torch.float32).to("npu") |
| 27 | argmax_overlaps = torch.tensor([1, 0, 1, 0], dtype=torch.int32).to("npu") | 27 | argmax_overlaps = torch.tensor([1, 0, 1, 0], dtype=torch.int32).to("npu") |
| 28 | gt_max_overlaps = torch.rand((2,), dtype=torch.float32).to("npu") | 28 | gt_max_overlaps = torch.rand((2,), dtype=torch.float32).to("npu") |
| 29 | gt_argmax_overlaps = torch.tensor([1, 0], dtype=torch.int32).to("npu") | 29 | gt_argmax_overlaps = torch.tensor([1, 0], dtype=torch.int32).to("npu") |
| 30 | num_gts = 128 | 30 | num_gts = 128 |
| 31 | pos_iou_thr = .5 | 31 | pos_iou_thr = .5 |
| 32 | min_pos_iou = .0 | 32 | min_pos_iou = .0 |
| 33 | gt_max_assign_all = True | 33 | gt_max_assign_all = True |
| 34 | 34 | ||
| 35 | supported_output = self.supported_op_exec(npu_input, box_responsible_flags, max_overlaps, | 35 | supported_output = self.supported_op_exec(npu_input, box_responsible_flags, max_overlaps, |
| 36 | argmax_overlaps, pos_iou_thr) | 36 | argmax_overlaps, pos_iou_thr) |
| 37 | custom_output = self.custom_op_exec(npu_input, overlaps, box_responsible_flags, max_overlaps, | 37 | custom_output = self.custom_op_exec(npu_input, overlaps, box_responsible_flags, max_overlaps, |
| 38 | argmax_overlaps, gt_max_overlaps, gt_argmax_overlaps, num_gts, | 38 | argmax_overlaps, gt_max_overlaps, gt_argmax_overlaps, num_gts, |
| 39 | pos_iou_thr, min_pos_iou, gt_max_assign_all) | 39 | pos_iou_thr, min_pos_iou, gt_max_assign_all) |
| 40 | self.assertRtolEqual(supported_output, custom_output) | 40 | self.assertRtolEqual(supported_output, custom_output) |
| 41 | 41 | ||
| 42 | 42 | ||
| 43 | if __name__ == "__main__": | 43 | if __name__ == "__main__": |
| 44 | run_tests() | 44 | run_tests() |
| @@ -1,94 +1,94 @@ | |||
| 1 | import numpy as np | 1 | import numpy as np |
| 2 | import torch | 2 | import torch |
| 3 | 3 | ||
| 4 | import torch_npu | 4 | import torch_npu |
| 5 | from torch_npu.testing.testcase import TestCase, run_tests | 5 | from torch_npu.testing.testcase import TestCase, run_tests |
| 6 | from torch_npu.testing.common_utils import create_common_tensor | 6 | from torch_npu.testing.common_utils import create_common_tensor |
| 7 | 7 | ||
| 8 | 8 | ||
| 9 | class TestPSROIPooling(TestCase): | 9 | class TestPSROIPooling(TestCase): |
| 10 | 10 | ||
| 11 | def cal_sum(self, hstart, hend, wstart, wend, image): | 11 | def cal_sum(self, hstart, hend, wstart, wend, image): |
| 12 | out_sum = 0.0 | 12 | out_sum = 0.0 |
| 13 | for row in range(hstart, hend): | 13 | for row in range(hstart, hend): |
| 14 | for col in range(wstart, wend): | 14 | for col in range(wstart, wend): |
| 15 | out_sum += image[row][col] | 15 | out_sum += image[row][col] |
| 16 | return out_sum | 16 | return out_sum |
| 17 | 17 | ||
| 18 | def supported_op_exec(self, input1, rois, spatial_scale, group_size, output_dim): | 18 | def supported_op_exec(self, input1, rois, spatial_scale, group_size, output_dim): |
| 19 | dst_type = input1.dtype | 19 | dst_type = input1.dtype |
| 20 | if dst_type != torch.float32: | 20 | if dst_type != torch.float32: |
| 21 | input1 = input1.to(torch.float32) | 21 | input1 = input1.to(torch.float32) |
| 22 | rois = rois.to(torch.float32) | 22 | rois = rois.to(torch.float32) |
| 23 | 23 | ||
| 24 | n, channels, height, width = input1.shape | 24 | n, channels, height, width = input1.shape |
| 25 | tensor_height = torch.tensor([height]).npu() | 25 | tensor_height = torch.tensor([height]).npu() |
| 26 | tensor_width = torch.tensor([width]).npu() | 26 | tensor_width = torch.tensor([width]).npu() |
| 27 | tensor_zero = torch.tensor([0]).npu() | 27 | tensor_zero = torch.tensor([0]).npu() |
| 28 | tensor_one_tenth = torch.tensor([0.1]).npu() | 28 | tensor_one_tenth = torch.tensor([0.1]).npu() |
| 29 | 29 | ||
| 30 | output_size = [rois.size(0) * rois.size(2), output_dim, group_size, group_size] | 30 | output_size = [rois.size(0) * rois.size(2), output_dim, group_size, group_size] |
| 31 | rois = rois.transpose(2, 1) | 31 | rois = rois.transpose(2, 1) |
| 32 | rois = torch.reshape(rois, (rois.shape[0] * rois.shape[1], rois.shape[2])) | 32 | rois = torch.reshape(rois, (rois.shape[0] * rois.shape[1], rois.shape[2])) |
| 33 | output = torch.zeros(output_size).npu() | 33 | output = torch.zeros(output_size).npu() |
| 34 | for out_n in range(output_size[0]): | 34 | for out_n in range(output_size[0]): |
| 35 | for ctop in range(output_size[1]): | 35 | for ctop in range(output_size[1]): |
| 36 | for ph in range(output_size[2]): | 36 | for ph in range(output_size[2]): |
| 37 | for pw in range(output_size[3]): | 37 | for pw in range(output_size[3]): |
| 38 | roi_batch_ind = rois[out_n][0].to(int) | 38 | roi_batch_ind = rois[out_n][0].to(int) |
| 39 | roi_start_w = torch.round(rois[out_n][1]) * spatial_scale | 39 | roi_start_w = torch.round(rois[out_n][1]) * spatial_scale |
| 40 | roi_start_h = torch.round(rois[out_n][2]) * spatial_scale | 40 | roi_start_h = torch.round(rois[out_n][2]) * spatial_scale |
| 41 | roi_end_w = torch.round(rois[out_n][3] + 1.0) * spatial_scale | 41 | roi_end_w = torch.round(rois[out_n][3] + 1.0) * spatial_scale |
| 42 | roi_end_h = torch.round(rois[out_n][4] + 1.0) * spatial_scale | 42 | roi_end_h = torch.round(rois[out_n][4] + 1.0) * spatial_scale |
| 43 | # Force too small ROIs to be 1x1 | 43 | # Force too small ROIs to be 1x1 |
| 44 | roi_width = torch.max(roi_end_w - roi_start_w, tensor_one_tenth) | 44 | roi_width = torch.max(roi_end_w - roi_start_w, tensor_one_tenth) |
| 45 | roi_height = torch.max(roi_end_h - roi_start_h, tensor_one_tenth) | 45 | roi_height = torch.max(roi_end_h - roi_start_h, tensor_one_tenth) |
| 46 | # Compute w and h at bottom | 46 | # Compute w and h at bottom |
| 47 | bin_size_h = roi_height / group_size | 47 | bin_size_h = roi_height / group_size |
| 48 | bin_size_w = roi_width / group_size | 48 | bin_size_w = roi_width / group_size |
| 49 | 49 | ||
| 50 | # Add roi offsets and clip to input boundaries | 50 | # Add roi offsets and clip to input boundaries |
| 51 | hstart = (ph * bin_size_h + roi_start_h).to(int) | 51 | hstart = (ph * bin_size_h + roi_start_h).to(int) |
| 52 | wstart = torch.floor(pw * bin_size_w + roi_start_w).to(int) | 52 | wstart = torch.floor(pw * bin_size_w + roi_start_w).to(int) |
| 53 | hend = torch.ceil((ph + 1) * bin_size_h + roi_start_h).to(int) | 53 | hend = torch.ceil((ph + 1) * bin_size_h + roi_start_h).to(int) |
| 54 | wend = torch.ceil((pw + 1) * bin_size_w + roi_start_w).to(int) | 54 | wend = torch.ceil((pw + 1) * bin_size_w + roi_start_w).to(int) |
| 55 | hstart = torch.min(torch.max(hstart, tensor_zero), tensor_height).to(int) | 55 | hstart = torch.min(torch.max(hstart, tensor_zero), tensor_height).to(int) |
| 56 | hend = torch.min(torch.max(hend, tensor_zero), tensor_height).to(int) | 56 | hend = torch.min(torch.max(hend, tensor_zero), tensor_height).to(int) |
| 57 | wstart = torch.min(torch.max(wstart, tensor_zero), tensor_width).to(int) | 57 | wstart = torch.min(torch.max(wstart, tensor_zero), tensor_width).to(int) |
| 58 | wend = torch.min(torch.max(wend, tensor_zero), tensor_width).to(int) | 58 | wend = torch.min(torch.max(wend, tensor_zero), tensor_width).to(int) |
| 59 | is_empty = ((hend <= hstart) or (wend <= wstart)) | 59 | is_empty = ((hend <= hstart) or (wend <= wstart)) |
| 60 | c = (ctop * group_size + ph) * group_size + pw | 60 | c = (ctop * group_size + ph) * group_size + pw |
| 61 | out_sum = self.cal_sum(hstart, | 61 | out_sum = self.cal_sum(hstart, |
| 62 | hend, | 62 | hend, |
| 63 | wstart, | 63 | wstart, |
| 64 | wend, | 64 | wend, |
| 65 | image=input1[roi_batch_ind][c]) | 65 | image=input1[roi_batch_ind][c]) |
| 66 | bin_area = (hend - hstart) * (wend - wstart) | 66 | bin_area = (hend - hstart) * (wend - wstart) |
| 67 | if is_empty: | 67 | if is_empty: |
| 68 | output[out_n][ctop][ph][pw] = 0.0 | 68 | output[out_n][ctop][ph][pw] = 0.0 |
| 69 | else: | 69 | else: |
| 70 | output[out_n][ctop][ph][pw] = out_sum / bin_area | 70 | output[out_n][ctop][ph][pw] = out_sum / bin_area |
| 71 | 71 | ||
| 72 | if dst_type != torch.float32: | 72 | if dst_type != torch.float32: |
| 73 | output = output.to(dst_type) | 73 | output = output.to(dst_type) |
| 74 | return output.cpu().detach() | 74 | return output.cpu().detach() |
| 75 | 75 | ||
| 76 | def custom_op_exec(self, input1, rois, spatial_scale, group_size, output_dim): | 76 | def custom_op_exec(self, input1, rois, spatial_scale, group_size, output_dim): |
| 77 | output = torch_npu.npu_ps_roi_pooling(input1, rois, spatial_scale, group_size, output_dim) | 77 | output = torch_npu.npu_ps_roi_pooling(input1, rois, spatial_scale, group_size, output_dim) |
| 78 | return output.cpu().detach() | 78 | return output.cpu().detach() |
| 79 | 79 | ||
| 80 | def test_npu_ps_roi_pooling(self, device="npu"): | 80 | def test_npu_ps_roi_pooling(self, device="npu"): |
| 81 | item = [np.float32, 0, (2, 961, 127, 127)] | 81 | item = [np.float32, 0, (2, 961, 127, 127)] |
| 82 | _, npu_input = create_common_tensor(item, 0.1, 1) | 82 | _, npu_input = create_common_tensor(item, 0.1, 1) |
| 83 | rois = torch.tensor([[[0], [3], [2], [9], [9]], [[1], [1], [4], [9], [9]]], dtype=torch.float32).npu() | 83 | rois = torch.tensor([[[0], [3], [2], [9], [9]], [[1], [1], [4], [9], [9]]], dtype=torch.float32).npu() |
| 84 | spatial_scale = 0.25 | 84 | spatial_scale = 0.25 |
| 85 | group_size = 31 | 85 | group_size = 31 |
| 86 | output_dim = 1 | 86 | output_dim = 1 |
| 87 | 87 | ||
| 88 | supported_output = self.supported_op_exec(npu_input, rois, spatial_scale, group_size, output_dim) | 88 | supported_output = self.supported_op_exec(npu_input, rois, spatial_scale, group_size, output_dim) |
| 89 | custom_output = self.custom_op_exec(npu_input, rois, spatial_scale, group_size, output_dim) | 89 | custom_output = self.custom_op_exec(npu_input, rois, spatial_scale, group_size, output_dim) |
| 90 | self.assertRtolEqual(supported_output, custom_output) | 90 | self.assertRtolEqual(supported_output, custom_output) |
| 91 | 91 | ||
| 92 | 92 | ||
| 93 | if __name__ == "__main__": | 93 | if __name__ == "__main__": |
| 94 | run_tests() | 94 | run_tests() |
| @@ -1,31 +1,31 @@ | |||
| 1 | import numpy as np | 1 | import numpy as np |
| 2 | import torch | 2 | import torch |
| 3 | 3 | ||
| 4 | import torch_npu | 4 | import torch_npu |
| 5 | from torch_npu.testing.testcase import TestCase, run_tests | 5 | from torch_npu.testing.testcase import TestCase, run_tests |
| 6 | from torch_npu.testing.common_utils import create_common_tensor | 6 | from torch_npu.testing.common_utils import create_common_tensor |
| 7 | 7 | ||
| 8 | 8 | ||
| 9 | class TestSoftmaxCrossEntropyWithLogits(TestCase): | 9 | class TestSoftmaxCrossEntropyWithLogits(TestCase): |
| 10 | 10 | ||
| 11 | def supported_op_exec(self, input1, label): | 11 | def supported_op_exec(self, input1, label): |
| 12 | softmax = torch.nn.functional.softmax(input1) | 12 | softmax = torch.nn.functional.softmax(input1) |
| 13 | log_softmax = torch.log(softmax) | 13 | log_softmax = torch.log(softmax) |
| 14 | loss = torch.sum(- label * log_softmax, dim=1) | 14 | loss = torch.sum(- label * log_softmax, dim=1) |
| 15 | return loss.cpu().detach() | 15 | return loss.cpu().detach() |
| 16 | 16 | ||
| 17 | def custom_op_exec(self, input1, label): | 17 | def custom_op_exec(self, input1, label): |
| 18 | output = torch_npu.npu_softmax_cross_entropy_with_logits(input1, label) | 18 | output = torch_npu.npu_softmax_cross_entropy_with_logits(input1, label) |
| 19 | return output.cpu().detach() | 19 | return output.cpu().detach() |
| 20 | 20 | ||
| 21 | def test_npu_softmax_cross_entropy_with_logits(self, device="npu"): | 21 | def test_npu_softmax_cross_entropy_with_logits(self, device="npu"): |
| 22 | item = [np.float32, 0, (64, 10)] | 22 | item = [np.float32, 0, (64, 10)] |
| 23 | _, npu_input = create_common_tensor(item, -1, 1) | 23 | _, npu_input = create_common_tensor(item, -1, 1) |
| 24 | _, label = create_common_tensor(item, 0, 1) | 24 | _, label = create_common_tensor(item, 0, 1) |
| 25 | supported_output = self.supported_op_exec(npu_input, label) | 25 | supported_output = self.supported_op_exec(npu_input, label) |
| 26 | custom_output = self.custom_op_exec(npu_input, label) | 26 | custom_output = self.custom_op_exec(npu_input, label) |
| 27 | self.assertRtolEqual(supported_output, custom_output) | 27 | self.assertRtolEqual(supported_output, custom_output) |
| 28 | 28 | ||
| 29 | 29 | ||
| 30 | if __name__ == "__main__": | 30 | if __name__ == "__main__": |
| 31 | run_tests() | 31 | run_tests() |
| @@ -1,115 +1,115 @@ | |||
| 1 | import numpy as np | 1 | import numpy as np |
| 2 | import torch | 2 | import torch |
| 3 | 3 | ||
| 4 | import torch_npu | 4 | import torch_npu |
| 5 | from torch_npu.testing.testcase import TestCase, run_tests | 5 | from torch_npu.testing.testcase import TestCase, run_tests |
| 6 | from torch_npu.testing.common_utils import create_common_tensor | 6 | from torch_npu.testing.common_utils import create_common_tensor |
| 7 | 7 | ||
| 8 | 8 | ||
| 9 | class TestScatterUpdate(TestCase): | 9 | class TestScatterUpdate(TestCase): |
| 10 | 10 | ||
| 11 | def supported_scatter_update_exec(self, var, updates, start, length, axis=-2): | 11 | def supported_scatter_update_exec(self, var, updates, start, length, axis=-2): |
| 12 | var_input = var.clone() | 12 | var_input = var.clone() |
| 13 | var_input[:, :, start: start + length, :] = updates | 13 | var_input[:, :, start: start + length, :] = updates |
| 14 | return var_input | 14 | return var_input |
| 15 | 15 | ||
| 16 | def custom_scatter_update_exec(self, var, indices, updates, axis=-2): | 16 | def custom_scatter_update_exec(self, var, indices, updates, axis=-2): |
| 17 | output = torch_npu.scatter_update(var, indices, updates, axis) | 17 | output = torch_npu.scatter_update(var, indices, updates, axis) |
| 18 | return output.cpu().detach() | 18 | return output.cpu().detach() |
| 19 | 19 | ||
| 20 | 20 | ||
| 21 | def supported_scatter_update__exec(self, var, updates, start, length, axis=-2): | 21 | def supported_scatter_update__exec(self, var, updates, start, length, axis=-2): |
| 22 | var[:, :, start: start + length, :] = updates | 22 | var[:, :, start: start + length, :] = updates |
| 23 | return var | 23 | return var |
| 24 | 24 | ||
| 25 | def custom_scatter_update__exec(self, var, indices, updates, axis=-2): | 25 | def custom_scatter_update__exec(self, var, indices, updates, axis=-2): |
| 26 | torch_npu.scatter_update_(var, indices, updates, axis) | 26 | torch_npu.scatter_update_(var, indices, updates, axis) |
| 27 | return var.cpu().detach() | 27 | return var.cpu().detach() |
| 28 | 28 | ||
| 29 | def custom_scatter_update__exec_return(self, var, indices, updates, axis=-2): | 29 | def custom_scatter_update__exec_return(self, var, indices, updates, axis=-2): |
| 30 | result = torch_npu.scatter_update_(var, indices, updates, axis) | 30 | result = torch_npu.scatter_update_(var, indices, updates, axis) |
| 31 | return result.cpu().detach() | 31 | return result.cpu().detach() |
| 32 | 32 | ||
| 33 | 33 | ||
| 34 | def test_scatter_update(self, device="npu"): | 34 | def test_scatter_update(self, device="npu"): |
| 35 | # input_dtype, slice_start, slice_length | 35 | # input_dtype, slice_start, slice_length |
| 36 | items = [ | 36 | items = [ |
| 37 | [torch.float16, 0, 1], | 37 | [torch.float16, 0, 1], |
| 38 | [torch.float16, 2, 1], | 38 | [torch.float16, 2, 1], |
| 39 | [torch.float16, 4, 8], | 39 | [torch.float16, 4, 8], |
| 40 | [torch.float16, 0, 16], | 40 | [torch.float16, 0, 16], |
| 41 | [torch.float32, 0, 1], | 41 | [torch.float32, 0, 1], |
| 42 | [torch.float32, 2, 1], | 42 | [torch.float32, 2, 1], |
| 43 | [torch.float32, 4, 8], | 43 | [torch.float32, 4, 8], |
| 44 | [torch.float32, 0, 16], | 44 | [torch.float32, 0, 16], |
| 45 | ] | 45 | ] |
| 46 | 46 | ||
| 47 | for item in items: | 47 | for item in items: |
| 48 | in_self_cpu = torch.randn(4, 8, 16, 64, dtype=item[0]) | 48 | in_self_cpu = torch.randn(4, 8, 16, 64, dtype=item[0]) |
| 49 | in_self_npu = in_self_cpu.npu() | 49 | in_self_npu = in_self_cpu.npu() |
| 50 | in_update_cpu = torch.randn(4, 8, item[2], 64, dtype=item[0]) | 50 | in_update_cpu = torch.randn(4, 8, item[2], 64, dtype=item[0]) |
| 51 | in_update_npu = in_update_cpu.npu() | 51 | in_update_npu = in_update_cpu.npu() |
| 52 | in_indices_npu = torch.tensor([item[1], item[1], item[1], item[1]]).npu() | 52 | in_indices_npu = torch.tensor([item[1], item[1], item[1], item[1]]).npu() |
| 53 | 53 | ||
| 54 | supported_output = self.supported_scatter_update_exec(in_self_cpu, in_update_cpu, item[1], item[2]) | 54 | supported_output = self.supported_scatter_update_exec(in_self_cpu, in_update_cpu, item[1], item[2]) |
| 55 | custom_output = self.custom_scatter_update_exec(in_self_npu, in_indices_npu, in_update_npu) | 55 | custom_output = self.custom_scatter_update_exec(in_self_npu, in_indices_npu, in_update_npu) |
| 56 | self.assertRtolEqual(supported_output, custom_output) | 56 | self.assertRtolEqual(supported_output, custom_output) |
| 57 | # check whether the custom operator modifies the input_self | 57 | # check whether the custom operator modifies the input_self |
| 58 | self.assertRtolEqual(in_self_cpu, in_self_npu.cpu().detach()) | 58 | self.assertRtolEqual(in_self_cpu, in_self_npu.cpu().detach()) |
| 59 | 59 | ||
| 60 | 60 | ||
| 61 | def test_scatter_update_(self, device="npu"): | 61 | def test_scatter_update_(self, device="npu"): |
| 62 | # input_dtype, slice_start, slice_length | 62 | # input_dtype, slice_start, slice_length |
| 63 | items = [ | 63 | items = [ |
| 64 | [torch.float16, 0, 1], | 64 | [torch.float16, 0, 1], |
| 65 | [torch.float16, 4, 1], | 65 | [torch.float16, 4, 1], |
| 66 | [torch.float16, 8, 32], | 66 | [torch.float16, 8, 32], |
| 67 | [torch.float16, 0, 64], | 67 | [torch.float16, 0, 64], |
| 68 | [torch.float32, 0, 1], | 68 | [torch.float32, 0, 1], |
| 69 | [torch.float32, 4, 1], | 69 | [torch.float32, 4, 1], |
| 70 | [torch.float32, 8, 32], | 70 | [torch.float32, 8, 32], |
| 71 | [torch.float32, 0, 64], | 71 | [torch.float32, 0, 64], |
| 72 | ] | 72 | ] |
| 73 | for item in items: | 73 | for item in items: |
| 74 | in_self_cpu = torch.randn(4, 8, 64, 128, dtype=item[0]) | 74 | in_self_cpu = torch.randn(4, 8, 64, 128, dtype=item[0]) |
| 75 | in_self_npu = in_self_cpu.npu() | 75 | in_self_npu = in_self_cpu.npu() |
| 76 | in_update_cpu = torch.randn(4, 8, item[2], 128, dtype=item[0]) | 76 | in_update_cpu = torch.randn(4, 8, item[2], 128, dtype=item[0]) |
| 77 | in_update_npu = in_update_cpu.npu() | 77 | in_update_npu = in_update_cpu.npu() |
| 78 | in_indices_npu = torch.tensor([item[1], item[1], item[1], item[1]]).npu() | 78 | in_indices_npu = torch.tensor([item[1], item[1], item[1], item[1]]).npu() |
| 79 | 79 | ||
| 80 | supported_output = self.supported_scatter_update__exec(in_self_cpu, in_update_cpu, item[1], item[2]) | 80 | supported_output = self.supported_scatter_update__exec(in_self_cpu, in_update_cpu, item[1], item[2]) |
| 81 | custom_output = self.custom_scatter_update__exec(in_self_npu, in_indices_npu, in_update_npu) | 81 | custom_output = self.custom_scatter_update__exec(in_self_npu, in_indices_npu, in_update_npu) |
| 82 | self.assertRtolEqual(supported_output, custom_output) | 82 | self.assertRtolEqual(supported_output, custom_output) |
| 83 | # check whether the custom operator modifies the input_self | 83 | # check whether the custom operator modifies the input_self |
| 84 | self.assertRtolEqual(in_self_cpu, in_self_npu.cpu().detach()) | 84 | self.assertRtolEqual(in_self_cpu, in_self_npu.cpu().detach()) |
| 85 | 85 | ||
| 86 | 86 | ||
| 87 | def test_scatter_update__return(self, device="npu"): | 87 | def test_scatter_update__return(self, device="npu"): |
| 88 | # input_dtype, slice_start, slice_length | 88 | # input_dtype, slice_start, slice_length |
| 89 | items = [ | 89 | items = [ |
| 90 | [torch.float16, 0, 1], | 90 | [torch.float16, 0, 1], |
| 91 | [torch.float16, 8, 1], | 91 | [torch.float16, 8, 1], |
| 92 | [torch.float16, 16, 8], | 92 | [torch.float16, 16, 8], |
| 93 | [torch.float16, 0, 32], | 93 | [torch.float16, 0, 32], |
| 94 | [torch.float32, 0, 1], | 94 | [torch.float32, 0, 1], |
| 95 | [torch.float32, 8, 1], | 95 | [torch.float32, 8, 1], |
| 96 | [torch.float32, 16, 8], | 96 | [torch.float32, 16, 8], |
| 97 | [torch.float32, 0, 32], | 97 | [torch.float32, 0, 32], |
| 98 | ] | 98 | ] |
| 99 | for item in items: | 99 | for item in items: |
| 100 | in_self_cpu = torch.randn(12, 2, 64, 128, dtype=item[0]) | 100 | in_self_cpu = torch.randn(12, 2, 64, 128, dtype=item[0]) |
| 101 | in_self_npu = in_self_cpu.npu() | 101 | in_self_npu = in_self_cpu.npu() |
| 102 | in_update_cpu = torch.randn(12, 2, item[2], 128, dtype=item[0]) | 102 | in_update_cpu = torch.randn(12, 2, item[2], 128, dtype=item[0]) |
| 103 | in_update_npu = in_update_cpu.npu() | 103 | in_update_npu = in_update_cpu.npu() |
| 104 | in_indices_npu = torch.tensor([item[1], item[1], item[1], item[1], item[1], item[1], | 104 | in_indices_npu = torch.tensor([item[1], item[1], item[1], item[1], item[1], item[1], |
| 105 | item[1], item[1], item[1], item[1], item[1], item[1]]).npu() | 105 | item[1], item[1], item[1], item[1], item[1], item[1]]).npu() |
| 106 | 106 | ||
| 107 | supported_output = self.supported_scatter_update__exec(in_self_cpu, in_update_cpu, item[1], item[2]) | 107 | supported_output = self.supported_scatter_update__exec(in_self_cpu, in_update_cpu, item[1], item[2]) |
| 108 | custom_output = self.custom_scatter_update__exec(in_self_npu, in_indices_npu, in_update_npu) | 108 | custom_output = self.custom_scatter_update__exec(in_self_npu, in_indices_npu, in_update_npu) |
| 109 | self.assertRtolEqual(supported_output, custom_output) | 109 | self.assertRtolEqual(supported_output, custom_output) |
| 110 | # check whether the custom operator modifies the input_self | 110 | # check whether the custom operator modifies the input_self |
| 111 | self.assertRtolEqual(in_self_cpu, in_self_npu.cpu().detach()) | 111 | self.assertRtolEqual(in_self_cpu, in_self_npu.cpu().detach()) |
| 112 | 112 | ||
| 113 | 113 | ||
| 114 | if __name__ == "__main__": | 114 | if __name__ == "__main__": |
| 115 | run_tests() | 115 | run_tests() |
| @@ -1,320 +1,320 @@ | |||
| 1 | """ | 1 | """ |
| 2 | Add validation cases for torch.distributed.elastic.events.record API: | 2 | Add validation cases for torch.distributed.elastic.events.record API: |
| 3 | 3 | ||
| 4 | 1. PyTorch community lacks direct test cases for torch.distributed.elastic.events.record | 4 | 1. PyTorch community lacks direct test cases for torch.distributed.elastic.events.record |
| 5 | in the standard test suite, so this file is added. | 5 | in the standard test suite, so this file is added. |
| 6 | 6 | ||
| 7 | 2. This file validates the following APIs: | 7 | 2. This file validates the following APIs: |
| 8 | torch.distributed.elastic.events.record (This is a pure Python-level event logging API with no hardware dependency.) | 8 | torch.distributed.elastic.events.record (This is a pure Python-level event logging API with no hardware dependency.) |
| 9 | torch.distributed.elastic.events.api.EventMetadataValue (Type alias for metadata values) | 9 | torch.distributed.elastic.events.api.EventMetadataValue (Type alias for metadata values) |
| 10 | (extendable) | 10 | (extendable) |
| 11 | """ | 11 | """ |
| 12 | 12 | ||
| 13 | import json | 13 | import json |
| 14 | import time | 14 | import time |
| 15 | from typing import Union, Optional, get_args, get_origin | 15 | from typing import Union, Optional, get_args, get_origin |
| 16 | from unittest.mock import patch, MagicMock | 16 | from unittest.mock import patch, MagicMock |
| 17 | 17 | ||
| 18 | import torch | 18 | import torch |
| 19 | from torch.distributed.elastic.events import record, get_logging_handler | 19 | from torch.distributed.elastic.events import record, get_logging_handler |
| 20 | from torch.distributed.elastic.events.api import Event, EventSource, EventMetadataValue | 20 | from torch.distributed.elastic.events.api import Event, EventSource, EventMetadataValue |
| 21 | from torch.testing._internal.common_utils import TestCase, run_tests | 21 | from torch.testing._internal.common_utils import TestCase, run_tests |
| 22 | 22 | ||
| 23 | 23 | ||
| 24 | class TestEventsRecord(TestCase): | 24 | class TestEventsRecord(TestCase): |
| 25 | """Test torch.distributed.elastic.events.record method.""" | 25 | """Test torch.distributed.elastic.events.record method.""" |
| 26 | 26 | ||
| 27 | DESTINATION_NULL = "null" | 27 | DESTINATION_NULL = "null" |
| 28 | DESTINATION_CONSOLE = "console" | 28 | DESTINATION_CONSOLE = "console" |
| 29 | TEST_EVENT_PREFIX = "test_event_" | 29 | TEST_EVENT_PREFIX = "test_event_" |
| 30 | 30 | ||
| 31 | def tearDown(self): | 31 | def tearDown(self): |
| 32 | """Clean up resources after each test case to ensure test isolation.""" | 32 | """Clean up resources after each test case to ensure test isolation.""" |
| 33 | from torch.distributed.elastic.events import _events_loggers | 33 | from torch.distributed.elastic.events import _events_loggers |
| 34 | _events_loggers.clear() | 34 | _events_loggers.clear() |
| 35 | 35 | ||
| 36 | if hasattr(get_logging_handler, "cache_clear"): | 36 | if hasattr(get_logging_handler, "cache_clear"): |
| 37 | get_logging_handler.cache_clear() | 37 | get_logging_handler.cache_clear() |
| 38 | 38 | ||
| 39 | patch.stopall() | 39 | patch.stopall() |
| 40 | super().tearDown() | 40 | super().tearDown() |
| 41 | 41 | ||
| 42 | def test_record_null_destination(self): | 42 | def test_record_null_destination(self): |
| 43 | """Verify that record does not raise an exception when using the default null destination.""" | 43 | """Verify that record does not raise an exception when using the default null destination.""" |
| 44 | event = Event( | 44 | event = Event( |
| 45 | name=f"{self.TEST_EVENT_PREFIX}null", | 45 | name=f"{self.TEST_EVENT_PREFIX}null", |
| 46 | source=EventSource.WORKER, | 46 | source=EventSource.WORKER, |
| 47 | metadata={"key": "value"} | 47 | metadata={"key": "value"} |
| 48 | ) | 48 | ) |
| 49 | record(event) | 49 | record(event) |
| 50 | 50 | ||
| 51 | def test_record_console_destination(self): | 51 | def test_record_console_destination(self): |
| 52 | """Verify that record does not raise an exception when using the console destination.""" | 52 | """Verify that record does not raise an exception when using the console destination.""" |
| 53 | event = Event( | 53 | event = Event( |
| 54 | name=f"{self.TEST_EVENT_PREFIX}console", | 54 | name=f"{self.TEST_EVENT_PREFIX}console", |
| 55 | source=EventSource.AGENT, | 55 | source=EventSource.AGENT, |
| 56 | metadata={"stage": "init"} | 56 | metadata={"stage": "init"} |
| 57 | ) | 57 | ) |
| 58 | record(event, destination=self.DESTINATION_CONSOLE) | 58 | record(event, destination=self.DESTINATION_CONSOLE) |
| 59 | 59 | ||
| 60 | def test_record_with_timestamp(self): | 60 | def test_record_with_timestamp(self): |
| 61 | """Verify that record can correctly log an event with a custom or auto-generated timestamp.""" | 61 | """Verify that record can correctly log an event with a custom or auto-generated timestamp.""" |
| 62 | custom_timestamp = int(time.time() * 1000) | 62 | custom_timestamp = int(time.time() * 1000) |
| 63 | event = Event( | 63 | event = Event( |
| 64 | name=f"{self.TEST_EVENT_PREFIX}timestamp_custom", | 64 | name=f"{self.TEST_EVENT_PREFIX}timestamp_custom", |
| 65 | source=EventSource.WORKER, | 65 | source=EventSource.WORKER, |
| 66 | timestamp=custom_timestamp, | 66 | timestamp=custom_timestamp, |
| 67 | metadata={"ts": custom_timestamp} | 67 | metadata={"ts": custom_timestamp} |
| 68 | ) | 68 | ) |
| 69 | record(event, destination=self.DESTINATION_NULL) | 69 | record(event, destination=self.DESTINATION_NULL) |
| 70 | self.assertEqual(event.timestamp, custom_timestamp) | 70 | self.assertEqual(event.timestamp, custom_timestamp) |
| 71 | self.assertIsInstance(event.timestamp, int) | 71 | self.assertIsInstance(event.timestamp, int) |
| 72 | self.assertGreaterEqual(event.timestamp, 0) | 72 | self.assertGreaterEqual(event.timestamp, 0) |
| 73 | 73 | ||
| 74 | event_with_auto_timestamp = Event( | 74 | event_with_auto_timestamp = Event( |
| 75 | name=f"{self.TEST_EVENT_PREFIX}timestamp_auto", | 75 | name=f"{self.TEST_EVENT_PREFIX}timestamp_auto", |
| 76 | source=EventSource.WORKER | 76 | source=EventSource.WORKER |
| 77 | ) | 77 | ) |
| 78 | record(event_with_auto_timestamp) | 78 | record(event_with_auto_timestamp) |
| 79 | self.assertIsNotNone(event_with_auto_timestamp.timestamp) | 79 | self.assertIsNotNone(event_with_auto_timestamp.timestamp) |
| 80 | self.assertIsInstance(event_with_auto_timestamp.timestamp, int) | 80 | self.assertIsInstance(event_with_auto_timestamp.timestamp, int) |
| 81 | self.assertGreaterEqual(event_with_auto_timestamp.timestamp, 0) | 81 | self.assertGreaterEqual(event_with_auto_timestamp.timestamp, 0) |
| 82 | 82 | ||
| 83 | @patch("torch.distributed.elastic.events._get_or_create_logger") | 83 | @patch("torch.distributed.elastic.events._get_or_create_logger") |
| 84 | def test_record_with_various_metadata_types(self, mock_get_logger): | 84 | def test_record_with_various_metadata_types(self, mock_get_logger): |
| 85 | """Verify that record correctly passes various metadata types to the underlying logger.""" | 85 | """Verify that record correctly passes various metadata types to the underlying logger.""" |
| 86 | mock_logger = MagicMock() | 86 | mock_logger = MagicMock() |
| 87 | mock_get_logger.return_value = mock_logger | 87 | mock_get_logger.return_value = mock_logger |
| 88 | 88 | ||
| 89 | metadata = { | 89 | metadata = { |
| 90 | "str_val": "hello", | 90 | "str_val": "hello", |
| 91 | "int_val": 42, | 91 | "int_val": 42, |
| 92 | "float_val": 3.14, | 92 | "float_val": 3.14, |
| 93 | "bool_val": True, | 93 | "bool_val": True, |
| 94 | "none_val": None, | 94 | "none_val": None, |
| 95 | "list_val": [1, 2, 3], | 95 | "list_val": [1, 2, 3], |
| 96 | "nested_dict": {"sub_key": "sub_val"} | 96 | "nested_dict": {"sub_key": "sub_val"} |
| 97 | } | 97 | } |
| 98 | event = Event( | 98 | event = Event( |
| 99 | name=f"{self.TEST_EVENT_PREFIX}metadata_types", | 99 | name=f"{self.TEST_EVENT_PREFIX}metadata_types", |
| 100 | source=EventSource.AGENT, | 100 | source=EventSource.AGENT, |
| 101 | metadata=metadata | 101 | metadata=metadata |
| 102 | ) | 102 | ) |
| 103 | record(event, destination=self.DESTINATION_CONSOLE) | 103 | record(event, destination=self.DESTINATION_CONSOLE) |
| 104 | 104 | ||
| 105 | # Verify the internal call chain | 105 | # Verify the internal call chain |
| 106 | mock_get_logger.assert_called_once_with(self.DESTINATION_CONSOLE) | 106 | mock_get_logger.assert_called_once_with(self.DESTINATION_CONSOLE) |
| 107 | mock_logger.info.assert_called_once() | 107 | mock_logger.info.assert_called_once() |
| 108 | 108 | ||
| 109 | # Verify that the serialized metadata is correctly preserved | 109 | # Verify that the serialized metadata is correctly preserved |
| 110 | logged_raw = mock_logger.info.call_args[0][0] | 110 | logged_raw = mock_logger.info.call_args[0][0] |
| 111 | logged_data = json.loads(logged_raw) | 111 | logged_data = json.loads(logged_raw) |
| 112 | self.assertEqual(logged_data["metadata"], metadata) | 112 | self.assertEqual(logged_data["metadata"], metadata) |
| 113 | 113 | ||
| 114 | def test_record_multiple_events(self): | 114 | def test_record_multiple_events(self): |
| 115 | """Verify that consecutive calls to record do not interfere with each other.""" | 115 | """Verify that consecutive calls to record do not interfere with each other.""" |
| 116 | event1 = Event( | 116 | event1 = Event( |
| 117 | name=f"{self.TEST_EVENT_PREFIX}multiple_1", | 117 | name=f"{self.TEST_EVENT_PREFIX}multiple_1", |
| 118 | source=EventSource.WORKER, | 118 | source=EventSource.WORKER, |
| 119 | metadata={"seq": 1} | 119 | metadata={"seq": 1} |
| 120 | ) | 120 | ) |
| 121 | event2 = Event( | 121 | event2 = Event( |
| 122 | name=f"{self.TEST_EVENT_PREFIX}multiple_2", | 122 | name=f"{self.TEST_EVENT_PREFIX}multiple_2", |
| 123 | source=EventSource.WORKER, | 123 | source=EventSource.WORKER, |
| 124 | metadata={"seq": 2} | 124 | metadata={"seq": 2} |
| 125 | ) | 125 | ) |
| 126 | 126 | ||
| 127 | record(event1) | 127 | record(event1) |
| 128 | record(event2) | 128 | record(event2) |
| 129 | 129 | ||
| 130 | self.assertIn("seq", event1.metadata) | 130 | self.assertIn("seq", event1.metadata) |
| 131 | self.assertEqual(event1.metadata["seq"], 1) | 131 | self.assertEqual(event1.metadata["seq"], 1) |
| 132 | self.assertIn("seq", event2.metadata) | 132 | self.assertIn("seq", event2.metadata) |
| 133 | self.assertEqual(event2.metadata["seq"], 2) | 133 | self.assertEqual(event2.metadata["seq"], 2) |
| 134 | 134 | ||
| 135 | @patch("torch.distributed.elastic.events._get_or_create_logger") | 135 | @patch("torch.distributed.elastic.events._get_or_create_logger") |
| 136 | def test_record_calls_get_or_create_logger(self, mock_get_logger): | 136 | def test_record_calls_get_or_create_logger(self, mock_get_logger): |
| 137 | """ | 137 | """ |
| 138 | Verify that record internally calls _get_or_create_logger to obtain the logger, | 138 | Verify that record internally calls _get_or_create_logger to obtain the logger, |
| 139 | and then calls .info() on that logger with the serialized event. | 139 | and then calls .info() on that logger with the serialized event. |
| 140 | """ | 140 | """ |
| 141 | mock_logger = MagicMock() | 141 | mock_logger = MagicMock() |
| 142 | mock_get_logger.return_value = mock_logger | 142 | mock_get_logger.return_value = mock_logger |
| 143 | 143 | ||
| 144 | event = Event( | 144 | event = Event( |
| 145 | name=f"{self.TEST_EVENT_PREFIX}mock", | 145 | name=f"{self.TEST_EVENT_PREFIX}mock", |
| 146 | source=EventSource.WORKER | 146 | source=EventSource.WORKER |
| 147 | ) | 147 | ) |
| 148 | record(event, destination=self.DESTINATION_CONSOLE) | 148 | record(event, destination=self.DESTINATION_CONSOLE) |
| 149 | 149 | ||
| 150 | mock_get_logger.assert_called_once_with(self.DESTINATION_CONSOLE) | 150 | mock_get_logger.assert_called_once_with(self.DESTINATION_CONSOLE) |
| 151 | mock_logger.info.assert_called_once_with(event.serialize()) | 151 | mock_logger.info.assert_called_once_with(event.serialize()) |
| 152 | 152 | ||
| 153 | def test_record_event_name_empty(self): | 153 | def test_record_event_name_empty(self): |
| 154 | """Verify that record does not raise an exception when the event name is an empty string.""" | 154 | """Verify that record does not raise an exception when the event name is an empty string.""" |
| 155 | event = Event(name="", source=EventSource.WORKER) | 155 | event = Event(name="", source=EventSource.WORKER) |
| 156 | record(event) | 156 | record(event) |
| 157 | 157 | ||
| 158 | def test_record_event_name_special_chars(self): | 158 | def test_record_event_name_special_chars(self): |
| 159 | """Verify event names with special characters (spaces, symbols, unicode) are handled gracefully.""" | 159 | """Verify event names with special characters (spaces, symbols, unicode) are handled gracefully.""" |
| 160 | special_names = ["test event", "test@event", "测试事件", "a" * 256] | 160 | special_names = ["test event", "test@event", "测试事件", "a" * 256] |
| 161 | for name in special_names: | 161 | for name in special_names: |
| 162 | with self.subTest(name=name): | 162 | with self.subTest(name=name): |
| 163 | event = Event(name=name, source=EventSource.WORKER) | 163 | event = Event(name=name, source=EventSource.WORKER) |
| 164 | record(event, destination=self.DESTINATION_NULL) | 164 | record(event, destination=self.DESTINATION_NULL) |
| 165 | 165 | ||
| 166 | def test_record_all_event_sources(self): | 166 | def test_record_all_event_sources(self): |
| 167 | """Verify record compatibility with all EventSource enum values.""" | 167 | """Verify record compatibility with all EventSource enum values.""" |
| 168 | for source in EventSource: | 168 | for source in EventSource: |
| 169 | with self.subTest(source=source): | 169 | with self.subTest(source=source): |
| 170 | event = Event( | 170 | event = Event( |
| 171 | name=f"{self.TEST_EVENT_PREFIX}source_{source.name.lower()}", | 171 | name=f"{self.TEST_EVENT_PREFIX}source_{source.name.lower()}", |
| 172 | source=source, | 172 | source=source, |
| 173 | metadata={"source_type": source.name} | 173 | metadata={"source_type": source.name} |
| 174 | ) | 174 | ) |
| 175 | record(event) | 175 | record(event) |
| 176 | self.assertEqual(event.source, source) | 176 | self.assertEqual(event.source, source) |
| 177 | self.assertIn("source_type", event.metadata) | 177 | self.assertIn("source_type", event.metadata) |
| 178 | self.assertEqual(event.metadata["source_type"], source.name) | 178 | self.assertEqual(event.metadata["source_type"], source.name) |
| 179 | 179 | ||
| 180 | 180 | ||
| 181 | class TestEventMetadataValue(TestCase): | 181 | class TestEventMetadataValue(TestCase): |
| 182 | """Test torch.distributed.elastic.events.api.EventMetadataValue type alias.""" | 182 | """Test torch.distributed.elastic.events.api.EventMetadataValue type alias.""" |
| 183 | 183 | ||
| 184 | def test_event_metadata_value_is_defined(self): | 184 | def test_event_metadata_value_is_defined(self): |
| 185 | """Verify that EventMetadataValue is exported and defined.""" | 185 | """Verify that EventMetadataValue is exported and defined.""" |
| 186 | self.assertIsNotNone(EventMetadataValue) | 186 | self.assertIsNotNone(EventMetadataValue) |
| 187 | self.assertIs(get_origin(EventMetadataValue), Union) | 187 | self.assertIs(get_origin(EventMetadataValue), Union) |
| 188 | 188 | ||
| 189 | def test_event_metadata_value_type_structure(self): | 189 | def test_event_metadata_value_type_structure(self): |
| 190 | """ | 190 | """ |
| 191 | Verify that EventMetadataValue is a Union of str, int, float, bool, None. | 191 | Verify that EventMetadataValue is a Union of str, int, float, bool, None. |
| 192 | Expected: Optional[Union[str, int, float, bool]] | 192 | Expected: Optional[Union[str, int, float, bool]] |
| 193 | """ | 193 | """ |
| 194 | origin = get_origin(EventMetadataValue) | 194 | origin = get_origin(EventMetadataValue) |
| 195 | args = get_args(EventMetadataValue) | 195 | args = get_args(EventMetadataValue) |
| 196 | 196 | ||
| 197 | self.assertIs(origin, Union) | 197 | self.assertIs(origin, Union) |
| 198 | self.assertIn(str, args) | 198 | self.assertIn(str, args) |
| 199 | self.assertIn(int, args) | 199 | self.assertIn(int, args) |
| 200 | self.assertIn(float, args) | 200 | self.assertIn(float, args) |
| 201 | self.assertIn(bool, args) | 201 | self.assertIn(bool, args) |
| 202 | self.assertIn(type(None), args) | 202 | self.assertIn(type(None), args) |
| 203 | self.assertEqual(len(args), 5) | 203 | self.assertEqual(len(args), 5) |
| 204 | 204 | ||
| 205 | def test_event_metadata_value_legal_primitives(self): | 205 | def test_event_metadata_value_legal_primitives(self): |
| 206 | """Verify that all legal primitive values are accepted by Event metadata.""" | 206 | """Verify that all legal primitive values are accepted by Event metadata.""" |
| 207 | test_cases = [ | 207 | test_cases = [ |
| 208 | ("string_val", "hello"), | 208 | ("string_val", "hello"), |
| 209 | ("int_val", 42), | 209 | ("int_val", 42), |
| 210 | ("int_val_neg", -7), | 210 | ("int_val_neg", -7), |
| 211 | ("float_val", 3.14159), | 211 | ("float_val", 3.14159), |
| 212 | ("float_val_zero", 0.0), | 212 | ("float_val_zero", 0.0), |
| 213 | ("bool_true", True), | 213 | ("bool_true", True), |
| 214 | ("bool_false", False), | 214 | ("bool_false", False), |
| 215 | ("none_val", None), | 215 | ("none_val", None), |
| 216 | ] | 216 | ] |
| 217 | 217 | ||
| 218 | for key, value in test_cases: | 218 | for key, value in test_cases: |
| 219 | with self.subTest(key=key, value=value): | 219 | with self.subTest(key=key, value=value): |
| 220 | event = Event( | 220 | event = Event( |
| 221 | name=f"test_metadata_{key}", | 221 | name=f"test_metadata_{key}", |
| 222 | source=EventSource.WORKER, | 222 | source=EventSource.WORKER, |
| 223 | metadata={key: value} | 223 | metadata={key: value} |
| 224 | ) | 224 | ) |
| 225 | self.assertIn(key, event.metadata) | 225 | self.assertIn(key, event.metadata) |
| 226 | self.assertEqual(event.metadata[key], value) | 226 | self.assertEqual(event.metadata[key], value) |
| 227 | record(event, destination=TestEventsRecord.DESTINATION_NULL) | 227 | record(event, destination=TestEventsRecord.DESTINATION_NULL) |
| 228 | 228 | ||
| 229 | def test_event_metadata_value_none_explicit(self): | 229 | def test_event_metadata_value_none_explicit(self): |
| 230 | """Verify that None is explicitly allowed as a metadata value.""" | 230 | """Verify that None is explicitly allowed as a metadata value.""" |
| 231 | event = Event( | 231 | event = Event( |
| 232 | name="test_metadata_none", | 232 | name="test_metadata_none", |
| 233 | source=EventSource.AGENT, | 233 | source=EventSource.AGENT, |
| 234 | metadata={"explicit_none": None} | 234 | metadata={"explicit_none": None} |
| 235 | ) | 235 | ) |
| 236 | self.assertIsNone(event.metadata["explicit_none"]) | 236 | self.assertIsNone(event.metadata["explicit_none"]) |
| 237 | record(event, destination=TestEventsRecord.DESTINATION_NULL) | 237 | record(event, destination=TestEventsRecord.DESTINATION_NULL) |
| 238 | 238 | ||
| 239 | def test_event_metadata_value_mixed_dict(self): | 239 | def test_event_metadata_value_mixed_dict(self): |
| 240 | """Verify that a metadata dict containing all legal types can be constructed and recorded.""" | 240 | """Verify that a metadata dict containing all legal types can be constructed and recorded.""" |
| 241 | metadata = { | 241 | metadata = { |
| 242 | "epoch": 10, | 242 | "epoch": 10, |
| 243 | "loss": 0.1234, | 243 | "loss": 0.1234, |
| 244 | "model_name": "resnet50", | 244 | "model_name": "resnet50", |
| 245 | "is_training": True, | 245 | "is_training": True, |
| 246 | "checkpoint_path": None, | 246 | "checkpoint_path": None, |
| 247 | } | 247 | } |
| 248 | event = Event( | 248 | event = Event( |
| 249 | name="test_metadata_mixed", | 249 | name="test_metadata_mixed", |
| 250 | source=EventSource.WORKER, | 250 | source=EventSource.WORKER, |
| 251 | metadata=metadata | 251 | metadata=metadata |
| 252 | ) | 252 | ) |
| 253 | self.assertEqual(len(event.metadata), 5) | 253 | self.assertEqual(len(event.metadata), 5) |
| 254 | record(event, destination=TestEventsRecord.DESTINATION_NULL) | 254 | record(event, destination=TestEventsRecord.DESTINATION_NULL) |
| 255 | 255 | ||
| 256 | def test_event_metadata_value_serialization_roundtrip(self): | 256 | def test_event_metadata_value_serialization_roundtrip(self): |
| 257 | """ | 257 | """ |
| 258 | Verify that metadata values survive the Event.serialize() roundtrip | 258 | Verify that metadata values survive the Event.serialize() roundtrip |
| 259 | and remain as their original Python types. | 259 | and remain as their original Python types. |
| 260 | """ | 260 | """ |
| 261 | metadata = { | 261 | metadata = { |
| 262 | "lr": 0.01, | 262 | "lr": 0.01, |
| 263 | "step": 100, | 263 | "step": 100, |
| 264 | "tag": "train", | 264 | "tag": "train", |
| 265 | "enabled": False, | 265 | "enabled": False, |
| 266 | "optional": None, | 266 | "optional": None, |
| 267 | } | 267 | } |
| 268 | event = Event( | 268 | event = Event( |
| 269 | name="test_serialization", | 269 | name="test_serialization", |
| 270 | source=EventSource.AGENT, | 270 | source=EventSource.AGENT, |
| 271 | metadata=metadata | 271 | metadata=metadata |
| 272 | ) | 272 | ) |
| 273 | serialized = event.serialize() | 273 | serialized = event.serialize() |
| 274 | 274 | ||
| 275 | self.assertIsInstance(serialized, str) | 275 | self.assertIsInstance(serialized, str) |
| 276 | data = json.loads(serialized) | 276 | data = json.loads(serialized) |
| 277 | self.assertIn("metadata", data) | 277 | self.assertIn("metadata", data) |
| 278 | 278 | ||
| 279 | serialized_metadata = data["metadata"] | 279 | serialized_metadata = data["metadata"] |
| 280 | self.assertEqual(serialized_metadata["lr"], 0.01) | 280 | self.assertEqual(serialized_metadata["lr"], 0.01) |
| 281 | self.assertEqual(serialized_metadata["step"], 100) | 281 | self.assertEqual(serialized_metadata["step"], 100) |
| 282 | self.assertEqual(serialized_metadata["tag"], "train") | 282 | self.assertEqual(serialized_metadata["tag"], "train") |
| 283 | self.assertEqual(serialized_metadata["enabled"], False) | 283 | self.assertEqual(serialized_metadata["enabled"], False) |
| 284 | self.assertIsNone(serialized_metadata["optional"]) | 284 | self.assertIsNone(serialized_metadata["optional"]) |
| 285 | 285 | ||
| 286 | def test_event_serialize_empty_metadata(self): | 286 | def test_event_serialize_empty_metadata(self): |
| 287 | """Verify that empty metadata serializes correctly without data loss.""" | 287 | """Verify that empty metadata serializes correctly without data loss.""" |
| 288 | event = Event( | 288 | event = Event( |
| 289 | name="test_empty_metadata", | 289 | name="test_empty_metadata", |
| 290 | source=EventSource.WORKER, | 290 | source=EventSource.WORKER, |
| 291 | metadata={} | 291 | metadata={} |
| 292 | ) | 292 | ) |
| 293 | data = json.loads(event.serialize()) | 293 | data = json.loads(event.serialize()) |
| 294 | self.assertEqual(data["metadata"], {}) | 294 | self.assertEqual(data["metadata"], {}) |
| 295 | 295 | ||
| 296 | def test_event_serialize_unsupported_metadata_type(self): | 296 | def test_event_serialize_unsupported_metadata_type(self): |
| 297 | """ | 297 | """ |
| 298 | Verify that unsupported metadata types (e.g. set, bytes) raise TypeError | 298 | Verify that unsupported metadata types (e.g. set, bytes) raise TypeError |
| 299 | during serialization / record, since Event itself does not validate at construction time. | 299 | during serialization / record, since Event itself does not validate at construction time. |
| 300 | """ | 300 | """ |
| 301 | invalid_metadata_cases = [ | 301 | invalid_metadata_cases = [ |
| 302 | ("set_val", {1, 2, 3}), | 302 | ("set_val", {1, 2, 3}), |
| 303 | ("bytes_val", b"test"), | 303 | ("bytes_val", b"test"), |
| 304 | ("object_val", MagicMock()), | 304 | ("object_val", MagicMock()), |
| 305 | ] | 305 | ] |
| 306 | 306 | ||
| 307 | for key, value in invalid_metadata_cases: | 307 | for key, value in invalid_metadata_cases: |
| 308 | with self.subTest(key=key): | 308 | with self.subTest(key=key): |
| 309 | event = Event( | 309 | event = Event( |
| 310 | name=f"test_invalid_{key}", | 310 | name=f"test_invalid_{key}", |
| 311 | source=EventSource.WORKER, | 311 | source=EventSource.WORKER, |
| 312 | metadata={key: value} | 312 | metadata={key: value} |
| 313 | ) | 313 | ) |
| 314 | # Event construction succeeds, but serialize/record should fail | 314 | # Event construction succeeds, but serialize/record should fail |
| 315 | with self.assertRaises(TypeError): | 315 | with self.assertRaises(TypeError): |
| 316 | event.serialize() | 316 | event.serialize() |
| 317 | 317 | ||
| 318 | 318 | ||
| 319 | if __name__ == "__main__": | 319 | if __name__ == "__main__": |
| 320 | run_tests() | 320 | run_tests() |
| @@ -127,4 +127,3 @@ class TestReduction(TestCase): | |||
| 127 | 127 | ||
| 128 | if __name__ == "__main__": | 128 | if __name__ == "__main__": |
| 129 | run_tests() | 129 | run_tests() |
| 130 | |||
| @@ -133,6 +133,7 @@ class HcclAlltoAllSingleTest(TestCase): | |||
| 133 | HcclAlltoAllSingleTest._test_alltoall_single_2p, | 133 | HcclAlltoAllSingleTest._test_alltoall_single_2p, |
| 134 | HcclAlltoAllSingleTest._init_dist_hccl) | 134 | HcclAlltoAllSingleTest._init_dist_hccl) |
| 135 | 135 | ||
| 136 | |||
| 136 | 137 | ||
| 137 | def test_alltoall_single_2p_size_dist(self): | 138 | def test_alltoall_single_2p_size_dist(self): |
| 138 | self._test_multiprocess_2p( | 139 | self._test_multiprocess_2p( |
| @@ -1,1101 +1,1101 @@ | |||
| 1 | # Copyright (c) Meta Platforms, Inc. and affiliates | 1 | # Copyright (c) Meta Platforms, Inc. and affiliates |
| 2 | # Owner(s): ["oncall: distributed"] | 2 | # Owner(s): ["oncall: distributed"] |
| 3 | import os | 3 | import os |
| 4 | 4 | ||
| 5 | import torch | 5 | import torch |
| 6 | import torch.distributed._functional_collectives as funcol | 6 | import torch.distributed._functional_collectives as funcol |
| 7 | from torch.distributed._tensor import DTensor | 7 | from torch.distributed._tensor import DTensor |
| 8 | from torch.distributed.device_mesh import _mesh_resources, DeviceMesh, init_device_mesh | 8 | from torch.distributed.device_mesh import _mesh_resources, DeviceMesh, init_device_mesh |
| 9 | from torch.distributed.distributed_c10d import ( | 9 | from torch.distributed.distributed_c10d import ( |
| 10 | _get_default_group, | 10 | _get_default_group, |
| 11 | _world, | 11 | _world, |
| 12 | get_global_rank, | 12 | get_global_rank, |
| 13 | get_world_size, | 13 | get_world_size, |
| 14 | init_process_group, | 14 | init_process_group, |
| 15 | is_initialized, | 15 | is_initialized, |
| 16 | ProcessGroup, | 16 | ProcessGroup, |
| 17 | ) | 17 | ) |
| 18 | from torch.distributed.tensor._collective_utils import ( | 18 | from torch.distributed.tensor._collective_utils import ( |
| 19 | mesh_broadcast, | 19 | mesh_broadcast, |
| 20 | mesh_scatter, | 20 | mesh_scatter, |
| 21 | unpad_tensor, | 21 | unpad_tensor, |
| 22 | ) | 22 | ) |
| 23 | from torch.distributed.tensor.placement_types import _Partial, Shard | 23 | from torch.distributed.tensor.placement_types import _Partial, Shard |
| 24 | from torch.testing._internal.distributed._tensor.common_dtensor import DTensorTestBase | 24 | from torch.testing._internal.distributed._tensor.common_dtensor import DTensorTestBase |
| 25 | from torch.testing._internal.distributed.fake_pg import FakeStore | 25 | from torch.testing._internal.distributed.fake_pg import FakeStore |
| 26 | 26 | ||
| 27 | import torch_npu | 27 | import torch_npu |
| 28 | from torch_npu.testing.common_distributed import with_comms, skipIfUnsupportMultiNPU | 28 | from torch_npu.testing.common_distributed import with_comms, skipIfUnsupportMultiNPU |
| 29 | from torch_npu.testing.testcase import run_tests | 29 | from torch_npu.testing.testcase import run_tests |
| 30 | 30 | ||
| 31 | 31 | ||
| 32 | def _get_device_type(world_size): | 32 | def _get_device_type(world_size): |
| 33 | if ( | 33 | if ( |
| 34 | torch.npu.is_available() | 34 | torch.npu.is_available() |
| 35 | and torch.npu.device_count() >= world_size | 35 | and torch.npu.device_count() >= world_size |
| 36 | and torch.distributed.is_hccl_available() | 36 | and torch.distributed.is_hccl_available() |
| 37 | ): | 37 | ): |
| 38 | device_type = "npu" | 38 | device_type = "npu" |
| 39 | else: | 39 | else: |
| 40 | device_type = "cpu" | 40 | device_type = "cpu" |
| 41 | return device_type | 41 | return device_type |
| 42 | 42 | ||
| 43 | 43 | ||
| 44 | def _set_env_var(addr="localhost", port="29500", world_size=1, rank=0): | 44 | def _set_env_var(addr="localhost", port="29500", world_size=1, rank=0): |
| 45 | os.environ["MASTER_ADDR"] = addr | 45 | os.environ["MASTER_ADDR"] = addr |
| 46 | os.environ["MASTER_PORT"] = port | 46 | os.environ["MASTER_PORT"] = port |
| 47 | os.environ["WORLD_SIZE"] = f"{world_size}" | 47 | os.environ["WORLD_SIZE"] = f"{world_size}" |
| 48 | os.environ["RANK"] = f"{rank}" | 48 | os.environ["RANK"] = f"{rank}" |
| 49 | 49 | ||
| 50 | 50 | ||
| 51 | class DeviceMeshTest(DTensorTestBase): | 51 | class DeviceMeshTest(DTensorTestBase): |
| 52 | @property | 52 | @property |
| 53 | def world_size(self): | 53 | def world_size(self): |
| 54 | return 2 | 54 | return 2 |
| 55 | 55 | ||
| 56 | @skipIfUnsupportMultiNPU(2) | 56 | @skipIfUnsupportMultiNPU(2) |
| 57 | def test_init_process_group(self): | 57 | def test_init_process_group(self): |
| 58 | device_type = _get_device_type(self.world_size) | 58 | device_type = _get_device_type(self.world_size) |
| 59 | mesh_tensor = torch.arange(2).reshape(2, 1) | 59 | mesh_tensor = torch.arange(2).reshape(2, 1) |
| 60 | self.assertTrue(not is_initialized()) | 60 | self.assertTrue(not is_initialized()) |
| 61 | _set_env_var(world_size=self.world_size, rank=self.rank) | 61 | _set_env_var(world_size=self.world_size, rank=self.rank) |
| 62 | DeviceMesh(device_type, mesh_tensor) | 62 | DeviceMesh(device_type, mesh_tensor) |
| 63 | self.assertTrue(is_initialized()) | 63 | self.assertTrue(is_initialized()) |
| 64 | self.destroy_pg() | 64 | self.destroy_pg() |
| 65 | 65 | ||
| 66 | @skipIfUnsupportMultiNPU(2) | 66 | @skipIfUnsupportMultiNPU(2) |
| 67 | @with_comms | 67 | @with_comms |
| 68 | def test_2d_mesh_non_eager_init_subgroup(self): | 68 | def test_2d_mesh_non_eager_init_subgroup(self): |
| 69 | mesh_shape = (2, self.world_size // 2) | 69 | mesh_shape = (2, self.world_size // 2) |
| 70 | mesh_2d = init_device_mesh(self.device_type, mesh_shape) | 70 | mesh_2d = init_device_mesh(self.device_type, mesh_shape) |
| 71 | 71 | ||
| 72 | self.assertEqual(mesh_2d.get_group(0).bound_device_id, None) | 72 | self.assertEqual(mesh_2d.get_group(0).bound_device_id, None) |
| 73 | self.assertEqual(mesh_2d.get_group(1).bound_device_id, None) | 73 | self.assertEqual(mesh_2d.get_group(1).bound_device_id, None) |
| 74 | 74 | ||
| 75 | # need to refactor the other tests in this file to test both | 75 | # need to refactor the other tests in this file to test both |
| 76 | # eager_init=True and eager_init=False scenarios. | 76 | # eager_init=True and eager_init=False scenarios. |
| 77 | @skipIfUnsupportMultiNPU(2) | 77 | @skipIfUnsupportMultiNPU(2) |
| 78 | @with_comms | 78 | @with_comms |
| 79 | def test_2d_mesh_eager_init_subgroup(self): | 79 | def test_2d_mesh_eager_init_subgroup(self): |
| 80 | # Test with eager_init=True | 80 | # Test with eager_init=True |
| 81 | with self.subTest(eager_init=True): | 81 | with self.subTest(eager_init=True): |
| 82 | mesh_shape = (2, self.world_size // 2) | 82 | mesh_shape = (2, self.world_size // 2) |
| 83 | mesh_2d = init_device_mesh(self.device_type, mesh_shape) | 83 | mesh_2d = init_device_mesh(self.device_type, mesh_shape) |
| 84 | 84 | ||
| 85 | # when eager init is used, the subgroup is created from hccl comm split and | 85 | # when eager init is used, the subgroup is created from hccl comm split and |
| 86 | # there would be bound_device_id immediately assigned for the subgroup. | 86 | # there would be bound_device_id immediately assigned for the subgroup. |
| 87 | if self.backend == "hccl": | 87 | if self.backend == "hccl": |
| 88 | curr_device = torch.npu.current_device() | 88 | curr_device = torch.npu.current_device() |
| 89 | self.assertEqual(mesh_2d.get_group(0).bound_device_id.index, curr_device) | 89 | self.assertEqual(mesh_2d.get_group(0).bound_device_id.index, curr_device) |
| 90 | self.assertEqual(mesh_2d.get_group(1).bound_device_id.index, curr_device) | 90 | self.assertEqual(mesh_2d.get_group(1).bound_device_id.index, curr_device) |
| 91 | 91 | ||
| 92 | # Test with eager_init=False | 92 | # Test with eager_init=False |
| 93 | with self.subTest(eager_init=False): | 93 | with self.subTest(eager_init=False): |
| 94 | mesh_shape = (2, self.world_size // 2) | 94 | mesh_shape = (2, self.world_size // 2) |
| 95 | mesh_2d = init_device_mesh(self.device_type, mesh_shape) | 95 | mesh_2d = init_device_mesh(self.device_type, mesh_shape) |
| 96 | 96 | ||
| 97 | # when eager init is used, the subgroup is created from hccl comm split and | 97 | # when eager init is used, the subgroup is created from hccl comm split and |
| 98 | # there would be bound_device_id immediately assigned for the subgroup. | 98 | # there would be bound_device_id immediately assigned for the subgroup. |
| 99 | if self.backend == "hccl": | 99 | if self.backend == "hccl": |
| 100 | curr_device = torch.npu.current_device() | 100 | curr_device = torch.npu.current_device() |
| 101 | self.assertEqual(mesh_2d.get_group(0).bound_device_id.index, curr_device) | 101 | self.assertEqual(mesh_2d.get_group(0).bound_device_id.index, curr_device) |
| 102 | self.assertEqual(mesh_2d.get_group(1).bound_device_id.index, curr_device) | 102 | self.assertEqual(mesh_2d.get_group(1).bound_device_id.index, curr_device) |
| 103 | 103 | ||
| 104 | @skipIfUnsupportMultiNPU(2) | 104 | @skipIfUnsupportMultiNPU(2) |
| 105 | @with_comms | 105 | @with_comms |
| 106 | def test_get_group_and_get_all_groups(self): | 106 | def test_get_group_and_get_all_groups(self): |
| 107 | mesh_shape = (2, self.world_size // 2) | 107 | mesh_shape = (2, self.world_size // 2) |
| 108 | mesh_2d = init_device_mesh( | 108 | mesh_2d = init_device_mesh( |
| 109 | self.device_type, mesh_shape, mesh_dim_names=("dp", "tp") | 109 | self.device_type, mesh_shape, mesh_dim_names=("dp", "tp") |
| 110 | ) | 110 | ) |
| 111 | 111 | ||
| 112 | tp_mesh = mesh_2d["tp"] | 112 | tp_mesh = mesh_2d["tp"] |
| 113 | dp_mesh = mesh_2d["dp"] | 113 | dp_mesh = mesh_2d["dp"] |
| 114 | 114 | ||
| 115 | self.assertEqual(mesh_2d.get_group(0), mesh_2d.get_group("dp")) | 115 | self.assertEqual(mesh_2d.get_group(0), mesh_2d.get_group("dp")) |
| 116 | self.assertEqual(mesh_2d.get_group(1), mesh_2d.get_group("tp")) | 116 | self.assertEqual(mesh_2d.get_group(1), mesh_2d.get_group("tp")) |
| 117 | 117 | ||
| 118 | self.assertEqual(mesh_2d.get_group("dp"), dp_mesh.get_group()) | 118 | self.assertEqual(mesh_2d.get_group("dp"), dp_mesh.get_group()) |
| 119 | self.assertEqual(mesh_2d.get_group("tp"), tp_mesh.get_group()) | 119 | self.assertEqual(mesh_2d.get_group("tp"), tp_mesh.get_group()) |
| 120 | 120 | ||
| 121 | groups = mesh_2d.get_all_groups() | 121 | groups = mesh_2d.get_all_groups() |
| 122 | self.assertEqual(len(groups), 2) | 122 | self.assertEqual(len(groups), 2) |
| 123 | self.assertTrue(tp_mesh.get_group() in groups) | 123 | self.assertTrue(tp_mesh.get_group() in groups) |
| 124 | self.assertTrue(dp_mesh.get_group() in groups) | 124 | self.assertTrue(dp_mesh.get_group() in groups) |
| 125 | 125 | ||
| 126 | @skipIfUnsupportMultiNPU(2) | 126 | @skipIfUnsupportMultiNPU(2) |
| 127 | @with_comms | 127 | @with_comms |
| 128 | def test_get_local_rank_raises_exception(self): | 128 | def test_get_local_rank_raises_exception(self): |
| 129 | mesh_shape = (2, self.world_size // 2) | 129 | mesh_shape = (2, self.world_size // 2) |
| 130 | mesh_2d = init_device_mesh( | 130 | mesh_2d = init_device_mesh( |
| 131 | self.device_type, mesh_shape, mesh_dim_names=("dp", "tp") | 131 | self.device_type, mesh_shape, mesh_dim_names=("dp", "tp") |
| 132 | ) | 132 | ) |
| 133 | 133 | ||
| 134 | with self.assertRaisesRegex( | 134 | with self.assertRaisesRegex( |
| 135 | RuntimeError, | 135 | RuntimeError, |
| 136 | "Optional kwarg `mesh_dim` needs to be specified when device_mesh.ndim > 1.", | 136 | "Optional kwarg `mesh_dim` needs to be specified when device_mesh.ndim > 1.", |
| 137 | ): | 137 | ): |
| 138 | mesh_2d.get_local_rank() | 138 | mesh_2d.get_local_rank() |
| 139 | 139 | ||
| 140 | @skipIfUnsupportMultiNPU(2) | 140 | @skipIfUnsupportMultiNPU(2) |
| 141 | @with_comms | 141 | @with_comms |
| 142 | def test_device_mesh_init_backend(self): | 142 | def test_device_mesh_init_backend(self): |
| 143 | mesh = DeviceMesh(self.device_type, [1], _init_backend=False) | 143 | mesh = DeviceMesh(self.device_type, [1], _init_backend=False) |
| 144 | 144 | ||
| 145 | with self.assertRaisesRegex(RuntimeError, "process groups not initialized!"): | 145 | with self.assertRaisesRegex(RuntimeError, "process groups not initialized!"): |
| 146 | mesh.get_group() | 146 | mesh.get_group() |
| 147 | 147 | ||
| 148 | # coordinates should always been populated when init_backend is False, as whenever | 148 | # coordinates should always been populated when init_backend is False, as whenever |
| 149 | # we call init_backend we should make sure the default pg already created | 149 | # we call init_backend we should make sure the default pg already created |
| 150 | mesh.get_coordinate() | 150 | mesh.get_coordinate() |
| 151 | 151 | ||
| 152 | @skipIfUnsupportMultiNPU(2) | 152 | @skipIfUnsupportMultiNPU(2) |
| 153 | def test_fake_pg_device_mesh(self): | 153 | def test_fake_pg_device_mesh(self): |
| 154 | fake_store = FakeStore() | 154 | fake_store = FakeStore() |
| 155 | init_process_group("fake", store=fake_store, rank=0, world_size=self.world_size) | 155 | init_process_group("fake", store=fake_store, rank=0, world_size=self.world_size) |
| 156 | device_type = "npu" if torch.npu.is_available() else "cpu" | 156 | device_type = "npu" if torch.npu.is_available() else "cpu" |
| 157 | mesh = DeviceMesh(device_type, torch.arange(self.world_size)) | 157 | mesh = DeviceMesh(device_type, torch.arange(self.world_size)) |
| 158 | 158 | ||
| 159 | local_tensor = torch.randn(2, 8) | 159 | local_tensor = torch.randn(2, 8) |
| 160 | global_tensor = funcol.all_gather_tensor( | 160 | global_tensor = funcol.all_gather_tensor( |
| 161 | local_tensor, gather_dim=0, group=(mesh, 0) | 161 | local_tensor, gather_dim=0, group=(mesh, 0) |
| 162 | ) | 162 | ) |
| 163 | self.assertEqual(global_tensor.shape, (self.world_size * 2, 8)) | 163 | self.assertEqual(global_tensor.shape, (self.world_size * 2, 8)) |
| 164 | 164 | ||
| 165 | @skipIfUnsupportMultiNPU(2) | 165 | @skipIfUnsupportMultiNPU(2) |
| 166 | @with_comms | 166 | @with_comms |
| 167 | def test_from_group_with_global_pg(self): | 167 | def test_from_group_with_global_pg(self): |
| 168 | # Simple test: check `from_group` from a mesh pg vs. directly | 168 | # Simple test: check `from_group` from a mesh pg vs. directly |
| 169 | # initializing via `init_device_mesh` | 169 | # initializing via `init_device_mesh` |
| 170 | ref_global_mesh = init_device_mesh(self.device_type, (self.world_size,)) | 170 | ref_global_mesh = init_device_mesh(self.device_type, (self.world_size,)) |
| 171 | mesh_pg = ref_global_mesh.get_group() | 171 | mesh_pg = ref_global_mesh.get_group() |
| 172 | global_mesh = DeviceMesh.from_group(mesh_pg, self.device_type) | 172 | global_mesh = DeviceMesh.from_group(mesh_pg, self.device_type) |
| 173 | self.assertEqual(ref_global_mesh, global_mesh) | 173 | self.assertEqual(ref_global_mesh, global_mesh) |
| 174 | self.assertEqual(ref_global_mesh._dim_group_infos, global_mesh._dim_group_infos) | 174 | self.assertEqual(ref_global_mesh._dim_group_infos, global_mesh._dim_group_infos) |
| 175 | self.assertEqual( | 175 | self.assertEqual( |
| 176 | ref_global_mesh._coordinate_on_dim, global_mesh._coordinate_on_dim | 176 | ref_global_mesh._coordinate_on_dim, global_mesh._coordinate_on_dim |
| 177 | ) | 177 | ) |
| 178 | # Check when `mesh` is passed as well | 178 | # Check when `mesh` is passed as well |
| 179 | global_mesh = DeviceMesh.from_group( | 179 | global_mesh = DeviceMesh.from_group( |
| 180 | mesh_pg, self.device_type, mesh=torch.arange(self.world_size) | 180 | mesh_pg, self.device_type, mesh=torch.arange(self.world_size) |
| 181 | ) | 181 | ) |
| 182 | self.assertEqual(ref_global_mesh, global_mesh) | 182 | self.assertEqual(ref_global_mesh, global_mesh) |
| 183 | self.assertEqual(ref_global_mesh._dim_group_infos, global_mesh._dim_group_infos) | 183 | self.assertEqual(ref_global_mesh._dim_group_infos, global_mesh._dim_group_infos) |
| 184 | self.assertEqual( | 184 | self.assertEqual( |
| 185 | ref_global_mesh._coordinate_on_dim, global_mesh._coordinate_on_dim | 185 | ref_global_mesh._coordinate_on_dim, global_mesh._coordinate_on_dim |
| 186 | ) | 186 | ) |
| 187 | 187 | ||
| 188 | @skipIfUnsupportMultiNPU(2) | 188 | @skipIfUnsupportMultiNPU(2) |
| 189 | def test_raises_invalid_device_type(self): | 189 | def test_raises_invalid_device_type(self): |
| 190 | with self.assertRaisesRegex( | 190 | with self.assertRaisesRegex( |
| 191 | RuntimeError, | 191 | RuntimeError, |
| 192 | "Device type with index is not supported", | 192 | "Device type with index is not supported", |
| 193 | ): | 193 | ): |
| 194 | # test init_device_mesh with an invalid device type that contains a NPU index | 194 | # test init_device_mesh with an invalid device type that contains a NPU index |
| 195 | mesh_shape = (2, self.world_size // 2) | 195 | mesh_shape = (2, self.world_size // 2) |
| 196 | mesh_2d = init_device_mesh( | 196 | mesh_2d = init_device_mesh( |
| 197 | "npu:0", mesh_shape=mesh_shape, mesh_dim_names=("dp", "tp") | 197 | "npu:0", mesh_shape=mesh_shape, mesh_dim_names=("dp", "tp") |
| 198 | ) | 198 | ) |
| 199 | 199 | ||
| 200 | @skipIfUnsupportMultiNPU(2) | 200 | @skipIfUnsupportMultiNPU(2) |
| 201 | @with_comms | 201 | @with_comms |
| 202 | def test_set_mesh_dim_group_options(self): | 202 | def test_set_mesh_dim_group_options(self): |
| 203 | device_type = "npu" if torch.npu.is_available() else "cpu" | 203 | device_type = "npu" if torch.npu.is_available() else "cpu" |
| 204 | _mesh_resources._set_mesh_dim_group_options(1, "fake", None) | 204 | _mesh_resources._set_mesh_dim_group_options(1, "fake", None) |
| 205 | 205 | ||
| 206 | mesh_tensor = torch.arange(2).reshape(2, 1) | 206 | mesh_tensor = torch.arange(2).reshape(2, 1) |
| 207 | mesh = DeviceMesh(device_type, mesh_tensor) | 207 | mesh = DeviceMesh(device_type, mesh_tensor) |
| 208 | # Fake pg only have BackendType as BackendType::CUSTOM. | 208 | # Fake pg only have BackendType as BackendType::CUSTOM. |
| 209 | self.assertEqual(mesh.get_group(1)._get_backend_name(), "custom") | 209 | self.assertEqual(mesh.get_group(1)._get_backend_name(), "custom") |
| 210 | 210 | ||
| 211 | 211 | ||
| 212 | #DeviceMeshTest with resetting world_size to 4. | 212 | #DeviceMeshTest with resetting world_size to 4. |
| 213 | class DeviceMeshTestF(DTensorTestBase): | 213 | class DeviceMeshTestF(DTensorTestBase): |
| 214 | @property | 214 | @property |
| 215 | def world_size(self): | 215 | def world_size(self): |
| 216 | return 4 | 216 | return 4 |
| 217 | 217 | ||
| 218 | @skipIfUnsupportMultiNPU(4) | 218 | @skipIfUnsupportMultiNPU(4) |
| 219 | @with_comms | 219 | @with_comms |
| 220 | def test_assert_invalid_mesh_tensor(self): | 220 | def test_assert_invalid_mesh_tensor(self): |
| 221 | mesh = torch.arange(self.world_size).to(self.rank) | 221 | mesh = torch.arange(self.world_size).to(self.rank) |
| 222 | with self.assertRaises(ValueError): | 222 | with self.assertRaises(ValueError): |
| 223 | device_mesh = DeviceMesh(self.device_type, mesh) | 223 | device_mesh = DeviceMesh(self.device_type, mesh) |
| 224 | 224 | ||
| 225 | @skipIfUnsupportMultiNPU(4) | 225 | @skipIfUnsupportMultiNPU(4) |
| 226 | @with_comms | 226 | @with_comms |
| 227 | def test_get_local_rank(self): | 227 | def test_get_local_rank(self): |
| 228 | mesh_shape = (2, self.world_size // 2) | 228 | mesh_shape = (2, self.world_size // 2) |
| 229 | mesh_2d = init_device_mesh( | 229 | mesh_2d = init_device_mesh( |
| 230 | self.device_type, mesh_shape, mesh_dim_names=("dp", "tp") | 230 | self.device_type, mesh_shape, mesh_dim_names=("dp", "tp") |
| 231 | ) | 231 | ) |
| 232 | self.assertEqual(mesh_2d.get_local_rank("dp"), mesh_2d.get_local_rank(0)) | 232 | self.assertEqual(mesh_2d.get_local_rank("dp"), mesh_2d.get_local_rank(0)) |
| 233 | self.assertEqual(mesh_2d.get_local_rank("tp"), mesh_2d.get_local_rank(1)) | 233 | self.assertEqual(mesh_2d.get_local_rank("tp"), mesh_2d.get_local_rank(1)) |
| 234 | 234 | ||
| 235 | dp_mesh = mesh_2d["dp"] | 235 | dp_mesh = mesh_2d["dp"] |
| 236 | tp_mesh = mesh_2d["tp"] | 236 | tp_mesh = mesh_2d["tp"] |
| 237 | self.assertEqual(dp_mesh.get_local_rank(), mesh_2d.get_local_rank("dp")) | 237 | self.assertEqual(dp_mesh.get_local_rank(), mesh_2d.get_local_rank("dp")) |
| 238 | self.assertEqual(tp_mesh.get_local_rank(), mesh_2d.get_local_rank("tp")) | 238 | self.assertEqual(tp_mesh.get_local_rank(), mesh_2d.get_local_rank("tp")) |
| 239 | 239 | ||
| 240 | # Verify flattened mesh local rank correctness. | 240 | # Verify flattened mesh local rank correctness. |
| 241 | flattened_mesh = mesh_2d["dp", "tp"]._flatten() | 241 | flattened_mesh = mesh_2d["dp", "tp"]._flatten() |
| 242 | self.assertEqual(flattened_mesh.get_local_rank(), self.rank) | 242 | self.assertEqual(flattened_mesh.get_local_rank(), self.rank) |
| 243 | 243 | ||
| 244 | @skipIfUnsupportMultiNPU(4) | 244 | @skipIfUnsupportMultiNPU(4) |
| 245 | @with_comms | 245 | @with_comms |
| 246 | def test_device_mesh_2d(self): | 246 | def test_device_mesh_2d(self): |
| 247 | mesh_tensor = torch.arange(4).reshape(2, 2) | 247 | mesh_tensor = torch.arange(4).reshape(2, 2) |
| 248 | # construct a npu device mesh | 248 | # construct a npu device mesh |
| 249 | mesh = DeviceMesh(self.device_type, mesh_tensor) | 249 | mesh = DeviceMesh(self.device_type, mesh_tensor) |
| 250 | 250 | ||
| 251 | # check all dim groups | 251 | # check all dim groups |
| 252 | dim_to_subgroups = mesh.get_all_groups() | 252 | dim_to_subgroups = mesh.get_all_groups() |
| 253 | 253 | ||
| 254 | expected_ranks_by_dim = [[[0, 2], [1, 3]], [[0, 1], [2, 3]]] | 254 | expected_ranks_by_dim = [[[0, 2], [1, 3]], [[0, 1], [2, 3]]] |
| 255 | for dim, dim_group in enumerate(dim_to_subgroups): | 255 | for dim, dim_group in enumerate(dim_to_subgroups): |
| 256 | self.assertTrue(dim < 2) | 256 | self.assertTrue(dim < 2) |
| 257 | dim_ranks = expected_ranks_by_dim[dim] | 257 | dim_ranks = expected_ranks_by_dim[dim] |
| 258 | 258 | ||
| 259 | dim_group_size = get_world_size(dim_group) | 259 | dim_group_size = get_world_size(dim_group) |
| 260 | self.assertIsInstance(dim_group, ProcessGroup) | 260 | self.assertIsInstance(dim_group, ProcessGroup) |
| 261 | self.assertEqual(dim_group_size, 2) | 261 | self.assertEqual(dim_group_size, 2) |
| 262 | global_ranks = [ | 262 | global_ranks = [ |
| 263 | get_global_rank(dim_group, i) for i in range(dim_group_size) | 263 | get_global_rank(dim_group, i) for i in range(dim_group_size) |
| 264 | ] | 264 | ] |
| 265 | current_rank_expected_group_ranks = ( | 265 | current_rank_expected_group_ranks = ( |
| 266 | dim_ranks[0] if self.rank in dim_ranks[0] else dim_ranks[1] | 266 | dim_ranks[0] if self.rank in dim_ranks[0] else dim_ranks[1] |
| 267 | ) | 267 | ) |
| 268 | self.assertEqual(global_ranks, current_rank_expected_group_ranks) | 268 | self.assertEqual(global_ranks, current_rank_expected_group_ranks) |
| 269 | 269 | ||
| 270 | @skipIfUnsupportMultiNPU(4) | 270 | @skipIfUnsupportMultiNPU(4) |
| 271 | @with_comms | 271 | @with_comms |
| 272 | def test_from_group_with_invalid_mesh(self): | 272 | def test_from_group_with_invalid_mesh(self): |
| 273 | global_pg = _get_default_group() | 273 | global_pg = _get_default_group() |
| 274 | global_pg_size = global_pg.size() | 274 | global_pg_size = global_pg.size() |
| 275 | assert global_pg_size == 4, "Test assumes global world size of 4" | 275 | assert global_pg_size == 4, "Test assumes global world size of 4" |
| 276 | invalid_mesh = [[0, 1], [2, 3]] # 2D mesh when we need 1D | 276 | invalid_mesh = [[0, 1], [2, 3]] # 2D mesh when we need 1D |
| 277 | regex = r"Invalid mesh \[\[0, 1\], \[2, 3\]\] for ProcessGroup with ranks \[0, 1, 2, 3\]" | 277 | regex = r"Invalid mesh \[\[0, 1\], \[2, 3\]\] for ProcessGroup with ranks \[0, 1, 2, 3\]" |
| 278 | with self.assertRaisesRegex(ValueError, regex): | 278 | with self.assertRaisesRegex(ValueError, regex): |
| 279 | DeviceMesh.from_group( | 279 | DeviceMesh.from_group( |
| 280 | global_pg, "npu", invalid_mesh, mesh_dim_names=("dim0", "dim1") | 280 | global_pg, "npu", invalid_mesh, mesh_dim_names=("dim0", "dim1") |
| 281 | ) | 281 | ) |
| 282 | 282 | ||
| 283 | device_mesh = init_device_mesh(self.device_type, (2, 2)) | 283 | device_mesh = init_device_mesh(self.device_type, (2, 2)) |
| 284 | groups = device_mesh.get_all_groups() | 284 | groups = device_mesh.get_all_groups() |
| 285 | invalid_mesh = (0, 1, 2, 3) # 1D mesh when we need 2D | 285 | invalid_mesh = (0, 1, 2, 3) # 1D mesh when we need 2D |
| 286 | regex = r"Expects mesh with ndim equal to number of ProcessGroups but got mesh \[0, 1, 2, 3\] and 2 ProcessGroups" | 286 | regex = r"Expects mesh with ndim equal to number of ProcessGroups but got mesh \[0, 1, 2, 3\] and 2 ProcessGroups" |
| 287 | with self.assertRaisesRegex(ValueError, regex): | 287 | with self.assertRaisesRegex(ValueError, regex): |
| 288 | DeviceMesh.from_group( | 288 | DeviceMesh.from_group( |
| 289 | groups, self.device_type, invalid_mesh, mesh_dim_names=("dim0", "dim1") | 289 | groups, self.device_type, invalid_mesh, mesh_dim_names=("dim0", "dim1") |
| 290 | ) | 290 | ) |
| 291 | 291 | ||
| 292 | 292 | ||
| 293 | class DeviceMeshTestNDim(DTensorTestBase): | 293 | class DeviceMeshTestNDim(DTensorTestBase): |
| 294 | @property | 294 | @property |
| 295 | def world_size(self): | 295 | def world_size(self): |
| 296 | return 2 | 296 | return 2 |
| 297 | 297 | ||
| 298 | @skipIfUnsupportMultiNPU(2) | 298 | @skipIfUnsupportMultiNPU(2) |
| 299 | @with_comms | 299 | @with_comms |
| 300 | def test_device_mesh_parent_child_hash(self): | 300 | def test_device_mesh_parent_child_hash(self): |
| 301 | mesh_2d = init_device_mesh( | 301 | mesh_2d = init_device_mesh( |
| 302 | self.device_type, (2, self.world_size // 2), mesh_dim_names=("DP", "TP") | 302 | self.device_type, (2, self.world_size // 2), mesh_dim_names=("DP", "TP") |
| 303 | ) | 303 | ) |
| 304 | 304 | ||
| 305 | mesh_group_1 = torch.arange(0, self.world_size // 2) | 305 | mesh_group_1 = torch.arange(0, self.world_size // 2) |
| 306 | mesh_group_2 = torch.arange(self.world_size // 2, self.world_size) | 306 | mesh_group_2 = torch.arange(self.world_size // 2, self.world_size) |
| 307 | ep_mesh_1 = DeviceMesh(self.device_type, mesh_group_1) | 307 | ep_mesh_1 = DeviceMesh(self.device_type, mesh_group_1) |
| 308 | ep_mesh_2 = DeviceMesh(self.device_type, mesh_group_2) | 308 | ep_mesh_2 = DeviceMesh(self.device_type, mesh_group_2) |
| 309 | ep_mesh = ep_mesh_1 if self.rank < self.world_size // 2 else ep_mesh_2 | 309 | ep_mesh = ep_mesh_1 if self.rank < self.world_size // 2 else ep_mesh_2 |
| 310 | # ep_mesh is considered different from mesh_2d["TP"] | 310 | # ep_mesh is considered different from mesh_2d["TP"] |
| 311 | self.assertEqual(mesh_2d["TP"]._flatten_mesh_list, ep_mesh._flatten_mesh_list) | 311 | self.assertEqual(mesh_2d["TP"]._flatten_mesh_list, ep_mesh._flatten_mesh_list) |
| 312 | self.assertEqual(mesh_2d["TP"].mesh.shape, ep_mesh.mesh.shape) | 312 | self.assertEqual(mesh_2d["TP"].mesh.shape, ep_mesh.mesh.shape) |
| 313 | self.assertEqual(mesh_2d["TP"].device_type, ep_mesh.device_type) | 313 | self.assertEqual(mesh_2d["TP"].device_type, ep_mesh.device_type) |
| 314 | self.assertNotEqual(mesh_2d["TP"].mesh_dim_names, ep_mesh.mesh_dim_names) | 314 | self.assertNotEqual(mesh_2d["TP"].mesh_dim_names, ep_mesh.mesh_dim_names) |
| 315 | self.assertEqual(mesh_2d["TP"]._thread_id, ep_mesh._thread_id) | 315 | self.assertEqual(mesh_2d["TP"]._thread_id, ep_mesh._thread_id) |
| 316 | self.assertNotEqual(hash(mesh_2d["TP"]), hash(ep_mesh)) | 316 | self.assertNotEqual(hash(mesh_2d["TP"]), hash(ep_mesh)) |
| 317 | self.assertNotEqual(mesh_2d["TP"], ep_mesh) | 317 | self.assertNotEqual(mesh_2d["TP"], ep_mesh) |
| 318 | 318 | ||
| 319 | another_mesh_1 = DeviceMesh(self.device_type, mesh_group_1) | 319 | another_mesh_1 = DeviceMesh(self.device_type, mesh_group_1) |
| 320 | another_mesh_2 = DeviceMesh(self.device_type, mesh_group_2) | 320 | another_mesh_2 = DeviceMesh(self.device_type, mesh_group_2) |
| 321 | another_mesh = ( | 321 | another_mesh = ( |
| 322 | another_mesh_1 if self.rank < self.world_size // 2 else another_mesh_2 | 322 | another_mesh_1 if self.rank < self.world_size // 2 else another_mesh_2 |
| 323 | ) | 323 | ) |
| 324 | # another_mesh is considered the same as ep_mesh | 324 | # another_mesh is considered the same as ep_mesh |
| 325 | self.assertEqual(ep_mesh._flatten_mesh_list, another_mesh._flatten_mesh_list) | 325 | self.assertEqual(ep_mesh._flatten_mesh_list, another_mesh._flatten_mesh_list) |
| 326 | self.assertEqual(ep_mesh.mesh.shape, another_mesh.mesh.shape) | 326 | self.assertEqual(ep_mesh.mesh.shape, another_mesh.mesh.shape) |
| 327 | self.assertEqual(ep_mesh.device_type, another_mesh.device_type) | 327 | self.assertEqual(ep_mesh.device_type, another_mesh.device_type) |
| 328 | self.assertEqual(ep_mesh.mesh_dim_names, another_mesh.mesh_dim_names) | 328 | self.assertEqual(ep_mesh.mesh_dim_names, another_mesh.mesh_dim_names) |
| 329 | self.assertEqual(ep_mesh._thread_id, another_mesh._thread_id) | 329 | self.assertEqual(ep_mesh._thread_id, another_mesh._thread_id) |
| 330 | self.assertEqual(hash(ep_mesh), hash(another_mesh)) | 330 | self.assertEqual(hash(ep_mesh), hash(another_mesh)) |
| 331 | self.assertEqual(ep_mesh, another_mesh) | 331 | self.assertEqual(ep_mesh, another_mesh) |
| 332 | 332 | ||
| 333 | 333 | ||
| 334 | #DeviceMeshTestNDim with resetting world_size to 8. | 334 | #DeviceMeshTestNDim with resetting world_size to 8. |
| 335 | class DeviceMeshTestNDimE(DTensorTestBase): | 335 | class DeviceMeshTestNDimE(DTensorTestBase): |
| 336 | @property | 336 | @property |
| 337 | def world_size(self): | 337 | def world_size(self): |
| 338 | return 8 | 338 | return 8 |
| 339 | 339 | ||
| 340 | @skipIfUnsupportMultiNPU(8) | 340 | @skipIfUnsupportMultiNPU(8) |
| 341 | @with_comms | 341 | @with_comms |
| 342 | def test_device_mesh_nd(self): | 342 | def test_device_mesh_nd(self): |
| 343 | # construct a npu device mesh | 343 | # construct a npu device mesh |
| 344 | mesh_tensor = torch.arange(8).reshape(2, 2, 2) | 344 | mesh_tensor = torch.arange(8).reshape(2, 2, 2) |
| 345 | mesh = DeviceMesh(self.device_type, mesh_tensor) | 345 | mesh = DeviceMesh(self.device_type, mesh_tensor) |
| 346 | 346 | ||
| 347 | # check all dim groups | 347 | # check all dim groups |
| 348 | dim_to_subgroups = mesh.get_all_groups() | 348 | dim_to_subgroups = mesh.get_all_groups() |
| 349 | 349 | ||
| 350 | for dim, dim_group in enumerate(dim_to_subgroups): | 350 | for dim, dim_group in enumerate(dim_to_subgroups): |
| 351 | self.assertTrue(dim < mesh_tensor.ndim) | 351 | self.assertTrue(dim < mesh_tensor.ndim) |
| 352 | dim_ranks = mesh_tensor.swapdims(-1, dim).reshape(-1, 2) | 352 | dim_ranks = mesh_tensor.swapdims(-1, dim).reshape(-1, 2) |
| 353 | 353 | ||
| 354 | dim_group_size = get_world_size(dim_group) | 354 | dim_group_size = get_world_size(dim_group) |
| 355 | self.assertIsInstance(dim_group, ProcessGroup) | 355 | self.assertIsInstance(dim_group, ProcessGroup) |
| 356 | self.assertEqual(dim_group_size, 2) | 356 | self.assertEqual(dim_group_size, 2) |
| 357 | global_ranks = [ | 357 | global_ranks = [ |
| 358 | get_global_rank(dim_group, i) for i in range(dim_group_size) | 358 | get_global_rank(dim_group, i) for i in range(dim_group_size) |
| 359 | ] | 359 | ] |
| 360 | for ranks in dim_ranks: | 360 | for ranks in dim_ranks: |
| 361 | if self.rank in ranks: | 361 | if self.rank in ranks: |
| 362 | self.assertEqual(global_ranks, ranks.tolist()) | 362 | self.assertEqual(global_ranks, ranks.tolist()) |
| 363 | 363 | ||
| 364 | @skipIfUnsupportMultiNPU(8) | 364 | @skipIfUnsupportMultiNPU(8) |
| 365 | @with_comms | 365 | @with_comms |
| 366 | def test_device_mesh_hash(self): | 366 | def test_device_mesh_hash(self): |
| 367 | mesh_tensor_2d = torch.arange(8).reshape(4, 2) | 367 | mesh_tensor_2d = torch.arange(8).reshape(4, 2) |
| 368 | mesh = DeviceMesh(self.device_type, mesh_tensor_2d) | 368 | mesh = DeviceMesh(self.device_type, mesh_tensor_2d) |
| 369 | mesh2 = DeviceMesh(self.device_type, mesh_tensor_2d) | 369 | mesh2 = DeviceMesh(self.device_type, mesh_tensor_2d) |
| 370 | self.assertEqual(hash(mesh), hash(mesh2)) | 370 | self.assertEqual(hash(mesh), hash(mesh2)) |
| 371 | mesh_tensor_3d = torch.arange(8).reshape(2, 2, 2) | 371 | mesh_tensor_3d = torch.arange(8).reshape(2, 2, 2) |
| 372 | mesh3 = DeviceMesh(self.device_type, mesh_tensor_3d) | 372 | mesh3 = DeviceMesh(self.device_type, mesh_tensor_3d) |
| 373 | self.assertNotEqual(hash(mesh), hash(mesh3)) | 373 | self.assertNotEqual(hash(mesh), hash(mesh3)) |
| 374 | self.assertNotEqual(hash(mesh2), hash(mesh3)) | 374 | self.assertNotEqual(hash(mesh2), hash(mesh3)) |
| 375 | 375 | ||
| 376 | @skipIfUnsupportMultiNPU(8) | 376 | @skipIfUnsupportMultiNPU(8) |
| 377 | @with_comms | 377 | @with_comms |
| 378 | def test_get_local_rank_3d(self): | 378 | def test_get_local_rank_3d(self): |
| 379 | """ | 379 | """ |
| 380 | If we have a 3D mesh and we want to apply dp, pp, tp to it, | 380 | If we have a 3D mesh and we want to apply dp, pp, tp to it, |
| 381 | mesh_dim_names = ["dp", "pp", "tp"], and the mesh tensor would be: | 381 | mesh_dim_names = ["dp", "pp", "tp"], and the mesh tensor would be: |
| 382 | mesh_3d_tensor = [ | 382 | mesh_3d_tensor = [ |
| 383 | [ | 383 | [ |
| 384 | [0, 1], | 384 | [0, 1], |
| 385 | [2, 3], | 385 | [2, 3], |
| 386 | ], | 386 | ], |
| 387 | [ | 387 | [ |
| 388 | [4, 5], | 388 | [4, 5], |
| 389 | [6, 7], | 389 | [6, 7], |
| 390 | ] | 390 | ] |
| 391 | 391 | ||
| 392 | ] | 392 | ] |
| 393 | """ | 393 | """ |
| 394 | mesh_shape = (2, 2, 2) | 394 | mesh_shape = (2, 2, 2) |
| 395 | mesh_3d = init_device_mesh( | 395 | mesh_3d = init_device_mesh( |
| 396 | self.device_type, mesh_shape, mesh_dim_names=("dp", "pp", "tp") | 396 | self.device_type, mesh_shape, mesh_dim_names=("dp", "pp", "tp") |
| 397 | ) | 397 | ) |
| 398 | 398 | ||
| 399 | # tp_rank_0: [0, 2, 4, 6], tp_rank_1: [1, 3, 5, 7] | 399 | # tp_rank_0: [0, 2, 4, 6], tp_rank_1: [1, 3, 5, 7] |
| 400 | tp_rank = mesh_3d.get_local_rank("tp") | 400 | tp_rank = mesh_3d.get_local_rank("tp") |
| 401 | expected_tp_rank = self.rank % 2 | 401 | expected_tp_rank = self.rank % 2 |
| 402 | self.assertEqual(tp_rank, expected_tp_rank) | 402 | self.assertEqual(tp_rank, expected_tp_rank) |
| 403 | 403 | ||
| 404 | # pp_rank_0: [0, 1, 4, 5], pp_rank_1: [2, 3, 6, 7] | 404 | # pp_rank_0: [0, 1, 4, 5], pp_rank_1: [2, 3, 6, 7] |
| 405 | pp_rank = mesh_3d.get_local_rank("pp") | 405 | pp_rank = mesh_3d.get_local_rank("pp") |
| 406 | expected_pp_rank = 0 if self.rank % 4 <= 1 else 1 | 406 | expected_pp_rank = 0 if self.rank % 4 <= 1 else 1 |
| 407 | self.assertEqual(pp_rank, expected_pp_rank) | 407 | self.assertEqual(pp_rank, expected_pp_rank) |
| 408 | 408 | ||
| 409 | # dp_rank_0: [0, 1, 2, 3], dp_rank_1: [4, 5, 6, 7] | 409 | # dp_rank_0: [0, 1, 2, 3], dp_rank_1: [4, 5, 6, 7] |
| 410 | dp_rank = mesh_3d.get_local_rank("dp") | 410 | dp_rank = mesh_3d.get_local_rank("dp") |
| 411 | expected_dp_rank = self.rank // 4 | 411 | expected_dp_rank = self.rank // 4 |
| 412 | self.assertEqual(dp_rank, expected_dp_rank) | 412 | self.assertEqual(dp_rank, expected_dp_rank) |
| 413 | 413 | ||
| 414 | @skipIfUnsupportMultiNPU(8) | 414 | @skipIfUnsupportMultiNPU(8) |
| 415 | @with_comms | 415 | @with_comms |
| 416 | def test_from_group_with_mesh_shape(self): | 416 | def test_from_group_with_mesh_shape(self): |
| 417 | """Tests ``from_group`` when passing ``mesh_shape`` as 2D.""" | 417 | """Tests ``from_group`` when passing ``mesh_shape`` as 2D.""" |
| 418 | # Consider two different logical views of the same mesh: | 418 | # Consider two different logical views of the same mesh: |
| 419 | # - (4, 2) ("dp", "tp") mesh | 419 | # - (4, 2) ("dp", "tp") mesh |
| 420 | # - (2, 2, 2) ("dp_replicate", "dp_shard", "tp") mesh | 420 | # - (2, 2, 2) ("dp_replicate", "dp_shard", "tp") mesh |
| 421 | mesh_shape = (2, 2, 2) | 421 | mesh_shape = (2, 2, 2) |
| 422 | mesh_dim_names = ("dp_replicate", "dp_shard", "tp") | 422 | mesh_dim_names = ("dp_replicate", "dp_shard", "tp") |
| 423 | ref_mesh = init_device_mesh( | 423 | ref_mesh = init_device_mesh( |
| 424 | self.device_type, mesh_shape, mesh_dim_names=mesh_dim_names | 424 | self.device_type, mesh_shape, mesh_dim_names=mesh_dim_names |
| 425 | ) | 425 | ) |
| 426 | 426 | ||
| 427 | dp_shard_group = ref_mesh["dp_shard"].get_group() | 427 | dp_shard_group = ref_mesh["dp_shard"].get_group() |
| 428 | dp_replicate_group = ref_mesh["dp_replicate"].get_group() | 428 | dp_replicate_group = ref_mesh["dp_replicate"].get_group() |
| 429 | 429 | ||
| 430 | dp_mesh = DeviceMesh.from_group( | 430 | dp_mesh = DeviceMesh.from_group( |
| 431 | [dp_replicate_group, dp_shard_group], | 431 | [dp_replicate_group, dp_shard_group], |
| 432 | self.device_type, | 432 | self.device_type, |
| 433 | mesh=ref_mesh.mesh[:, :, ref_mesh.get_local_rank(2)], | 433 | mesh=ref_mesh.mesh[:, :, ref_mesh.get_local_rank(2)], |
| 434 | mesh_dim_names=mesh_dim_names[:2], | 434 | mesh_dim_names=mesh_dim_names[:2], |
| 435 | ) | 435 | ) |
| 436 | 436 | ||
| 437 | ref_mesh_dp_dim_group_infos = ref_mesh._dim_group_infos[:2] | 437 | ref_mesh_dp_dim_group_infos = ref_mesh._dim_group_infos[:2] |
| 438 | for (_, ref_ranks, _), (_, ranks, _) in zip( | 438 | for (_, ref_ranks, _), (_, ranks, _) in zip( |
| 439 | ref_mesh_dp_dim_group_infos, dp_mesh._dim_group_infos | 439 | ref_mesh_dp_dim_group_infos, dp_mesh._dim_group_infos |
| 440 | ): | 440 | ): |
| 441 | self.assertEqual(ref_ranks, ranks) | 441 | self.assertEqual(ref_ranks, ranks) |
| 442 | # Cannot check directly for mesh equality since parent meshes are not | 442 | # Cannot check directly for mesh equality since parent meshes are not |
| 443 | # the same since the ref's parent mesh is 3D | 443 | # the same since the ref's parent mesh is 3D |
| 444 | self.assertEqual(dp_mesh["dp_replicate"].mesh, ref_mesh["dp_replicate"].mesh) | 444 | self.assertEqual(dp_mesh["dp_replicate"].mesh, ref_mesh["dp_replicate"].mesh) |
| 445 | for (_, ref_ranks, _), (_, ranks, _) in zip( | 445 | for (_, ref_ranks, _), (_, ranks, _) in zip( |
| 446 | dp_mesh["dp_replicate"]._dim_group_infos, | 446 | dp_mesh["dp_replicate"]._dim_group_infos, |
| 447 | ref_mesh["dp_replicate"]._dim_group_infos, | 447 | ref_mesh["dp_replicate"]._dim_group_infos, |
| 448 | ): | 448 | ): |
| 449 | self.assertEqual(ref_ranks, ranks) | 449 | self.assertEqual(ref_ranks, ranks) |
| 450 | self.assertEqual(dp_mesh["dp_shard"].mesh, ref_mesh["dp_shard"].mesh) | 450 | self.assertEqual(dp_mesh["dp_shard"].mesh, ref_mesh["dp_shard"].mesh) |
| 451 | for (_, ref_ranks, _), (_, ranks, _) in zip( | 451 | for (_, ref_ranks, _), (_, ranks, _) in zip( |
| 452 | dp_mesh["dp_shard"]._dim_group_infos, ref_mesh["dp_shard"]._dim_group_infos | 452 | dp_mesh["dp_shard"]._dim_group_infos, ref_mesh["dp_shard"]._dim_group_infos |
| 453 | ): | 453 | ): |
| 454 | self.assertEqual(ref_ranks, ranks) | 454 | self.assertEqual(ref_ranks, ranks) |
| 455 | 455 | ||
| 456 | 456 | ||
| 457 | class InitDeviceMeshTest(DTensorTestBase): | 457 | class InitDeviceMeshTest(DTensorTestBase): |
| 458 | @property | 458 | @property |
| 459 | def world_size(self): | 459 | def world_size(self): |
| 460 | return 2 | 460 | return 2 |
| 461 | 461 | ||
| 462 | @skipIfUnsupportMultiNPU(2) | 462 | @skipIfUnsupportMultiNPU(2) |
| 463 | @with_comms | 463 | @with_comms |
| 464 | def test_init_device_mesh(self): | 464 | def test_init_device_mesh(self): |
| 465 | mesh_shape = (2, 1) | 465 | mesh_shape = (2, 1) |
| 466 | mesh_dim_names = ("DP", "TP") | 466 | mesh_dim_names = ("DP", "TP") |
| 467 | ref_mesh = DeviceMesh( | 467 | ref_mesh = DeviceMesh( |
| 468 | self.device_type, | 468 | self.device_type, |
| 469 | torch.arange(2).view(mesh_shape), | 469 | torch.arange(2).view(mesh_shape), |
| 470 | mesh_dim_names=mesh_dim_names, | 470 | mesh_dim_names=mesh_dim_names, |
| 471 | ) | 471 | ) |
| 472 | 472 | ||
| 473 | # test init_device_mesh with mesh_dim_names | 473 | # test init_device_mesh with mesh_dim_names |
| 474 | mesh_2d = init_device_mesh( | 474 | mesh_2d = init_device_mesh( |
| 475 | self.device_type, mesh_shape, mesh_dim_names=mesh_dim_names | 475 | self.device_type, mesh_shape, mesh_dim_names=mesh_dim_names |
| 476 | ) | 476 | ) |
| 477 | self.assertEqual(mesh_2d, ref_mesh) | 477 | self.assertEqual(mesh_2d, ref_mesh) |
| 478 | self.assertEqual(mesh_2d.mesh_dim_names, mesh_dim_names) | 478 | self.assertEqual(mesh_2d.mesh_dim_names, mesh_dim_names) |
| 479 | 479 | ||
| 480 | @skipIfUnsupportMultiNPU(2) | 480 | @skipIfUnsupportMultiNPU(2) |
| 481 | @with_comms | 481 | @with_comms |
| 482 | def test_raises_duplicate_mesh_dim_names(self): | 482 | def test_raises_duplicate_mesh_dim_names(self): |
| 483 | with self.assertRaisesRegex( | 483 | with self.assertRaisesRegex( |
| 484 | RuntimeError, | 484 | RuntimeError, |
| 485 | "Each mesh_dim_name must be unique.", | 485 | "Each mesh_dim_name must be unique.", |
| 486 | ): | 486 | ): |
| 487 | mesh = init_device_mesh( | 487 | mesh = init_device_mesh( |
| 488 | self.device_type, | 488 | self.device_type, |
| 489 | (1, 2), | 489 | (1, 2), |
| 490 | mesh_dim_names=["dp", "dp"], | 490 | mesh_dim_names=["dp", "dp"], |
| 491 | ) | 491 | ) |
| 492 | 492 | ||
| 493 | @skipIfUnsupportMultiNPU(2) | 493 | @skipIfUnsupportMultiNPU(2) |
| 494 | @with_comms | 494 | @with_comms |
| 495 | def test_raises_mesh_shape_mesh_dim_names_mismatch(self): | 495 | def test_raises_mesh_shape_mesh_dim_names_mismatch(self): |
| 496 | with self.assertRaisesRegex( | 496 | with self.assertRaisesRegex( |
| 497 | RuntimeError, | 497 | RuntimeError, |
| 498 | "mesh_shape and mesh_dim_names should have same length!", | 498 | "mesh_shape and mesh_dim_names should have same length!", |
| 499 | ): | 499 | ): |
| 500 | mesh = init_device_mesh( | 500 | mesh = init_device_mesh( |
| 501 | self.device_type, | 501 | self.device_type, |
| 502 | (2,), | 502 | (2,), |
| 503 | mesh_dim_names=["dp", "tp"], | 503 | mesh_dim_names=["dp", "tp"], |
| 504 | ) | 504 | ) |
| 505 | 505 | ||
| 506 | 506 | ||
| 507 | class TestDeviceMeshGetItem(DTensorTestBase): | 507 | class TestDeviceMeshGetItem(DTensorTestBase): |
| 508 | @property | 508 | @property |
| 509 | def world_size(self): | 509 | def world_size(self): |
| 510 | return 2 | 510 | return 2 |
| 511 | 511 | ||
| 512 | @skipIfUnsupportMultiNPU(2) | 512 | @skipIfUnsupportMultiNPU(2) |
| 513 | @with_comms | 513 | @with_comms |
| 514 | def test_raises_no_mesh_dim_found(self): | 514 | def test_raises_no_mesh_dim_found(self): |
| 515 | with self.assertRaisesRegex( | 515 | with self.assertRaisesRegex( |
| 516 | RuntimeError, "Cannot slice a DeviceMesh without mesh_dim_names!" | 516 | RuntimeError, "Cannot slice a DeviceMesh without mesh_dim_names!" |
| 517 | ): | 517 | ): |
| 518 | mesh = init_device_mesh(self.device_type, (1, 2)) | 518 | mesh = init_device_mesh(self.device_type, (1, 2)) |
| 519 | child_mesh = mesh["DP"] | 519 | child_mesh = mesh["DP"] |
| 520 | 520 | ||
| 521 | @skipIfUnsupportMultiNPU(2) | 521 | @skipIfUnsupportMultiNPU(2) |
| 522 | @with_comms | 522 | @with_comms |
| 523 | def test_raises_invalid_mesh_dim_name(self): | 523 | def test_raises_invalid_mesh_dim_name(self): |
| 524 | child_mesh_dim_name = ("PP",) | 524 | child_mesh_dim_name = ("PP",) |
| 525 | with self.assertRaisesRegex(KeyError, "Invalid mesh_dim_name"): | 525 | with self.assertRaisesRegex(KeyError, "Invalid mesh_dim_name"): |
| 526 | mesh_dim_names = ("DP", "TP") | 526 | mesh_dim_names = ("DP", "TP") |
| 527 | mesh = init_device_mesh( | 527 | mesh = init_device_mesh( |
| 528 | self.device_type, (1, 2), mesh_dim_names=mesh_dim_names | 528 | self.device_type, (1, 2), mesh_dim_names=mesh_dim_names |
| 529 | ) | 529 | ) |
| 530 | child_mesh = mesh[child_mesh_dim_name] | 530 | child_mesh = mesh[child_mesh_dim_name] |
| 531 | 531 | ||
| 532 | @skipIfUnsupportMultiNPU(2) | 532 | @skipIfUnsupportMultiNPU(2) |
| 533 | @with_comms | 533 | @with_comms |
| 534 | def test_get_item_1d(self): | 534 | def test_get_item_1d(self): |
| 535 | mesh = init_device_mesh(self.device_type, (2,), mesh_dim_names=("dp",)) | 535 | mesh = init_device_mesh(self.device_type, (2,), mesh_dim_names=("dp",)) |
| 536 | # Make sure slicing out 1D mesh from a 1D mesh works. | 536 | # Make sure slicing out 1D mesh from a 1D mesh works. |
| 537 | dp_mesh = mesh["dp"] | 537 | dp_mesh = mesh["dp"] |
| 538 | self.assertEqual(dp_mesh, mesh) | 538 | self.assertEqual(dp_mesh, mesh) |
| 539 | 539 | ||
| 540 | with self.assertRaisesRegex(KeyError, "Invalid mesh_dim_name"): | 540 | with self.assertRaisesRegex(KeyError, "Invalid mesh_dim_name"): |
| 541 | dp_mesh = mesh["dim0"] | 541 | dp_mesh = mesh["dim0"] |
| 542 | 542 | ||
| 543 | @skipIfUnsupportMultiNPU(2) | 543 | @skipIfUnsupportMultiNPU(2) |
| 544 | @with_comms | 544 | @with_comms |
| 545 | def test_cache_and_reuse_submesh_slice_result(self): | 545 | def test_cache_and_reuse_submesh_slice_result(self): |
| 546 | mesh = init_device_mesh(self.device_type, (1, 2), mesh_dim_names=("dp", "tp")) | 546 | mesh = init_device_mesh(self.device_type, (1, 2), mesh_dim_names=("dp", "tp")) |
| 547 | 547 | ||
| 548 | dp_mesh = mesh["dp"] | 548 | dp_mesh = mesh["dp"] |
| 549 | ref_pg_count = _world.group_count | 549 | ref_pg_count = _world.group_count |
| 550 | 550 | ||
| 551 | # When we access the "dp" slice again it should not create any new pg. | 551 | # When we access the "dp" slice again it should not create any new pg. |
| 552 | # As we are just using the cached result so the pg count should be the same. | 552 | # As we are just using the cached result so the pg count should be the same. |
| 553 | dp_mesh_2 = mesh["dp"] | 553 | dp_mesh_2 = mesh["dp"] |
| 554 | self.assertEqual(ref_pg_count, _world.group_count) | 554 | self.assertEqual(ref_pg_count, _world.group_count) |
| 555 | 555 | ||
| 556 | # When we access the "tp" slice, it should not create a new pg, as the "tp" slice would | 556 | # When we access the "tp" slice, it should not create a new pg, as the "tp" slice would |
| 557 | # just reuse the parent mesh pg. | 557 | # just reuse the parent mesh pg. |
| 558 | tp_mesh = mesh["tp"] | 558 | tp_mesh = mesh["tp"] |
| 559 | self.assertEqual(_world.group_count, ref_pg_count) | 559 | self.assertEqual(_world.group_count, ref_pg_count) |
| 560 | 560 | ||
| 561 | @skipIfUnsupportMultiNPU(2) | 561 | @skipIfUnsupportMultiNPU(2) |
| 562 | @with_comms | 562 | @with_comms |
| 563 | def test_flatten_mesh_3d(self): | 563 | def test_flatten_mesh_3d(self): |
| 564 | mesh_shape = (1, 1, 2) | 564 | mesh_shape = (1, 1, 2) |
| 565 | mesh_dim_names = ("dp", "cp", "tp") | 565 | mesh_dim_names = ("dp", "cp", "tp") |
| 566 | mesh_3d = init_device_mesh( | 566 | mesh_3d = init_device_mesh( |
| 567 | self.device_type, mesh_shape, mesh_dim_names=mesh_dim_names | 567 | self.device_type, mesh_shape, mesh_dim_names=mesh_dim_names |
| 568 | ) | 568 | ) |
| 569 | 569 | ||
| 570 | # Test flatten contiguous dims | 570 | # Test flatten contiguous dims |
| 571 | dp_cp_mesh = mesh_3d["dp", "cp"] | 571 | dp_cp_mesh = mesh_3d["dp", "cp"] |
| 572 | flattened_dp_cp_mesh = dp_cp_mesh._flatten() | 572 | flattened_dp_cp_mesh = dp_cp_mesh._flatten() |
| 573 | self.assertEqual(dp_cp_mesh.mesh.flatten(), flattened_dp_cp_mesh.mesh) | 573 | self.assertEqual(dp_cp_mesh.mesh.flatten(), flattened_dp_cp_mesh.mesh) |
| 574 | self.assertEqual(flattened_dp_cp_mesh.mesh_dim_names[0], "dp_cp") | 574 | self.assertEqual(flattened_dp_cp_mesh.mesh_dim_names[0], "dp_cp") |
| 575 | root_mesh = _mesh_resources.get_root_mesh(dp_cp_mesh) | 575 | root_mesh = _mesh_resources.get_root_mesh(dp_cp_mesh) |
| 576 | self.assertEqual(root_mesh, mesh_3d) | 576 | self.assertEqual(root_mesh, mesh_3d) |
| 577 | flatten_mesh_root_dims = _mesh_resources.flatten_name_to_root_dims[root_mesh][ | 577 | flatten_mesh_root_dims = _mesh_resources.flatten_name_to_root_dims[root_mesh][ |
| 578 | "dp_cp" | 578 | "dp_cp" |
| 579 | ] | 579 | ] |
| 580 | self.assertEqual(flatten_mesh_root_dims, (0, 1)) | 580 | self.assertEqual(flatten_mesh_root_dims, (0, 1)) |
| 581 | 581 | ||
| 582 | ref_pg_count = _world.group_count | 582 | ref_pg_count = _world.group_count |
| 583 | # Calling flatten again should not create a new pg. | 583 | # Calling flatten again should not create a new pg. |
| 584 | flattened_dp_cp_mesh_2 = dp_cp_mesh._flatten() | 584 | flattened_dp_cp_mesh_2 = dp_cp_mesh._flatten() |
| 585 | self.assertEqual(flattened_dp_cp_mesh, flattened_dp_cp_mesh_2) | 585 | self.assertEqual(flattened_dp_cp_mesh, flattened_dp_cp_mesh_2) |
| 586 | self.assertEqual(ref_pg_count, _world.group_count) | 586 | self.assertEqual(ref_pg_count, _world.group_count) |
| 587 | 587 | ||
| 588 | # Test flatten non-contiguous dims | 588 | # Test flatten non-contiguous dims |
| 589 | dp_tp_mesh = mesh_3d["dp", "tp"] | 589 | dp_tp_mesh = mesh_3d["dp", "tp"] |
| 590 | flattened_dp_tp_mesh = dp_tp_mesh._flatten() | 590 | flattened_dp_tp_mesh = dp_tp_mesh._flatten() |
| 591 | self.assertEqual(dp_tp_mesh.mesh.flatten(), flattened_dp_tp_mesh.mesh) | 591 | self.assertEqual(dp_tp_mesh.mesh.flatten(), flattened_dp_tp_mesh.mesh) |
| 592 | self.assertEqual(flattened_dp_tp_mesh.mesh_dim_names[0], "dp_tp") | 592 | self.assertEqual(flattened_dp_tp_mesh.mesh_dim_names[0], "dp_tp") |
| 593 | root_mesh = _mesh_resources.get_root_mesh(dp_tp_mesh) | 593 | root_mesh = _mesh_resources.get_root_mesh(dp_tp_mesh) |
| 594 | self.assertEqual(root_mesh, mesh_3d) | 594 | self.assertEqual(root_mesh, mesh_3d) |
| 595 | flatten_mesh_root_dims = _mesh_resources.flatten_name_to_root_dims[root_mesh][ | 595 | flatten_mesh_root_dims = _mesh_resources.flatten_name_to_root_dims[root_mesh][ |
| 596 | "dp_tp" | 596 | "dp_tp" |
| 597 | ] | 597 | ] |
| 598 | self.assertEqual(flatten_mesh_root_dims, (0, 2)) | 598 | self.assertEqual(flatten_mesh_root_dims, (0, 2)) |
| 599 | 599 | ||
| 600 | # Test flatten with a flattened mesh_dim_name | 600 | # Test flatten with a flattened mesh_dim_name |
| 601 | cp_tp_mesh = mesh_3d["cp", "tp"] | 601 | cp_tp_mesh = mesh_3d["cp", "tp"] |
| 602 | cp_tp_mesh._flatten("dummy") | 602 | cp_tp_mesh._flatten("dummy") |
| 603 | self.assertEqual(mesh_3d["dummy"].mesh_dim_names[0], "dummy") | 603 | self.assertEqual(mesh_3d["dummy"].mesh_dim_names[0], "dummy") |
| 604 | 604 | ||
| 605 | @skipIfUnsupportMultiNPU(2) | 605 | @skipIfUnsupportMultiNPU(2) |
| 606 | @with_comms | 606 | @with_comms |
| 607 | def test_flatten_mesh_4d(self): | 607 | def test_flatten_mesh_4d(self): |
| 608 | with self.subTest(eager_init=True): | 608 | with self.subTest(eager_init=True): |
| 609 | mesh_shape = (2, 1, 1, 1) | 609 | mesh_shape = (2, 1, 1, 1) |
| 610 | mesh_dim_names = ("dp_replicate", "dp_shard", "cp", "tp") | 610 | mesh_dim_names = ("dp_replicate", "dp_shard", "cp", "tp") |
| 611 | mesh_4d = init_device_mesh( | 611 | mesh_4d = init_device_mesh( |
| 612 | self.device_type, mesh_shape, mesh_dim_names=mesh_dim_names | 612 | self.device_type, mesh_shape, mesh_dim_names=mesh_dim_names |
| 613 | ) | 613 | ) |
| 614 | 614 | ||
| 615 | # flatten HSDP and CP into one mesh | 615 | # flatten HSDP and CP into one mesh |
| 616 | dp_cp_mesh = mesh_4d[mesh_dim_names[:3]]._flatten("dp_cp") | 616 | dp_cp_mesh = mesh_4d[mesh_dim_names[:3]]._flatten("dp_cp") |
| 617 | # check flattened mesh integrity | 617 | # check flattened mesh integrity |
| 618 | self.assertEqual(mesh_4d["dp_cp"].mesh.flatten(), dp_cp_mesh.mesh) | 618 | self.assertEqual(mesh_4d["dp_cp"].mesh.flatten(), dp_cp_mesh.mesh) |
| 619 | # check flattened mesh dim names is correct | 619 | # check flattened mesh dim names is correct |
| 620 | self.assertEqual(dp_cp_mesh.mesh_dim_names, ("dp_cp",)) | 620 | self.assertEqual(dp_cp_mesh.mesh_dim_names, ("dp_cp",)) |
| 621 | # check flattened mesh dependency | 621 | # check flattened mesh dependency |
| 622 | self.assertEqual(_mesh_resources.get_root_mesh(dp_cp_mesh), mesh_4d) | 622 | self.assertEqual(_mesh_resources.get_root_mesh(dp_cp_mesh), mesh_4d) |
| 623 | 623 | ||
| 624 | with self.subTest(eager_init=False): | 624 | with self.subTest(eager_init=False): |
| 625 | mesh_shape = (2, 1, 1, 1) | 625 | mesh_shape = (2, 1, 1, 1) |
| 626 | mesh_dim_names = ("dp_replicate", "dp_shard", "cp", "tp") | 626 | mesh_dim_names = ("dp_replicate", "dp_shard", "cp", "tp") |
| 627 | mesh_4d = init_device_mesh( | 627 | mesh_4d = init_device_mesh( |
| 628 | self.device_type, mesh_shape, mesh_dim_names=mesh_dim_names | 628 | self.device_type, mesh_shape, mesh_dim_names=mesh_dim_names |
| 629 | ) | 629 | ) |
| 630 | 630 | ||
| 631 | # flatten HSDP and CP into one mesh | 631 | # flatten HSDP and CP into one mesh |
| 632 | dp_cp_mesh = mesh_4d[mesh_dim_names[:3]]._flatten("dp_cp") | 632 | dp_cp_mesh = mesh_4d[mesh_dim_names[:3]]._flatten("dp_cp") |
| 633 | # check flattened mesh integrity | 633 | # check flattened mesh integrity |
| 634 | self.assertEqual(mesh_4d["dp_cp"].mesh.flatten(), dp_cp_mesh.mesh) | 634 | self.assertEqual(mesh_4d["dp_cp"].mesh.flatten(), dp_cp_mesh.mesh) |
| 635 | # check flattened mesh dim names is correct | 635 | # check flattened mesh dim names is correct |
| 636 | self.assertEqual(dp_cp_mesh.mesh_dim_names, ("dp_cp",)) | 636 | self.assertEqual(dp_cp_mesh.mesh_dim_names, ("dp_cp",)) |
| 637 | # check flattened mesh dependency | 637 | # check flattened mesh dependency |
| 638 | self.assertEqual(_mesh_resources.get_root_mesh(dp_cp_mesh), mesh_4d) | 638 | self.assertEqual(_mesh_resources.get_root_mesh(dp_cp_mesh), mesh_4d) |
| 639 | 639 | ||
| 640 | 640 | ||
| 641 | #TestDeviceMeshGetItem with resetting world_size to 8. | 641 | #TestDeviceMeshGetItem with resetting world_size to 8. |
| 642 | class TestDeviceMeshGetItemE(DTensorTestBase): | 642 | class TestDeviceMeshGetItemE(DTensorTestBase): |
| 643 | @property | 643 | @property |
| 644 | def world_size(self): | 644 | def world_size(self): |
| 645 | return 8 | 645 | return 8 |
| 646 | 646 | ||
| 647 | @skipIfUnsupportMultiNPU(8) | 647 | @skipIfUnsupportMultiNPU(8) |
| 648 | @with_comms | 648 | @with_comms |
| 649 | def test_get_item_2d(self): | 649 | def test_get_item_2d(self): |
| 650 | mesh_shape = (2, 4) | 650 | mesh_shape = (2, 4) |
| 651 | mesh_dim_names = ("DP", "TP") | 651 | mesh_dim_names = ("DP", "TP") |
| 652 | mesh_2d = init_device_mesh( | 652 | mesh_2d = init_device_mesh( |
| 653 | self.device_type, mesh_shape, mesh_dim_names=mesh_dim_names | 653 | self.device_type, mesh_shape, mesh_dim_names=mesh_dim_names |
| 654 | ) | 654 | ) |
| 655 | 655 | ||
| 656 | pg_ranks_by_dim_name = {} | 656 | pg_ranks_by_dim_name = {} |
| 657 | for mesh_dim_name in mesh_dim_names: | 657 | for mesh_dim_name in mesh_dim_names: |
| 658 | mesh_dim = mesh_dim_names.index(mesh_dim_name) | 658 | mesh_dim = mesh_dim_names.index(mesh_dim_name) |
| 659 | pg_ranks_by_dim_name[mesh_dim_name] = mesh_2d.mesh.swapdims( | 659 | pg_ranks_by_dim_name[mesh_dim_name] = mesh_2d.mesh.swapdims( |
| 660 | -1, mesh_dim | 660 | -1, mesh_dim |
| 661 | ).reshape(-1, mesh_2d.mesh.size(mesh_dim)) | 661 | ).reshape(-1, mesh_2d.mesh.size(mesh_dim)) |
| 662 | 662 | ||
| 663 | tp_mesh = mesh_2d["TP"] | 663 | tp_mesh = mesh_2d["TP"] |
| 664 | tp_group_idx = self.rank // 4 | 664 | tp_group_idx = self.rank // 4 |
| 665 | self.assertEqual(tp_mesh.mesh, pg_ranks_by_dim_name["TP"][tp_group_idx]) | 665 | self.assertEqual(tp_mesh.mesh, pg_ranks_by_dim_name["TP"][tp_group_idx]) |
| 666 | 666 | ||
| 667 | dp_mesh = mesh_2d["DP"] | 667 | dp_mesh = mesh_2d["DP"] |
| 668 | dp_group_idx = self.rank % 4 | 668 | dp_group_idx = self.rank % 4 |
| 669 | self.assertEqual(mesh_2d["DP"].mesh, pg_ranks_by_dim_name["DP"][dp_group_idx]) | 669 | self.assertEqual(mesh_2d["DP"].mesh, pg_ranks_by_dim_name["DP"][dp_group_idx]) |
| 670 | 670 | ||
| 671 | @skipIfUnsupportMultiNPU(8) | 671 | @skipIfUnsupportMultiNPU(8) |
| 672 | @with_comms | 672 | @with_comms |
| 673 | def test_get_item_3d(self): | 673 | def test_get_item_3d(self): |
| 674 | mesh_shape = (2, 2, 2) | 674 | mesh_shape = (2, 2, 2) |
| 675 | mesh_dim_names = ("Replicate", "Shard", "TP") | 675 | mesh_dim_names = ("Replicate", "Shard", "TP") |
| 676 | mesh_3d = init_device_mesh( | 676 | mesh_3d = init_device_mesh( |
| 677 | self.device_type, mesh_shape, mesh_dim_names=mesh_dim_names | 677 | self.device_type, mesh_shape, mesh_dim_names=mesh_dim_names |
| 678 | ) | 678 | ) |
| 679 | 679 | ||
| 680 | tp_group = [[0, 1], [2, 3], [4, 5], [6, 7]] | 680 | tp_group = [[0, 1], [2, 3], [4, 5], [6, 7]] |
| 681 | tp_group_idx = int(self.rank / 2) | 681 | tp_group_idx = int(self.rank / 2) |
| 682 | self.assertEqual(mesh_3d["TP"].mesh.tolist(), tp_group[tp_group_idx]) | 682 | self.assertEqual(mesh_3d["TP"].mesh.tolist(), tp_group[tp_group_idx]) |
| 683 | 683 | ||
| 684 | shard_group = [[0, 2], [1, 3], [4, 6], [5, 7]] | 684 | shard_group = [[0, 2], [1, 3], [4, 6], [5, 7]] |
| 685 | shard_group_idx = self.rank % 2 + self.rank // 4 * 2 | 685 | shard_group_idx = self.rank % 2 + self.rank // 4 * 2 |
| 686 | self.assertEqual(mesh_3d["Shard"].mesh.tolist(), shard_group[shard_group_idx]) | 686 | self.assertEqual(mesh_3d["Shard"].mesh.tolist(), shard_group[shard_group_idx]) |
| 687 | 687 | ||
| 688 | replicate_group = [[0, 4], [1, 5], [2, 6], [3, 7]] | 688 | replicate_group = [[0, 4], [1, 5], [2, 6], [3, 7]] |
| 689 | replicate_group_idx = self.rank % 4 | 689 | replicate_group_idx = self.rank % 4 |
| 690 | self.assertEqual( | 690 | self.assertEqual( |
| 691 | mesh_3d["Replicate"].mesh.tolist(), replicate_group[replicate_group_idx] | 691 | mesh_3d["Replicate"].mesh.tolist(), replicate_group[replicate_group_idx] |
| 692 | ) | 692 | ) |
| 693 | 693 | ||
| 694 | # We support both UX for nD slicing. | 694 | # We support both UX for nD slicing. |
| 695 | # E.g. mesh_3d[["Replicate", "Shard"]] or mesh_3d["Replicate", "Shard"]. | 695 | # E.g. mesh_3d[["Replicate", "Shard"]] or mesh_3d["Replicate", "Shard"]. |
| 696 | hsdp_mesh_1 = mesh_3d[["Replicate", "Shard"]] | 696 | hsdp_mesh_1 = mesh_3d[["Replicate", "Shard"]] |
| 697 | hsdp_mesh_2 = mesh_3d["Replicate", "Shard"] | 697 | hsdp_mesh_2 = mesh_3d["Replicate", "Shard"] |
| 698 | hsdp_group = [[[0, 2], [4, 6]], [[1, 3], [5, 7]]] | 698 | hsdp_group = [[[0, 2], [4, 6]], [[1, 3], [5, 7]]] |
| 699 | hsdp_group_idx = self.rank % 2 | 699 | hsdp_group_idx = self.rank % 2 |
| 700 | self.assertEqual(hsdp_mesh_1.mesh.tolist(), hsdp_group[hsdp_group_idx]) | 700 | self.assertEqual(hsdp_mesh_1.mesh.tolist(), hsdp_group[hsdp_group_idx]) |
| 701 | self.assertEqual(hsdp_mesh_2.mesh.tolist(), hsdp_group[hsdp_group_idx]) | 701 | self.assertEqual(hsdp_mesh_2.mesh.tolist(), hsdp_group[hsdp_group_idx]) |
| 702 | self.assertEqual(hsdp_mesh_1, hsdp_mesh_2) | 702 | self.assertEqual(hsdp_mesh_1, hsdp_mesh_2) |
| 703 | 703 | ||
| 704 | @skipIfUnsupportMultiNPU(8) | 704 | @skipIfUnsupportMultiNPU(8) |
| 705 | @with_comms | 705 | @with_comms |
| 706 | def test_get_item_3d_noncontiguous_slicing(self): | 706 | def test_get_item_3d_noncontiguous_slicing(self): |
| 707 | mesh_shape = (2, 2, 2) | 707 | mesh_shape = (2, 2, 2) |
| 708 | mesh_dim_names = ("dp", "pp", "cp") | 708 | mesh_dim_names = ("dp", "pp", "cp") |
| 709 | mesh_3d = init_device_mesh( | 709 | mesh_3d = init_device_mesh( |
| 710 | self.device_type, mesh_shape, mesh_dim_names=mesh_dim_names | 710 | self.device_type, mesh_shape, mesh_dim_names=mesh_dim_names |
| 711 | ) | 711 | ) |
| 712 | 712 | ||
| 713 | # Slice order simply decides which mesh_dim sits on which mesh_dim. | 713 | # Slice order simply decides which mesh_dim sits on which mesh_dim. |
| 714 | # For dp_cp_mesh, cp mesh is the innermost dimension. | 714 | # For dp_cp_mesh, cp mesh is the innermost dimension. |
| 715 | dp_cp_mesh = mesh_3d["dp", "cp"] | 715 | dp_cp_mesh = mesh_3d["dp", "cp"] |
| 716 | expected_mesh_tensor = ( | 716 | expected_mesh_tensor = ( |
| 717 | torch.tensor([[0, 1], [4, 5]], dtype=torch.int) | 717 | torch.tensor([[0, 1], [4, 5]], dtype=torch.int) |
| 718 | if self.rank in (0, 1, 4, 5) | 718 | if self.rank in (0, 1, 4, 5) |
| 719 | else torch.tensor([[2, 3], [6, 7]], dtype=torch.int) | 719 | else torch.tensor([[2, 3], [6, 7]], dtype=torch.int) |
| 720 | ) | 720 | ) |
| 721 | dp_local_rank = dp_cp_mesh.get_local_rank("dp") | 721 | dp_local_rank = dp_cp_mesh.get_local_rank("dp") |
| 722 | self.assertEqual(dp_cp_mesh.mesh, expected_mesh_tensor) | 722 | self.assertEqual(dp_cp_mesh.mesh, expected_mesh_tensor) |
| 723 | cp_mesh = mesh_3d["cp"] | 723 | cp_mesh = mesh_3d["cp"] |
| 724 | # Check on the current dp_local_rank, whether the cp mesh tensor is the same. | 724 | # Check on the current dp_local_rank, whether the cp mesh tensor is the same. |
| 725 | self.assertEqual(dp_cp_mesh.mesh[dp_local_rank], cp_mesh.mesh) | 725 | self.assertEqual(dp_cp_mesh.mesh[dp_local_rank], cp_mesh.mesh) |
| 726 | 726 | ||
| 727 | with self.assertRaisesRegex( | 727 | with self.assertRaisesRegex( |
| 728 | KeyError, | 728 | KeyError, |
| 729 | "Invalid mesh_dim_names", | 729 | "Invalid mesh_dim_names", |
| 730 | ): | 730 | ): |
| 731 | cp_dp_mesh = mesh_3d["cp", "dp"] | 731 | cp_dp_mesh = mesh_3d["cp", "dp"] |
| 732 | 732 | ||
| 733 | @skipIfUnsupportMultiNPU(8) | 733 | @skipIfUnsupportMultiNPU(8) |
| 734 | @with_comms | 734 | @with_comms |
| 735 | def test_reconstruct_mesh_with_flatten_dim(self): | 735 | def test_reconstruct_mesh_with_flatten_dim(self): |
| 736 | mesh_3d = init_device_mesh( | 736 | mesh_3d = init_device_mesh( |
| 737 | self.device_type, (2, 2, 2), mesh_dim_names=("replicate", "shard", "cp") | 737 | self.device_type, (2, 2, 2), mesh_dim_names=("replicate", "shard", "cp") |
| 738 | ) | 738 | ) |
| 739 | shard_cp_mesh = mesh_3d["shard", "cp"]._flatten() | 739 | shard_cp_mesh = mesh_3d["shard", "cp"]._flatten() |
| 740 | hsdp_mesh = mesh_3d["replicate", "shard_cp"] | 740 | hsdp_mesh = mesh_3d["replicate", "shard_cp"] |
| 741 | expected_mesh_tensor = torch.tensor( | 741 | expected_mesh_tensor = torch.tensor( |
| 742 | [[0, 1, 2, 3], [4, 5, 6, 7]], dtype=torch.int | 742 | [[0, 1, 2, 3], [4, 5, 6, 7]], dtype=torch.int |
| 743 | ) | 743 | ) |
| 744 | self.assertEqual(hsdp_mesh.mesh, expected_mesh_tensor) | 744 | self.assertEqual(hsdp_mesh.mesh, expected_mesh_tensor) |
| 745 | self.assertEqual(shard_cp_mesh.get_group(), mesh_3d["shard_cp"].get_group()) | 745 | self.assertEqual(shard_cp_mesh.get_group(), mesh_3d["shard_cp"].get_group()) |
| 746 | self.assertEqual( | 746 | self.assertEqual( |
| 747 | shard_cp_mesh.get_group(), mesh_3d.get_group(mesh_dim="shard_cp") | 747 | shard_cp_mesh.get_group(), mesh_3d.get_group(mesh_dim="shard_cp") |
| 748 | ) | 748 | ) |
| 749 | 749 | ||
| 750 | mesh_3d = init_device_mesh( | 750 | mesh_3d = init_device_mesh( |
| 751 | self.device_type, (2, 2, 2), mesh_dim_names=("dp", "cp", "tp") | 751 | self.device_type, (2, 2, 2), mesh_dim_names=("dp", "cp", "tp") |
| 752 | ) | 752 | ) |
| 753 | dp_cp_mesh = mesh_3d["dp", "cp"]._flatten() | 753 | dp_cp_mesh = mesh_3d["dp", "cp"]._flatten() |
| 754 | spmd_mesh = mesh_3d["dp_cp", "tp"] | 754 | spmd_mesh = mesh_3d["dp_cp", "tp"] |
| 755 | expected_mesh_tensor = torch.tensor( | 755 | expected_mesh_tensor = torch.tensor( |
| 756 | [[0, 1], [2, 3], [4, 5], [6, 7]], dtype=torch.int | 756 | [[0, 1], [2, 3], [4, 5], [6, 7]], dtype=torch.int |
| 757 | ) | 757 | ) |
| 758 | self.assertEqual(spmd_mesh.mesh, expected_mesh_tensor) | 758 | self.assertEqual(spmd_mesh.mesh, expected_mesh_tensor) |
| 759 | self.assertEqual(dp_cp_mesh.get_group(), mesh_3d["dp_cp"].get_group()) | 759 | self.assertEqual(dp_cp_mesh.get_group(), mesh_3d["dp_cp"].get_group()) |
| 760 | self.assertEqual(dp_cp_mesh.get_group(), mesh_3d.get_group(mesh_dim="dp_cp")) | 760 | self.assertEqual(dp_cp_mesh.get_group(), mesh_3d.get_group(mesh_dim="dp_cp")) |
| 761 | 761 | ||
| 762 | 762 | ||
| 763 | class TestMeshEnv(DTensorTestBase): | 763 | class TestMeshEnv(DTensorTestBase): |
| 764 | @property | 764 | @property |
| 765 | def world_size(self): | 765 | def world_size(self): |
| 766 | return 2 | 766 | return 2 |
| 767 | 767 | ||
| 768 | @skipIfUnsupportMultiNPU(2) | 768 | @skipIfUnsupportMultiNPU(2) |
| 769 | @with_comms | 769 | @with_comms |
| 770 | def test_get_root_mesh(self): | 770 | def test_get_root_mesh(self): |
| 771 | mesh_3d = init_device_mesh( | 771 | mesh_3d = init_device_mesh( |
| 772 | self.device_type, (2, 1, 1), mesh_dim_names=("dp", "cp", "tp") | 772 | self.device_type, (2, 1, 1), mesh_dim_names=("dp", "cp", "tp") |
| 773 | ) | 773 | ) |
| 774 | 774 | ||
| 775 | dp_cp_mesh = mesh_3d["dp", "cp"] | 775 | dp_cp_mesh = mesh_3d["dp", "cp"] |
| 776 | dp_tp_mesh = mesh_3d["dp", "tp"] | 776 | dp_tp_mesh = mesh_3d["dp", "tp"] |
| 777 | cp_tp_mesh = mesh_3d["cp", "tp"] | 777 | cp_tp_mesh = mesh_3d["cp", "tp"] |
| 778 | dp_mesh = mesh_3d["dp"] | 778 | dp_mesh = mesh_3d["dp"] |
| 779 | cp_mesh = mesh_3d["cp"] | 779 | cp_mesh = mesh_3d["cp"] |
| 780 | tp_mesh = mesh_3d["tp"] | 780 | tp_mesh = mesh_3d["tp"] |
| 781 | self.assertEqual(_mesh_resources.get_root_mesh(dp_cp_mesh), mesh_3d) | 781 | self.assertEqual(_mesh_resources.get_root_mesh(dp_cp_mesh), mesh_3d) |
| 782 | self.assertEqual(_mesh_resources.get_root_mesh(dp_tp_mesh), mesh_3d) | 782 | self.assertEqual(_mesh_resources.get_root_mesh(dp_tp_mesh), mesh_3d) |
| 783 | self.assertEqual(_mesh_resources.get_root_mesh(cp_tp_mesh), mesh_3d) | 783 | self.assertEqual(_mesh_resources.get_root_mesh(cp_tp_mesh), mesh_3d) |
| 784 | self.assertEqual(_mesh_resources.get_root_mesh(dp_mesh), mesh_3d) | 784 | self.assertEqual(_mesh_resources.get_root_mesh(dp_mesh), mesh_3d) |
| 785 | self.assertEqual(_mesh_resources.get_root_mesh(cp_mesh), mesh_3d) | 785 | self.assertEqual(_mesh_resources.get_root_mesh(cp_mesh), mesh_3d) |
| 786 | self.assertEqual(_mesh_resources.get_root_mesh(tp_mesh), mesh_3d) | 786 | self.assertEqual(_mesh_resources.get_root_mesh(tp_mesh), mesh_3d) |
| 787 | 787 | ||
| 788 | @skipIfUnsupportMultiNPU(2) | 788 | @skipIfUnsupportMultiNPU(2) |
| 789 | @with_comms | 789 | @with_comms |
| 790 | def test_get_root_mesh_dim_exist(self): | 790 | def test_get_root_mesh_dim_exist(self): |
| 791 | mesh_shape = (2, self.world_size // 2) | 791 | mesh_shape = (2, self.world_size // 2) |
| 792 | mesh_dim_names = ("DP", "TP") | 792 | mesh_dim_names = ("DP", "TP") |
| 793 | mesh_2d = init_device_mesh( | 793 | mesh_2d = init_device_mesh( |
| 794 | self.device_type, mesh_shape, mesh_dim_names=mesh_dim_names | 794 | self.device_type, mesh_shape, mesh_dim_names=mesh_dim_names |
| 795 | ) | 795 | ) |
| 796 | 796 | ||
| 797 | self.assertEqual(_mesh_resources.get_root_mesh_dim(mesh_2d["DP"]), 0) | 797 | self.assertEqual(_mesh_resources.get_root_mesh_dim(mesh_2d["DP"]), 0) |
| 798 | self.assertEqual(_mesh_resources.get_root_mesh_dim(mesh_2d["TP"]), 1) | 798 | self.assertEqual(_mesh_resources.get_root_mesh_dim(mesh_2d["TP"]), 1) |
| 799 | 799 | ||
| 800 | @skipIfUnsupportMultiNPU(2) | 800 | @skipIfUnsupportMultiNPU(2) |
| 801 | @with_comms | 801 | @with_comms |
| 802 | def test_get_root_mesh_dim_not_exist(self): | 802 | def test_get_root_mesh_dim_not_exist(self): |
| 803 | mesh_shape = (self.world_size,) | 803 | mesh_shape = (self.world_size,) |
| 804 | mesh = init_device_mesh(self.device_type, mesh_shape) | 804 | mesh = init_device_mesh(self.device_type, mesh_shape) |
| 805 | 805 | ||
| 806 | self.assertEqual(_mesh_resources.get_root_mesh_dim(mesh), None) | 806 | self.assertEqual(_mesh_resources.get_root_mesh_dim(mesh), None) |
| 807 | 807 | ||
| 808 | @skipIfUnsupportMultiNPU(2) | 808 | @skipIfUnsupportMultiNPU(2) |
| 809 | @with_comms | 809 | @with_comms |
| 810 | def test_get_mesh_dim_by_name(self): | 810 | def test_get_mesh_dim_by_name(self): |
| 811 | mesh_shape = (2, self.world_size // 2) | 811 | mesh_shape = (2, self.world_size // 2) |
| 812 | mesh_dim_names = ("DP", "TP") | 812 | mesh_dim_names = ("DP", "TP") |
| 813 | mesh_2d = init_device_mesh( | 813 | mesh_2d = init_device_mesh( |
| 814 | self.device_type, mesh_shape, mesh_dim_names=mesh_dim_names | 814 | self.device_type, mesh_shape, mesh_dim_names=mesh_dim_names |
| 815 | ) | 815 | ) |
| 816 | 816 | ||
| 817 | self.assertEqual(_mesh_resources.get_mesh_dim_by_name(mesh_2d, "DP"), 0) | 817 | self.assertEqual(_mesh_resources.get_mesh_dim_by_name(mesh_2d, "DP"), 0) |
| 818 | self.assertEqual(_mesh_resources.get_mesh_dim_by_name(mesh_2d, "TP"), 1) | 818 | self.assertEqual(_mesh_resources.get_mesh_dim_by_name(mesh_2d, "TP"), 1) |
| 819 | 819 | ||
| 820 | @skipIfUnsupportMultiNPU(2) | 820 | @skipIfUnsupportMultiNPU(2) |
| 821 | @with_comms | 821 | @with_comms |
| 822 | def test_get_all_submeshes(self): | 822 | def test_get_all_submeshes(self): |
| 823 | mesh_2d = init_device_mesh( | 823 | mesh_2d = init_device_mesh( |
| 824 | self.device_type, (1, 2), mesh_dim_names=("replicate", "shard") | 824 | self.device_type, (1, 2), mesh_dim_names=("replicate", "shard") |
| 825 | ) | 825 | ) |
| 826 | all_submeshes = _mesh_resources._get_all_submeshes(mesh_2d, "replicate") | 826 | all_submeshes = _mesh_resources._get_all_submeshes(mesh_2d, "replicate") |
| 827 | self.assertEqual(len(all_submeshes), 2) | 827 | self.assertEqual(len(all_submeshes), 2) |
| 828 | self.assertEqual( | 828 | self.assertEqual( |
| 829 | all(submesh.mesh.numel() == 1 for submesh in all_submeshes), True | 829 | all(submesh.mesh.numel() == 1 for submesh in all_submeshes), True |
| 830 | ) | 830 | ) |
| 831 | 831 | ||
| 832 | 832 | ||
| 833 | class DeviceMeshCollectiveTest(DTensorTestBase): | 833 | class DeviceMeshCollectiveTest(DTensorTestBase): |
| 834 | @property | 834 | @property |
| 835 | def world_size(self): | 835 | def world_size(self): |
| 836 | return 2 | 836 | return 2 |
| 837 | 837 | ||
| 838 | @skipIfUnsupportMultiNPU(2) | 838 | @skipIfUnsupportMultiNPU(2) |
| 839 | @with_comms | 839 | @with_comms |
| 840 | def test_broadcast_1d(self): | 840 | def test_broadcast_1d(self): |
| 841 | mesh = DeviceMesh(self.device_type, torch.arange(self.world_size)) | 841 | mesh = DeviceMesh(self.device_type, torch.arange(self.world_size)) |
| 842 | local_tensor = torch.ones(3, 3, device=self.device_type) * self.rank | 842 | local_tensor = torch.ones(3, 3, device=self.device_type) * self.rank |
| 843 | mesh_broadcast(local_tensor, mesh, mesh_dim=0) | 843 | mesh_broadcast(local_tensor, mesh, mesh_dim=0) |
| 844 | self.assertEqual(local_tensor, torch.zeros(3, 3)) | 844 | self.assertEqual(local_tensor, torch.zeros(3, 3)) |
| 845 | 845 | ||
| 846 | @skipIfUnsupportMultiNPU(2) | 846 | @skipIfUnsupportMultiNPU(2) |
| 847 | @with_comms | 847 | @with_comms |
| 848 | def test_scatter_1d(self): | 848 | def test_scatter_1d(self): |
| 849 | mesh = DeviceMesh(self.device_type, torch.arange(self.world_size)) | 849 | mesh = DeviceMesh(self.device_type, torch.arange(self.world_size)) |
| 850 | scatter_tensor_shape = [3, 3, 3] | 850 | scatter_tensor_shape = [3, 3, 3] |
| 851 | len_scatter_tensor_shape = len(scatter_tensor_shape) | 851 | len_scatter_tensor_shape = len(scatter_tensor_shape) |
| 852 | for scatter_dim in range(len_scatter_tensor_shape): | 852 | for scatter_dim in range(len_scatter_tensor_shape): |
| 853 | shard_placement = Shard(scatter_dim) | 853 | shard_placement = Shard(scatter_dim) |
| 854 | scatter_tensor_shape[scatter_dim] *= self.world_size | 854 | scatter_tensor_shape[scatter_dim] *= self.world_size |
| 855 | # make the random seed same across rank | 855 | # make the random seed same across rank |
| 856 | torch.manual_seed(0) | 856 | torch.manual_seed(0) |
| 857 | global_tensor = torch.randn(scatter_tensor_shape, device=self.device_type) | 857 | global_tensor = torch.randn(scatter_tensor_shape, device=self.device_type) |
| 858 | splitted_list, _ = shard_placement._split_tensor( | 858 | splitted_list, _ = shard_placement._split_tensor( |
| 859 | global_tensor, mesh.size(), with_padding=True, contiguous=True | 859 | global_tensor, mesh.size(), with_padding=True, contiguous=True |
| 860 | ) | 860 | ) |
| 861 | recv_tensor = torch.empty_like(splitted_list[mesh.get_rank()]) | 861 | recv_tensor = torch.empty_like(splitted_list[mesh.get_rank()]) |
| 862 | # scatter on dim > 0 would generate non-contiguous tensor, verify that works | 862 | # scatter on dim > 0 would generate non-contiguous tensor, verify that works |
| 863 | mesh_scatter(recv_tensor, splitted_list, mesh, mesh_dim=0) | 863 | mesh_scatter(recv_tensor, splitted_list, mesh, mesh_dim=0) |
| 864 | self.assertEqual(recv_tensor, splitted_list[mesh.get_rank()]) | 864 | self.assertEqual(recv_tensor, splitted_list[mesh.get_rank()]) |
| 865 | 865 | ||
| 866 | @skipIfUnsupportMultiNPU(2) | 866 | @skipIfUnsupportMultiNPU(2) |
| 867 | @with_comms | 867 | @with_comms |
| 868 | def test_scatter_uneven(self): | 868 | def test_scatter_uneven(self): |
| 869 | device_mesh = DeviceMesh(self.device_type, list(range(self.world_size))) | 869 | device_mesh = DeviceMesh(self.device_type, list(range(self.world_size))) |
| 870 | my_rank = device_mesh.get_rank() | 870 | my_rank = device_mesh.get_rank() |
| 871 | tensor_to_split = torch.randn( | 871 | tensor_to_split = torch.randn( |
| 872 | device_mesh.size() + 3, device_mesh.size() + 1, device=self.device_type | 872 | device_mesh.size() + 3, device_mesh.size() + 1, device=self.device_type |
| 873 | ) | 873 | ) |
| 874 | 874 | ||
| 875 | for shard_dim in range(tensor_to_split.ndim): | 875 | for shard_dim in range(tensor_to_split.ndim): |
| 876 | shard_placement = Shard(shard_dim) | 876 | shard_placement = Shard(shard_dim) |
| 877 | 877 | ||
| 878 | tensor_to_scatter = tensor_to_split.clone() | 878 | tensor_to_scatter = tensor_to_split.clone() |
| 879 | tensor_splitted_list = list( | 879 | tensor_splitted_list = list( |
| 880 | torch.chunk(tensor_to_split, self.world_size, dim=shard_dim) | 880 | torch.chunk(tensor_to_split, self.world_size, dim=shard_dim) |
| 881 | ) | 881 | ) |
| 882 | for _ in range(self.world_size - len(tensor_splitted_list)): | 882 | for _ in range(self.world_size - len(tensor_splitted_list)): |
| 883 | tensor_splitted_list.append(torch.tensor([], device=self.device_type)) | 883 | tensor_splitted_list.append(torch.tensor([], device=self.device_type)) |
| 884 | 884 | ||
| 885 | padded_tensor_list, pad_sizes = shard_placement._split_tensor( | 885 | padded_tensor_list, pad_sizes = shard_placement._split_tensor( |
| 886 | tensor_to_scatter, | 886 | tensor_to_scatter, |
| 887 | device_mesh.size(), | 887 | device_mesh.size(), |
| 888 | with_padding=True, | 888 | with_padding=True, |
| 889 | contiguous=True, | 889 | contiguous=True, |
| 890 | ) | 890 | ) |
| 891 | 891 | ||
| 892 | scattered_tensor = torch.empty_like(padded_tensor_list[my_rank]) | 892 | scattered_tensor = torch.empty_like(padded_tensor_list[my_rank]) |
| 893 | mesh_scatter(scattered_tensor, padded_tensor_list, device_mesh, mesh_dim=0) | 893 | mesh_scatter(scattered_tensor, padded_tensor_list, device_mesh, mesh_dim=0) |
| 894 | 894 | ||
| 895 | if pad_sizes[my_rank] != 0: | 895 | if pad_sizes[my_rank] != 0: |
| 896 | scattered_tensor = unpad_tensor( | 896 | scattered_tensor = unpad_tensor( |
| 897 | scattered_tensor, shard_dim, pad_sizes[my_rank] | 897 | scattered_tensor, shard_dim, pad_sizes[my_rank] |
| 898 | ) | 898 | ) |
| 899 | 899 | ||
| 900 | if scattered_tensor.numel() == 0: | 900 | if scattered_tensor.numel() == 0: |
| 901 | # We need to check numel() instead of size if a tensor is ([]) after unpadding, | 901 | # We need to check numel() instead of size if a tensor is ([]) after unpadding, |
| 902 | # since the size could be ([0, 8]) after unpadding. | 902 | # since the size could be ([0, 8]) after unpadding. |
| 903 | self.assertEqual( | 903 | self.assertEqual( |
| 904 | scattered_tensor.numel(), tensor_splitted_list[my_rank].numel() | 904 | scattered_tensor.numel(), tensor_splitted_list[my_rank].numel() |
| 905 | ) | 905 | ) |
| 906 | else: | 906 | else: |
| 907 | self.assertEqual( | 907 | self.assertEqual( |
| 908 | scattered_tensor.size(), tensor_splitted_list[my_rank].size() | 908 | scattered_tensor.size(), tensor_splitted_list[my_rank].size() |
| 909 | ) | 909 | ) |
| 910 | self.assertEqual(scattered_tensor, tensor_splitted_list[my_rank]) | 910 | self.assertEqual(scattered_tensor, tensor_splitted_list[my_rank]) |
| 911 | 911 | ||
| 912 | @skipIfUnsupportMultiNPU(2) | 912 | @skipIfUnsupportMultiNPU(2) |
| 913 | @with_comms | 913 | @with_comms |
| 914 | def test_all_gather_uneven(self): | 914 | def test_all_gather_uneven(self): |
| 915 | device_mesh = DeviceMesh(self.device_type, list(range(self.world_size))) | 915 | device_mesh = DeviceMesh(self.device_type, list(range(self.world_size))) |
| 916 | my_rank = device_mesh.get_rank() | 916 | my_rank = device_mesh.get_rank() |
| 917 | tensor_to_split = torch.ones( | 917 | tensor_to_split = torch.ones( |
| 918 | device_mesh.size() + 3, | 918 | device_mesh.size() + 3, |
| 919 | device_mesh.size() + 1, | 919 | device_mesh.size() + 1, |
| 920 | device=self.device_type, | 920 | device=self.device_type, |
| 921 | ) | 921 | ) |
| 922 | 922 | ||
| 923 | for shard_dim in range(tensor_to_split.ndim): | 923 | for shard_dim in range(tensor_to_split.ndim): |
| 924 | shard_placement = Shard(shard_dim) | 924 | shard_placement = Shard(shard_dim) |
| 925 | tensor_padded_list, pad_sizes = shard_placement._split_tensor( | 925 | tensor_padded_list, pad_sizes = shard_placement._split_tensor( |
| 926 | tensor_to_split, | 926 | tensor_to_split, |
| 927 | device_mesh.size(), | 927 | device_mesh.size(), |
| 928 | with_padding=True, | 928 | with_padding=True, |
| 929 | contiguous=True, | 929 | contiguous=True, |
| 930 | ) | 930 | ) |
| 931 | local_tensor = tensor_padded_list[my_rank] | 931 | local_tensor = tensor_padded_list[my_rank] |
| 932 | big_tensor = funcol.all_gather_tensor( | 932 | big_tensor = funcol.all_gather_tensor( |
| 933 | local_tensor, gather_dim=shard_dim, group=(device_mesh, 0) | 933 | local_tensor, gather_dim=shard_dim, group=(device_mesh, 0) |
| 934 | ) | 934 | ) |
| 935 | big_tensor_chunks = list( | 935 | big_tensor_chunks = list( |
| 936 | torch.chunk(big_tensor, device_mesh.size(), dim=shard_dim) | 936 | torch.chunk(big_tensor, device_mesh.size(), dim=shard_dim) |
| 937 | ) | 937 | ) |
| 938 | unpadded_list = [ | 938 | unpadded_list = [ |
| 939 | ( | 939 | ( |
| 940 | unpad_tensor(big_tensor, shard_dim, pad_sizes[i]) | 940 | unpad_tensor(big_tensor, shard_dim, pad_sizes[i]) |
| 941 | if pad_sizes[i] > 0 | 941 | if pad_sizes[i] > 0 |
| 942 | else big_tensor | 942 | else big_tensor |
| 943 | ) | 943 | ) |
| 944 | for i, big_tensor in enumerate(big_tensor_chunks) | 944 | for i, big_tensor in enumerate(big_tensor_chunks) |
| 945 | ] | 945 | ] |
| 946 | all_gathered_tensor = torch.cat(unpadded_list, dim=shard_dim) | 946 | all_gathered_tensor = torch.cat(unpadded_list, dim=shard_dim) |
| 947 | 947 | ||
| 948 | self.assertEqual(all_gathered_tensor.size(), tensor_to_split.size()) | 948 | self.assertEqual(all_gathered_tensor.size(), tensor_to_split.size()) |
| 949 | self.assertEqual(all_gathered_tensor, tensor_to_split) | 949 | self.assertEqual(all_gathered_tensor, tensor_to_split) |
| 950 | 950 | ||
| 951 | @skipIfUnsupportMultiNPU(2) | 951 | @skipIfUnsupportMultiNPU(2) |
| 952 | @with_comms | 952 | @with_comms |
| 953 | def test_reduce_scatter_contiguous(self): | 953 | def test_reduce_scatter_contiguous(self): |
| 954 | device_mesh = DeviceMesh(self.device_type, list(range(self.world_size))) | 954 | device_mesh = DeviceMesh(self.device_type, list(range(self.world_size))) |
| 955 | my_rank = device_mesh.get_rank() | 955 | my_rank = device_mesh.get_rank() |
| 956 | 956 | ||
| 957 | # Init the tensor | 957 | # Init the tensor |
| 958 | step = self.world_size * 2 | 958 | step = self.world_size * 2 |
| 959 | total_elem = step**2 | 959 | total_elem = step**2 |
| 960 | tensor = torch.arange(0, total_elem).view(step, -1).to(device=self.device_type) | 960 | tensor = torch.arange(0, total_elem).view(step, -1).to(device=self.device_type) |
| 961 | tensor = tensor * (my_rank + 1) | 961 | tensor = tensor * (my_rank + 1) |
| 962 | 962 | ||
| 963 | # Get non-contiguous tensor by slicing | 963 | # Get non-contiguous tensor by slicing |
| 964 | tensor_to_reduce = tensor[::2, :2] | 964 | tensor_to_reduce = tensor[::2, :2] |
| 965 | tensor_contiguous = tensor_to_reduce.clone().contiguous() | 965 | tensor_contiguous = tensor_to_reduce.clone().contiguous() |
| 966 | 966 | ||
| 967 | # Partial to Shard to trigger reduce_scatter | 967 | # Partial to Shard to trigger reduce_scatter |
| 968 | tensor_to_reduce = DTensor.from_local( | 968 | tensor_to_reduce = DTensor.from_local( |
| 969 | tensor_to_reduce, device_mesh, [_Partial()] | 969 | tensor_to_reduce, device_mesh, [_Partial()] |
| 970 | ) | 970 | ) |
| 971 | tensor_contiguous = DTensor.from_local( | 971 | tensor_contiguous = DTensor.from_local( |
| 972 | tensor_contiguous, device_mesh, [_Partial()] | 972 | tensor_contiguous, device_mesh, [_Partial()] |
| 973 | ) | 973 | ) |
| 974 | new_tensor = tensor_to_reduce.redistribute(device_mesh, [Shard(0)]) | 974 | new_tensor = tensor_to_reduce.redistribute(device_mesh, [Shard(0)]) |
| 975 | new_tensor_contiguous = tensor_contiguous.redistribute(device_mesh, [Shard(0)]) | 975 | new_tensor_contiguous = tensor_contiguous.redistribute(device_mesh, [Shard(0)]) |
| 976 | 976 | ||
| 977 | # The output for contiguous and non-contiguous tensors of the same value | 977 | # The output for contiguous and non-contiguous tensors of the same value |
| 978 | # should return the same reducescatter value. | 978 | # should return the same reducescatter value. |
| 979 | new_tensor_local = new_tensor._local_tensor | 979 | new_tensor_local = new_tensor._local_tensor |
| 980 | new_tensor_contiguous_local = new_tensor_contiguous._local_tensor | 980 | new_tensor_contiguous_local = new_tensor_contiguous._local_tensor |
| 981 | self.assertEqual(new_tensor_local, new_tensor_contiguous_local) | 981 | self.assertEqual(new_tensor_local, new_tensor_contiguous_local) |
| 982 | self.assertEqual(list(new_tensor_local.size()), [1, 2]) | 982 | self.assertEqual(list(new_tensor_local.size()), [1, 2]) |
| 983 | 983 | ||
| 984 | # Check the reduce numerical value | 984 | # Check the reduce numerical value |
| 985 | sum_base = (1 + self.world_size) * self.world_size / 2 | 985 | sum_base = (1 + self.world_size) * self.world_size / 2 |
| 986 | first_elem = my_rank * sum_base * step * 2 | 986 | first_elem = my_rank * sum_base * step * 2 |
| 987 | expected_tensor = torch.tensor( | 987 | expected_tensor = torch.tensor( |
| 988 | [[first_elem, first_elem + sum_base]], | 988 | [[first_elem, first_elem + sum_base]], |
| 989 | dtype=new_tensor_local.dtype, | 989 | dtype=new_tensor_local.dtype, |
| 990 | device=self.device_type, | 990 | device=self.device_type, |
| 991 | ) | 991 | ) |
| 992 | self.assertEqual(new_tensor_local, expected_tensor) | 992 | self.assertEqual(new_tensor_local, expected_tensor) |
| 993 | 993 | ||
| 994 | @skipIfUnsupportMultiNPU(2) | 994 | @skipIfUnsupportMultiNPU(2) |
| 995 | @with_comms | 995 | @with_comms |
| 996 | def test_reduce_scatter_uneven(self): | 996 | def test_reduce_scatter_uneven(self): |
| 997 | device_mesh = DeviceMesh(self.device_type, list(range(self.world_size))) | 997 | device_mesh = DeviceMesh(self.device_type, list(range(self.world_size))) |
| 998 | my_rank = device_mesh.get_rank() | 998 | my_rank = device_mesh.get_rank() |
| 999 | tensor_to_split = ( | 999 | tensor_to_split = ( |
| 1000 | torch.ones( | 1000 | torch.ones( |
| 1001 | device_mesh.size() + 3, | 1001 | device_mesh.size() + 3, |
| 1002 | device_mesh.size() + 1, | 1002 | device_mesh.size() + 1, |
| 1003 | device=self.device_type, | 1003 | device=self.device_type, |
| 1004 | ) | 1004 | ) |
| 1005 | * self.rank | 1005 | * self.rank |
| 1006 | ) | 1006 | ) |
| 1007 | 1007 | ||
| 1008 | for shard_dim in range(tensor_to_split.ndim): | 1008 | for shard_dim in range(tensor_to_split.ndim): |
| 1009 | shard_placement = Shard(shard_dim) | 1009 | shard_placement = Shard(shard_dim) |
| 1010 | tensor_to_scatter = tensor_to_split.clone() | 1010 | tensor_to_scatter = tensor_to_split.clone() |
| 1011 | 1011 | ||
| 1012 | tensor_splitted_list = list( | 1012 | tensor_splitted_list = list( |
| 1013 | torch.chunk(tensor_to_split, self.world_size, dim=shard_dim) | 1013 | torch.chunk(tensor_to_split, self.world_size, dim=shard_dim) |
| 1014 | ) | 1014 | ) |
| 1015 | for _ in range(self.world_size - len(tensor_splitted_list)): | 1015 | for _ in range(self.world_size - len(tensor_splitted_list)): |
| 1016 | tensor_splitted_list.append(torch.tensor([], device=self.device_type)) | 1016 | tensor_splitted_list.append(torch.tensor([], device=self.device_type)) |
| 1017 | 1017 | ||
| 1018 | padded_tensor_list, pad_sizes = shard_placement._split_tensor( | 1018 | padded_tensor_list, pad_sizes = shard_placement._split_tensor( |
| 1019 | tensor_to_scatter, | 1019 | tensor_to_scatter, |
| 1020 | device_mesh.size(), | 1020 | device_mesh.size(), |
| 1021 | with_padding=True, | 1021 | with_padding=True, |
| 1022 | contiguous=True, | 1022 | contiguous=True, |
| 1023 | ) | 1023 | ) |
| 1024 | 1024 | ||
| 1025 | tensor_to_reduce = torch.cat(padded_tensor_list, shard_dim) | 1025 | tensor_to_reduce = torch.cat(padded_tensor_list, shard_dim) |
| 1026 | 1026 | ||
| 1027 | res_num = ((0 + self.world_size - 1) * self.world_size) / 2 | 1027 | res_num = ((0 + self.world_size - 1) * self.world_size) / 2 |
| 1028 | 1028 | ||
| 1029 | scattered_tensor = funcol.reduce_scatter_tensor( | 1029 | scattered_tensor = funcol.reduce_scatter_tensor( |
| 1030 | tensor_to_reduce, | 1030 | tensor_to_reduce, |
| 1031 | reduceOp="sum", | 1031 | reduceOp="sum", |
| 1032 | scatter_dim=shard_dim, | 1032 | scatter_dim=shard_dim, |
| 1033 | group=(device_mesh, 0), | 1033 | group=(device_mesh, 0), |
| 1034 | ) | 1034 | ) |
| 1035 | 1035 | ||
| 1036 | # unpad scattered_tensor | 1036 | # unpad scattered_tensor |
| 1037 | if pad_sizes[my_rank] > 0: | 1037 | if pad_sizes[my_rank] > 0: |
| 1038 | scattered_tensor = unpad_tensor( | 1038 | scattered_tensor = unpad_tensor( |
| 1039 | scattered_tensor, shard_dim, pad_sizes[my_rank] | 1039 | scattered_tensor, shard_dim, pad_sizes[my_rank] |
| 1040 | ) | 1040 | ) |
| 1041 | 1041 | ||
| 1042 | if scattered_tensor.numel() == 0: | 1042 | if scattered_tensor.numel() == 0: |
| 1043 | # We need to check numel() instead of size if a tensor is ([]) after unpadding, | 1043 | # We need to check numel() instead of size if a tensor is ([]) after unpadding, |
| 1044 | # since the size could be ([0, 8]) after unpadding. | 1044 | # since the size could be ([0, 8]) after unpadding. |
| 1045 | self.assertEqual( | 1045 | self.assertEqual( |
| 1046 | scattered_tensor.numel(), tensor_splitted_list[my_rank].numel() | 1046 | scattered_tensor.numel(), tensor_splitted_list[my_rank].numel() |
| 1047 | ) | 1047 | ) |
| 1048 | else: | 1048 | else: |
| 1049 | self.assertEqual( | 1049 | self.assertEqual( |
| 1050 | scattered_tensor.size(), tensor_splitted_list[my_rank].size() | 1050 | scattered_tensor.size(), tensor_splitted_list[my_rank].size() |
| 1051 | ) | 1051 | ) |
| 1052 | self.assertEqual( | 1052 | self.assertEqual( |
| 1053 | scattered_tensor, | 1053 | scattered_tensor, |
| 1054 | torch.ones_like(tensor_splitted_list[my_rank]) * res_num, | 1054 | torch.ones_like(tensor_splitted_list[my_rank]) * res_num, |
| 1055 | ) | 1055 | ) |
| 1056 | 1056 | ||
| 1057 | @skipIfUnsupportMultiNPU(2) | 1057 | @skipIfUnsupportMultiNPU(2) |
| 1058 | @with_comms | 1058 | @with_comms |
| 1059 | def test_broadcast_nd(self): | 1059 | def test_broadcast_nd(self): |
| 1060 | mesh_tensor = torch.arange(2).reshape(2, 1, 1) | 1060 | mesh_tensor = torch.arange(2).reshape(2, 1, 1) |
| 1061 | mesh = DeviceMesh(self.device_type, mesh_tensor) | 1061 | mesh = DeviceMesh(self.device_type, mesh_tensor) |
| 1062 | local_tensor = torch.ones(3, 3, device=self.device_type) * self.rank | 1062 | local_tensor = torch.ones(3, 3, device=self.device_type) * self.rank |
| 1063 | 1063 | ||
| 1064 | # check all dim groups | 1064 | # check all dim groups |
| 1065 | dim_to_subgroups = mesh.get_all_groups() | 1065 | dim_to_subgroups = mesh.get_all_groups() |
| 1066 | for dim, dim_group in enumerate(dim_to_subgroups): | 1066 | for dim, dim_group in enumerate(dim_to_subgroups): |
| 1067 | dim_group_size = get_world_size(dim_group) | 1067 | dim_group_size = get_world_size(dim_group) |
| 1068 | global_ranks = [ | 1068 | global_ranks = [ |
| 1069 | get_global_rank(dim_group, i) for i in range(dim_group_size) | 1069 | get_global_rank(dim_group, i) for i in range(dim_group_size) |
| 1070 | ] | 1070 | ] |
| 1071 | cloned_local_tensor = local_tensor.clone() | 1071 | cloned_local_tensor = local_tensor.clone() |
| 1072 | mesh_broadcast(cloned_local_tensor, mesh, mesh_dim=dim) | 1072 | mesh_broadcast(cloned_local_tensor, mesh, mesh_dim=dim) |
| 1073 | res_num = global_ranks[0] | 1073 | res_num = global_ranks[0] |
| 1074 | self.assertEqual(cloned_local_tensor, torch.ones(3, 3) * res_num) | 1074 | self.assertEqual(cloned_local_tensor, torch.ones(3, 3) * res_num) |
| 1075 | 1075 | ||
| 1076 | @skipIfUnsupportMultiNPU(2) | 1076 | @skipIfUnsupportMultiNPU(2) |
| 1077 | @with_comms | 1077 | @with_comms |
| 1078 | def test_scatter_nd(self): | 1078 | def test_scatter_nd(self): |
| 1079 | mesh_tensor = torch.arange(2).reshape(2, 1, 1) | 1079 | mesh_tensor = torch.arange(2).reshape(2, 1, 1) |
| 1080 | mesh = DeviceMesh(self.device_type, mesh_tensor) | 1080 | mesh = DeviceMesh(self.device_type, mesh_tensor) |
| 1081 | 1081 | ||
| 1082 | # check all dim groups | 1082 | # check all dim groups |
| 1083 | dim_to_subgroups = mesh.get_all_groups() | 1083 | dim_to_subgroups = mesh.get_all_groups() |
| 1084 | for dim, dim_group in enumerate(dim_to_subgroups): | 1084 | for dim, dim_group in enumerate(dim_to_subgroups): |
| 1085 | dim_group_size = get_world_size(dim_group) | 1085 | dim_group_size = get_world_size(dim_group) |
| 1086 | global_ranks = [ | 1086 | global_ranks = [ |
| 1087 | get_global_rank(dim_group, i) for i in range(dim_group_size) | 1087 | get_global_rank(dim_group, i) for i in range(dim_group_size) |
| 1088 | ] | 1088 | ] |
| 1089 | scattered_tensors = [ | 1089 | scattered_tensors = [ |
| 1090 | torch.ones(3, 3, device=self.device_type) * global_rank | 1090 | torch.ones(3, 3, device=self.device_type) * global_rank |
| 1091 | for global_rank in global_ranks | 1091 | for global_rank in global_ranks |
| 1092 | ] | 1092 | ] |
| 1093 | received_tensor = torch.empty_like( | 1093 | received_tensor = torch.empty_like( |
| 1094 | scattered_tensors[mesh.get_coordinate()[dim]] | 1094 | scattered_tensors[mesh.get_coordinate()[dim]] |
| 1095 | ) | 1095 | ) |
| 1096 | mesh_scatter(received_tensor, scattered_tensors, mesh, mesh_dim=dim) | 1096 | mesh_scatter(received_tensor, scattered_tensors, mesh, mesh_dim=dim) |
| 1097 | self.assertEqual(received_tensor, torch.ones(3, 3) * self.rank) | 1097 | self.assertEqual(received_tensor, torch.ones(3, 3) * self.rank) |
| 1098 | 1098 | ||
| 1099 | 1099 | ||
| 1100 | if __name__ == "__main__": | 1100 | if __name__ == "__main__": |
| 1101 | run_tests() | 1101 | run_tests() |
| @@ -1,27 +1,27 @@ | |||
| 1 | import os | 1 | import os |
| 2 | import time | 2 | import time |
| 3 | import datetime | 3 | import datetime |
| 4 | import torch.distributed as dist | 4 | import torch.distributed as dist |
| 5 | import torch | 5 | import torch |
| 6 | import torch_npu | 6 | import torch_npu |
| 7 | 7 | ||
| 8 | 8 | ||
| 9 | def main(): | 9 | def main(): |
| 10 | torch.npu.set_compile_mode(jit_compile=True) | 10 | torch.npu.set_compile_mode(jit_compile=True) |
| 11 | rank = int(os.environ['RANK']) | 11 | rank = int(os.environ['RANK']) |
| 12 | local_rank = int(os.environ['LOCAL_RANK']) | 12 | local_rank = int(os.environ['LOCAL_RANK']) |
| 13 | device = torch.device('npu:{}'.format(local_rank)) | 13 | device = torch.device('npu:{}'.format(local_rank)) |
| 14 | torch.npu.set_device(device) | 14 | torch.npu.set_device(device) |
| 15 | dist.init_process_group(backend='hccl', rank=rank, world_size=2, timeout=datetime.timedelta(seconds=120)) | 15 | dist.init_process_group(backend='hccl', rank=rank, world_size=2, timeout=datetime.timedelta(seconds=120)) |
| 16 | tensor = torch.tensor(1).npu() | 16 | tensor = torch.tensor(1).npu() |
| 17 | dist.all_reduce(tensor) | 17 | dist.all_reduce(tensor) |
| 18 | if rank == 0: | 18 | if rank == 0: |
| 19 | x1 = torch.randn(3).float().npu() | 19 | x1 = torch.randn(3).float().npu() |
| 20 | x2 = torch.randn(1).long().npu() | 20 | x2 = torch.randn(1).long().npu() |
| 21 | x3 = torch.randn(1).float().npu() | 21 | x3 = torch.randn(1).float().npu() |
| 22 | y = torch.addcmul(x1, x2, x3) | 22 | y = torch.addcmul(x1, x2, x3) |
| 23 | dist.all_reduce(tensor) | 23 | dist.all_reduce(tensor) |
| 24 | 24 | ||
| 25 | 25 | ||
| 26 | if __name__ == "__main__": | 26 | if __name__ == "__main__": |
| 27 | main() | 27 | main() |
| @@ -1,32 +1,32 @@ | |||
| 1 | # Owner(s): ["module: dynamo"] | 1 | # Owner(s): ["module: dynamo"] |
| 2 | import functools | 2 | import functools |
| 3 | import unittest | 3 | import unittest |
| 4 | import torch | 4 | import torch |
| 5 | import torch._dynamo.test_case | 5 | import torch._dynamo.test_case |
| 6 | import torch_npu | 6 | import torch_npu |
| 7 | 7 | ||
| 8 | requires_npu = functools.partial(unittest.skipIf, not torch.npu.is_available(), "requires npu") | 8 | requires_npu = functools.partial(unittest.skipIf, not torch.npu.is_available(), "requires npu") |
| 9 | 9 | ||
| 10 | 10 | ||
| 11 | class StreamintoDynamoTests(torch._dynamo.test_case.TestCase): | 11 | class StreamintoDynamoTests(torch._dynamo.test_case.TestCase): |
| 12 | 12 | ||
| 13 | @requires_npu() | 13 | @requires_npu() |
| 14 | def test_stream(self): | 14 | def test_stream(self): |
| 15 | def model_1(x): | 15 | def model_1(x): |
| 16 | a = x * x | 16 | a = x * x |
| 17 | s = torch.npu.Stream() | 17 | s = torch.npu.Stream() |
| 18 | s.wait_stream(torch.npu.current_stream()) | 18 | s.wait_stream(torch.npu.current_stream()) |
| 19 | with torch.npu.stream(s): | 19 | with torch.npu.stream(s): |
| 20 | b = x + a | 20 | b = x + a |
| 21 | return b | 21 | return b |
| 22 | inp = torch.randn(2, 8).npu() | 22 | inp = torch.randn(2, 8).npu() |
| 23 | m = torch.compile(model_1, backend="aot_eager", fullgraph=True) | 23 | m = torch.compile(model_1, backend="aot_eager", fullgraph=True) |
| 24 | output = m(inp) | 24 | output = m(inp) |
| 25 | output1 = model_1(inp) | 25 | output1 = model_1(inp) |
| 26 | torch.allclose(output, output1) | 26 | torch.allclose(output, output1) |
| 27 | 27 | ||
| 28 | 28 | ||
| 29 | if __name__ == "__main__": | 29 | if __name__ == "__main__": |
| 30 | from torch._dynamo.test_case import run_tests | 30 | from torch._dynamo.test_case import run_tests |
| 31 | 31 | ||
| 32 | run_tests() | 32 | run_tests() |
| @@ -44,4 +44,3 @@ if __name__ == "__main__": | |||
| 44 | args = parser.parse_args() | 44 | args = parser.parse_args() |
| 45 | failed_ut = get_error_or_fail_ut(args.file) | 45 | failed_ut = get_error_or_fail_ut(args.file) |
| 46 | write_to_json(ut_list=failed_ut) | 46 | write_to_json(ut_list=failed_ut) |
| 47 | |||
| @@ -1,36 +1,36 @@ | |||
| 1 | import torch | 1 | import torch |
| 2 | from torch.testing._internal.common_utils import TestCase, run_tests | 2 | from torch.testing._internal.common_utils import TestCase, run_tests |
| 3 | import torch_npu | 3 | import torch_npu |
| 4 | 4 | ||
| 5 | # 关闭NPU JIT编译,减少CI耗时 | 5 | # 关闭NPU JIT编译,减少CI耗时 |
| 6 | torch_npu.npu.set_compile_mode(jit_compile=False) | 6 | torch_npu.npu.set_compile_mode(jit_compile=False) |
| 7 | 7 | ||
| 8 | 8 | ||
| 9 | # 修复:将自定义属性设为类属性(确保实例化后必存在) | 9 | # 修复:将自定义属性设为类属性(确保实例化后必存在) |
| 10 | class CustomParameter(torch.nn.Parameter): | 10 | class CustomParameter(torch.nn.Parameter): |
| 11 | custom_attr = "custom_param" # 类属性,所有实例共享,无需__init__赋值 | 11 | custom_attr = "custom_param" # 类属性,所有实例共享,无需__init__赋值 |
| 12 | 12 | ||
| 13 | def __init__(self, data=None, requires_grad=True): | 13 | def __init__(self, data=None, requires_grad=True): |
| 14 | super().__init__(data, requires_grad) | 14 | super().__init__(data, requires_grad) |
| 15 | 15 | ||
| 16 | 16 | ||
| 17 | class TestUninitializedParameterClsToBecome(TestCase): | 17 | class TestUninitializedParameterClsToBecome(TestCase): |
| 18 | 18 | ||
| 19 | def test_core_functionality_npu(self): | 19 | def test_core_functionality_npu(self): |
| 20 | """极简验证NPU环境下cls_to_become+materialize核心功能""" | 20 | """极简验证NPU环境下cls_to_become+materialize核心功能""" |
| 21 | # 1. 创建NPU未初始化参数 | 21 | # 1. 创建NPU未初始化参数 |
| 22 | uninit_param = torch.nn.parameter.UninitializedParameter(device="npu") | 22 | uninit_param = torch.nn.parameter.UninitializedParameter(device="npu") |
| 23 | # 2. 绑定自定义类 | 23 | # 2. 绑定自定义类 |
| 24 | uninit_param.cls_to_become = CustomParameter | 24 | uninit_param.cls_to_become = CustomParameter |
| 25 | # 3. 实例化参数 | 25 | # 3. 实例化参数 |
| 26 | uninit_param.materialize(shape=(3, 3)) | 26 | uninit_param.materialize(shape=(3, 3)) |
| 27 | 27 | ||
| 28 | # 核心断言(全部通过,无报错) | 28 | # 核心断言(全部通过,无报错) |
| 29 | self.assertEqual(uninit_param.shape, torch.Size((3, 3))) | 29 | self.assertEqual(uninit_param.shape, torch.Size((3, 3))) |
| 30 | self.assertEqual(uninit_param.device.type, "npu") | 30 | self.assertEqual(uninit_param.device.type, "npu") |
| 31 | self.assertIsInstance(uninit_param, CustomParameter) | 31 | self.assertIsInstance(uninit_param, CustomParameter) |
| 32 | self.assertEqual(uninit_param.custom_attr, "custom_param") # 现在能正常访问 | 32 | self.assertEqual(uninit_param.custom_attr, "custom_param") # 现在能正常访问 |
| 33 | 33 | ||
| 34 | 34 | ||
| 35 | if __name__ == "__main__": | 35 | if __name__ == "__main__": |
| 36 | run_tests() | 36 | run_tests() |
| @@ -1,526 +1,526 @@ | |||
| 1 | import unittest | 1 | import unittest |
| 2 | from itertools import chain | 2 | from itertools import chain |
| 3 | 3 | ||
| 4 | import torch | 4 | import torch |
| 5 | 5 | ||
| 6 | import torch_npu | 6 | import torch_npu |
| 7 | from torch_npu.npu.amp import GradScaler, autocast | 7 | from torch_npu.npu.amp import GradScaler, autocast |
| 8 | from torch_npu.testing.common_utils import SupportedDevices | 8 | from torch_npu.testing.common_utils import SupportedDevices |
| 9 | from torch_npu.testing.testcase import TestCase, run_tests | 9 | from torch_npu.testing.testcase import TestCase, run_tests |
| 10 | 10 | ||
| 11 | 11 | ||
| 12 | def make_device_overflow_1(): | 12 | def make_device_overflow_1(): |
| 13 | float_tensor = torch.tensor([40000.0], dtype=torch.float16).npu() | 13 | float_tensor = torch.tensor([40000.0], dtype=torch.float16).npu() |
| 14 | float_tensor = float_tensor + float_tensor | 14 | float_tensor = float_tensor + float_tensor |
| 15 | 15 | ||
| 16 | 16 | ||
| 17 | def make_device_overflow_2(model): | 17 | def make_device_overflow_2(model): |
| 18 | for param in model.parameters(): | 18 | for param in model.parameters(): |
| 19 | if param.grad is not None: | 19 | if param.grad is not None: |
| 20 | param.grad = torch.full_like(param.grad, float("inf")) | 20 | param.grad = torch.full_like(param.grad, float("inf")) |
| 21 | break | 21 | break |
| 22 | 22 | ||
| 23 | 23 | ||
| 24 | class TestAmp(TestCase): | 24 | class TestAmp(TestCase): |
| 25 | 25 | ||
| 26 | def test_grad_scaling_scale(self): | 26 | def test_grad_scaling_scale(self): |
| 27 | scaler = GradScaler(init_scale=2.) | 27 | scaler = GradScaler(init_scale=2.) |
| 28 | t0 = torch.full((1,), 4.0, dtype=torch.float32, device="npu") | 28 | t0 = torch.full((1,), 4.0, dtype=torch.float32, device="npu") |
| 29 | t1 = torch.full((1,), 4.0, dtype=torch.float32, device="npu") | 29 | t1 = torch.full((1,), 4.0, dtype=torch.float32, device="npu") |
| 30 | # Create some nested iterables of tensors on different devices. | 30 | # Create some nested iterables of tensors on different devices. |
| 31 | outputs = (t1.clone(), (t0.clone(), t1.clone()), [t0.clone(), (t1.clone(), t0.clone())]) | 31 | outputs = (t1.clone(), (t0.clone(), t1.clone()), [t0.clone(), (t1.clone(), t0.clone())]) |
| 32 | outputs = scaler.scale(outputs) | 32 | outputs = scaler.scale(outputs) |
| 33 | self.assertTrue(outputs[0] == 8.0 and outputs[1][0] == 8.0 and outputs[1][1] == 8.0 and | 33 | self.assertTrue(outputs[0] == 8.0 and outputs[1][0] == 8.0 and outputs[1][1] == 8.0 and |
| 34 | outputs[2][0] == 8.0 and outputs[2][1][0] == 8.0 and outputs[2][1][1] == 8.0) | 34 | outputs[2][0] == 8.0 and outputs[2][1][0] == 8.0 and outputs[2][1][1] == 8.0) |
| 35 | self.assertTrue(scaler._scale.device == t1.device) | 35 | self.assertTrue(scaler._scale.device == t1.device) |
| 36 | 36 | ||
| 37 | def test_grad_scaling_state_dict(self): | 37 | def test_grad_scaling_state_dict(self): |
| 38 | for lazy_init_scale in True, False: | 38 | for lazy_init_scale in True, False: |
| 39 | s0 = GradScaler(init_scale=3., growth_factor=4., backoff_factor=.5, growth_interval=2) | 39 | s0 = GradScaler(init_scale=3., growth_factor=4., backoff_factor=.5, growth_interval=2) |
| 40 | s1 = GradScaler(init_scale=6., growth_factor=7., backoff_factor=.8, growth_interval=1) | 40 | s1 = GradScaler(init_scale=6., growth_factor=7., backoff_factor=.8, growth_interval=1) |
| 41 | 41 | ||
| 42 | # sets a random value for load_state_dict to overwrite | 42 | # sets a random value for load_state_dict to overwrite |
| 43 | s1._init_growth_tracker = 7 | 43 | s1._init_growth_tracker = 7 |
| 44 | 44 | ||
| 45 | if lazy_init_scale: | 45 | if lazy_init_scale: |
| 46 | # Dummy scale() call to ensure the scale tensor is lazily initialized. | 46 | # Dummy scale() call to ensure the scale tensor is lazily initialized. |
| 47 | s1.scale(torch.full((1,), 4.0, dtype=torch.float32, device="npu")) | 47 | s1.scale(torch.full((1,), 4.0, dtype=torch.float32, device="npu")) |
| 48 | self.assertTrue(isinstance(s1._scale, torch.npu.FloatTensor)) | 48 | self.assertTrue(isinstance(s1._scale, torch.npu.FloatTensor)) |
| 49 | 49 | ||
| 50 | s1.load_state_dict(s0.state_dict()) | 50 | s1.load_state_dict(s0.state_dict()) |
| 51 | 51 | ||
| 52 | self.assertTrue(s1.get_scale() == 3.) | 52 | self.assertTrue(s1.get_scale() == 3.) |
| 53 | self.assertTrue(s1.get_growth_factor() == 4.) | 53 | self.assertTrue(s1.get_growth_factor() == 4.) |
| 54 | self.assertTrue(s1.get_backoff_factor() == .5) | 54 | self.assertTrue(s1.get_backoff_factor() == .5) |
| 55 | self.assertTrue(s1.get_growth_interval() == 2) | 55 | self.assertTrue(s1.get_growth_interval() == 2) |
| 56 | self.assertTrue(s1._init_growth_tracker == 0) | 56 | self.assertTrue(s1._init_growth_tracker == 0) |
| 57 | 57 | ||
| 58 | def _create_scaling_models_optimizers(self, device="npu"): | 58 | def _create_scaling_models_optimizers(self, device="npu"): |
| 59 | # Create a module+optimizer that will use scaling, and a control module+optimizer | 59 | # Create a module+optimizer that will use scaling, and a control module+optimizer |
| 60 | # that will not use scaling, against which the scaling-enabled module+optimizer can be compared. | 60 | # that will not use scaling, against which the scaling-enabled module+optimizer can be compared. |
| 61 | mod_control = torch.nn.Sequential(torch.nn.Linear(8, 8), torch.nn.Linear(8, 8)).to(device=device) | 61 | mod_control = torch.nn.Sequential(torch.nn.Linear(8, 8), torch.nn.Linear(8, 8)).to(device=device) |
| 62 | mod_scaling = torch.nn.Sequential(torch.nn.Linear(8, 8), torch.nn.Linear(8, 8)).to(device=device) | 62 | mod_scaling = torch.nn.Sequential(torch.nn.Linear(8, 8), torch.nn.Linear(8, 8)).to(device=device) |
| 63 | for c, s in zip(mod_control.parameters(), mod_scaling.parameters()): | 63 | for c, s in zip(mod_control.parameters(), mod_scaling.parameters()): |
| 64 | s.data.copy_(c.data) | 64 | s.data.copy_(c.data) |
| 65 | 65 | ||
| 66 | opt_control = torch.optim.SGD(mod_control.parameters(), lr=1.0) | 66 | opt_control = torch.optim.SGD(mod_control.parameters(), lr=1.0) |
| 67 | opt_scaling = torch.optim.SGD(mod_scaling.parameters(), lr=1.0) | 67 | opt_scaling = torch.optim.SGD(mod_scaling.parameters(), lr=1.0) |
| 68 | 68 | ||
| 69 | ret = (mod_control, mod_scaling, opt_control, opt_scaling) | 69 | ret = (mod_control, mod_scaling, opt_control, opt_scaling) |
| 70 | return ret | 70 | return ret |
| 71 | 71 | ||
| 72 | def _create_scaling_case(self, device="npu", dtype=torch.float): | 72 | def _create_scaling_case(self, device="npu", dtype=torch.float): |
| 73 | data = [(torch.randn((8, 8), dtype=dtype, device=device), torch.randn((8, 8), dtype=dtype, device=device)), | 73 | data = [(torch.randn((8, 8), dtype=dtype, device=device), torch.randn((8, 8), dtype=dtype, device=device)), |
| 74 | (torch.randn((8, 8), dtype=dtype, device=device), torch.randn((8, 8), dtype=dtype, device=device)), | 74 | (torch.randn((8, 8), dtype=dtype, device=device), torch.randn((8, 8), dtype=dtype, device=device)), |
| 75 | (torch.randn((8, 8), dtype=dtype, device=device), torch.randn((8, 8), dtype=dtype, device=device)), | 75 | (torch.randn((8, 8), dtype=dtype, device=device), torch.randn((8, 8), dtype=dtype, device=device)), |
| 76 | (torch.randn((8, 8), dtype=dtype, device=device), torch.randn((8, 8), dtype=dtype, device=device))] | 76 | (torch.randn((8, 8), dtype=dtype, device=device), torch.randn((8, 8), dtype=dtype, device=device))] |
| 77 | 77 | ||
| 78 | loss_fn = torch.nn.MSELoss().npu() | 78 | loss_fn = torch.nn.MSELoss().npu() |
| 79 | 79 | ||
| 80 | skip_iter = 2 | 80 | skip_iter = 2 |
| 81 | 81 | ||
| 82 | return self._create_scaling_models_optimizers(device=device) + (data, loss_fn, skip_iter) | 82 | return self._create_scaling_models_optimizers(device=device) + (data, loss_fn, skip_iter) |
| 83 | 83 | ||
| 84 | # _run_scaling_case generalizes some single-optimizer test logic to avoid too much copy-pasting below. | 84 | # _run_scaling_case generalizes some single-optimizer test logic to avoid too much copy-pasting below. |
| 85 | def _run_scaling_case(self, run, unskipped, skipped, atol=1e-7): | 85 | def _run_scaling_case(self, run, unskipped, skipped, atol=1e-7): |
| 86 | # Ensure scaling can be disabled without changing user control flow. | 86 | # Ensure scaling can be disabled without changing user control flow. |
| 87 | for enabled in True, False: | 87 | for enabled in True, False: |
| 88 | mod_control, mod_scaling, opt_control, opt_scaling, data, loss_fn, skip_iter = self._create_scaling_case() | 88 | mod_control, mod_scaling, opt_control, opt_scaling, data, loss_fn, skip_iter = self._create_scaling_case() |
| 89 | 89 | ||
| 90 | # For functionality, test with a modest initial scale, and an unrealistically-large growth factor | 90 | # For functionality, test with a modest initial scale, and an unrealistically-large growth factor |
| 91 | # so any potential errors with the growth factor handling will be magnified. | 91 | # so any potential errors with the growth factor handling will be magnified. |
| 92 | scaler = GradScaler(init_scale=128., growth_factor=2.0, enabled=enabled, growth_interval=1) | 92 | scaler = GradScaler(init_scale=128., growth_factor=2.0, enabled=enabled, growth_interval=1) |
| 93 | 93 | ||
| 94 | _ = run(data, mod_control, opt_control, scaler, loss_fn, skip_iter, False) | 94 | _ = run(data, mod_control, opt_control, scaler, loss_fn, skip_iter, False) |
| 95 | ret = run(data, mod_scaling, opt_scaling, scaler, loss_fn, skip_iter, True) | 95 | ret = run(data, mod_scaling, opt_scaling, scaler, loss_fn, skip_iter, True) |
| 96 | 96 | ||
| 97 | # Allows run() to optionally return a different scaler instance. | 97 | # Allows run() to optionally return a different scaler instance. |
| 98 | scaler = ret if ret else scaler | 98 | scaler = ret if ret else scaler |
| 99 | 99 | ||
| 100 | # If scaling was enabled, the scale factor should have been multiplied by the growth factor | 100 | # If scaling was enabled, the scale factor should have been multiplied by the growth factor |
| 101 | # len(data) - skipped times and the backoff factor "skipped" times. | 101 | # len(data) - skipped times and the backoff factor "skipped" times. |
| 102 | if enabled: | 102 | if enabled: |
| 103 | net_growth = scaler.get_growth_factor()**unskipped if unskipped > 0 else 1.0 | 103 | net_growth = scaler.get_growth_factor()**unskipped if unskipped > 0 else 1.0 |
| 104 | net_backoff = scaler.get_backoff_factor()**skipped if skipped > 0 else 1.0 | 104 | net_backoff = scaler.get_backoff_factor()**skipped if skipped > 0 else 1.0 |
| 105 | self.assertTrue(scaler.get_scale() == (128. * net_growth * net_backoff)) | 105 | self.assertTrue(scaler.get_scale() == (128. * net_growth * net_backoff)) |
| 106 | else: | 106 | else: |
| 107 | self.assertTrue(scaler.get_scale() == 1.0) | 107 | self.assertTrue(scaler.get_scale() == 1.0) |
| 108 | 108 | ||
| 109 | for c, s in zip(mod_control.parameters(), mod_scaling.parameters()): | 109 | for c, s in zip(mod_control.parameters(), mod_scaling.parameters()): |
| 110 | c = c.cpu().to(torch.float).detach().numpy() | 110 | c = c.cpu().to(torch.float).detach().numpy() |
| 111 | s = s.cpu().to(torch.float).detach().numpy() | 111 | s = s.cpu().to(torch.float).detach().numpy() |
| 112 | self.assertRtolEqual(c, s, atol) | 112 | self.assertRtolEqual(c, s, atol) |
| 113 | 113 | ||
| 114 | # Compares no scaling + no autocasting against scaling + autocasting. | 114 | # Compares no scaling + no autocasting against scaling + autocasting. |
| 115 | @SupportedDevices(['Ascend910A', 'Ascend910P']) | 115 | @SupportedDevices(['Ascend910A', 'Ascend910P']) |
| 116 | def test_grad_scaling_autocast_1(self): | 116 | def test_grad_scaling_autocast_1(self): |
| 117 | def run(data, model, optimizer, scaler, loss_fn, skip_iter, try_scaling_api): | 117 | def run(data, model, optimizer, scaler, loss_fn, skip_iter, try_scaling_api): |
| 118 | for i, (input_data, target) in enumerate(data): | 118 | for i, (input_data, target) in enumerate(data): |
| 119 | optimizer.zero_grad() | 119 | optimizer.zero_grad() |
| 120 | with torch.autocast('npu', enabled=try_scaling_api): | 120 | with torch.autocast('npu', enabled=try_scaling_api): |
| 121 | output = model(input_data) | 121 | output = model(input_data) |
| 122 | loss = loss_fn(output, target) | 122 | loss = loss_fn(output, target) |
| 123 | if try_scaling_api: | 123 | if try_scaling_api: |
| 124 | scaler.scale(loss).backward() | 124 | scaler.scale(loss).backward() |
| 125 | if i == skip_iter and scaler.is_enabled(): | 125 | if i == skip_iter and scaler.is_enabled(): |
| 126 | make_device_overflow_1() | 126 | make_device_overflow_1() |
| 127 | scaler.step(optimizer) | 127 | scaler.step(optimizer) |
| 128 | scaler.update() | 128 | scaler.update() |
| 129 | else: | 129 | else: |
| 130 | loss.backward() | 130 | loss.backward() |
| 131 | if (not scaler.is_enabled()) or (i != skip_iter): | 131 | if (not scaler.is_enabled()) or (i != skip_iter): |
| 132 | optimizer.step() | 132 | optimizer.step() |
| 133 | return scaler | 133 | return scaler |
| 134 | 134 | ||
| 135 | # sets atol=1e-3 because we're comparing pure fp32 arithmetic vs a mixture of fp16 and fp32 | 135 | # sets atol=1e-3 because we're comparing pure fp32 arithmetic vs a mixture of fp16 and fp32 |
| 136 | self._run_scaling_case(run, unskipped=3, skipped=1, atol=1e-3) | 136 | self._run_scaling_case(run, unskipped=3, skipped=1, atol=1e-3) |
| 137 | 137 | ||
| 138 | @SupportedDevices(['Ascend910B']) | 138 | @SupportedDevices(['Ascend910B']) |
| 139 | def test_grad_scaling_autocast_2(self): | 139 | def test_grad_scaling_autocast_2(self): |
| 140 | def run(data, model, optimizer, scaler, loss_fn, skip_iter, try_scaling_api): | 140 | def run(data, model, optimizer, scaler, loss_fn, skip_iter, try_scaling_api): |
| 141 | for i, (input_data, target) in enumerate(data): | 141 | for i, (input_data, target) in enumerate(data): |
| 142 | optimizer.zero_grad() | 142 | optimizer.zero_grad() |
| 143 | with torch.autocast('npu', enabled=try_scaling_api): | 143 | with torch.autocast('npu', enabled=try_scaling_api): |
| 144 | output = model(input_data) | 144 | output = model(input_data) |
| 145 | loss = loss_fn(output, target) | 145 | loss = loss_fn(output, target) |
| 146 | if try_scaling_api: | 146 | if try_scaling_api: |
| 147 | scaler.scale(loss).backward() | 147 | scaler.scale(loss).backward() |
| 148 | if i == skip_iter and scaler.is_enabled(): | 148 | if i == skip_iter and scaler.is_enabled(): |
| 149 | make_device_overflow_2(model) | 149 | make_device_overflow_2(model) |
| 150 | scaler.step(optimizer) | 150 | scaler.step(optimizer) |
| 151 | scaler.update() | 151 | scaler.update() |
| 152 | else: | 152 | else: |
| 153 | loss.backward() | 153 | loss.backward() |
| 154 | if (not scaler.is_enabled()) or (i != skip_iter): | 154 | if (not scaler.is_enabled()) or (i != skip_iter): |
| 155 | optimizer.step() | 155 | optimizer.step() |
| 156 | return scaler | 156 | return scaler |
| 157 | 157 | ||
| 158 | # sets atol=1e-3 because we're comparing pure fp32 arithmetic vs a mixture of fp16 and fp32 | 158 | # sets atol=1e-3 because we're comparing pure fp32 arithmetic vs a mixture of fp16 and fp32 |
| 159 | self._run_scaling_case(run, unskipped=3, skipped=1, atol=1e-3) | 159 | self._run_scaling_case(run, unskipped=3, skipped=1, atol=1e-3) |
| 160 | 160 | ||
| 161 | @SupportedDevices(['Ascend910A', 'Ascend910P']) | 161 | @SupportedDevices(['Ascend910A', 'Ascend910P']) |
| 162 | def test_grad_scaling_clipping_1(self): | 162 | def test_grad_scaling_clipping_1(self): |
| 163 | def run(data, model, optimizer, scaler, loss_fn, skip_iter, try_scaling_api): | 163 | def run(data, model, optimizer, scaler, loss_fn, skip_iter, try_scaling_api): |
| 164 | max_norm = 0.2 # A reasonable value that actually has an effect, based on printouts of grads | 164 | max_norm = 0.2 # A reasonable value that actually has an effect, based on printouts of grads |
| 165 | for i, (input_data, target) in enumerate(data): | 165 | for i, (input_data, target) in enumerate(data): |
| 166 | optimizer.zero_grad() | 166 | optimizer.zero_grad() |
| 167 | output = model(input_data) | 167 | output = model(input_data) |
| 168 | loss = loss_fn(output, target) | 168 | loss = loss_fn(output, target) |
| 169 | if try_scaling_api: | 169 | if try_scaling_api: |
| 170 | scaler.scale(loss).backward() | 170 | scaler.scale(loss).backward() |
| 171 | torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm * scaler.get_scale()) | 171 | torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm * scaler.get_scale()) |
| 172 | if i == skip_iter and scaler.is_enabled(): | 172 | if i == skip_iter and scaler.is_enabled(): |
| 173 | make_device_overflow_1() | 173 | make_device_overflow_1() |
| 174 | scaler.step(optimizer) | 174 | scaler.step(optimizer) |
| 175 | scaler.update() | 175 | scaler.update() |
| 176 | else: | 176 | else: |
| 177 | loss.backward() | 177 | loss.backward() |
| 178 | torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm) | 178 | torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm) |
| 179 | if (not scaler.is_enabled()) or (i != skip_iter): | 179 | if (not scaler.is_enabled()) or (i != skip_iter): |
| 180 | optimizer.step() | 180 | optimizer.step() |
| 181 | 181 | ||
| 182 | self._run_scaling_case(run, unskipped=3, skipped=1, atol=1e-6) | 182 | self._run_scaling_case(run, unskipped=3, skipped=1, atol=1e-6) |
| 183 | 183 | ||
| 184 | @SupportedDevices(['Ascend910B']) | 184 | @SupportedDevices(['Ascend910B']) |
| 185 | def test_grad_scaling_clipping_2(self): | 185 | def test_grad_scaling_clipping_2(self): |
| 186 | def run(data, model, optimizer, scaler, loss_fn, skip_iter, try_scaling_api): | 186 | def run(data, model, optimizer, scaler, loss_fn, skip_iter, try_scaling_api): |
| 187 | max_norm = 0.2 # A reasonable value that actually has an effect, based on printouts of grads | 187 | max_norm = 0.2 # A reasonable value that actually has an effect, based on printouts of grads |
| 188 | for i, (input_data, target) in enumerate(data): | 188 | for i, (input_data, target) in enumerate(data): |
| 189 | optimizer.zero_grad() | 189 | optimizer.zero_grad() |
| 190 | output = model(input_data) | 190 | output = model(input_data) |
| 191 | loss = loss_fn(output, target) | 191 | loss = loss_fn(output, target) |
| 192 | if try_scaling_api: | 192 | if try_scaling_api: |
| 193 | scaler.scale(loss).backward() | 193 | scaler.scale(loss).backward() |
| 194 | torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm * scaler.get_scale()) | 194 | torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm * scaler.get_scale()) |
| 195 | if i == skip_iter and scaler.is_enabled(): | 195 | if i == skip_iter and scaler.is_enabled(): |
| 196 | make_device_overflow_2(model) | 196 | make_device_overflow_2(model) |
| 197 | scaler.step(optimizer) | 197 | scaler.step(optimizer) |
| 198 | scaler.update() | 198 | scaler.update() |
| 199 | else: | 199 | else: |
| 200 | loss.backward() | 200 | loss.backward() |
| 201 | torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm) | 201 | torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm) |
| 202 | if (not scaler.is_enabled()) or (i != skip_iter): | 202 | if (not scaler.is_enabled()) or (i != skip_iter): |
| 203 | optimizer.step() | 203 | optimizer.step() |
| 204 | 204 | ||
| 205 | self._run_scaling_case(run, unskipped=3, skipped=1, atol=1e-6) | 205 | self._run_scaling_case(run, unskipped=3, skipped=1, atol=1e-6) |
| 206 | 206 | ||
| 207 | @SupportedDevices(['Ascend910A', 'Ascend910P']) | 207 | @SupportedDevices(['Ascend910A', 'Ascend910P']) |
| 208 | def test_grad_scaling_clipping_separate_unscale_1(self): | 208 | def test_grad_scaling_clipping_separate_unscale_1(self): |
| 209 | def run(data, model, optimizer, scaler, loss_fn, skip_iter, try_scaling_api): | 209 | def run(data, model, optimizer, scaler, loss_fn, skip_iter, try_scaling_api): |
| 210 | max_norm = 0.2 # A reasonable value that actually has an effect, based on printouts of grads | 210 | max_norm = 0.2 # A reasonable value that actually has an effect, based on printouts of grads |
| 211 | for i, (input_data, target) in enumerate(data): | 211 | for i, (input_data, target) in enumerate(data): |
| 212 | optimizer.zero_grad() | 212 | optimizer.zero_grad() |
| 213 | output = model(input_data) | 213 | output = model(input_data) |
| 214 | loss = loss_fn(output, target) | 214 | loss = loss_fn(output, target) |
| 215 | if try_scaling_api: | 215 | if try_scaling_api: |
| 216 | scaler.scale(loss).backward() | 216 | scaler.scale(loss).backward() |
| 217 | if i == skip_iter and scaler.is_enabled(): | 217 | if i == skip_iter and scaler.is_enabled(): |
| 218 | make_device_overflow_1() | 218 | make_device_overflow_1() |
| 219 | scaler.unscale_(optimizer) | 219 | scaler.unscale_(optimizer) |
| 220 | torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm) | 220 | torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm) |
| 221 | scaler.step(optimizer) | 221 | scaler.step(optimizer) |
| 222 | scaler.update() | 222 | scaler.update() |
| 223 | else: | 223 | else: |
| 224 | loss.backward() | 224 | loss.backward() |
| 225 | torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm) | 225 | torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm) |
| 226 | if (not scaler.is_enabled()) or (i != skip_iter): | 226 | if (not scaler.is_enabled()) or (i != skip_iter): |
| 227 | optimizer.step() | 227 | optimizer.step() |
| 228 | 228 | ||
| 229 | self._run_scaling_case(run, unskipped=3, skipped=1) | 229 | self._run_scaling_case(run, unskipped=3, skipped=1) |
| 230 | 230 | ||
| 231 | @SupportedDevices(['Ascend910B']) | 231 | @SupportedDevices(['Ascend910B']) |
| 232 | def test_grad_scaling_clipping_separate_unscale_2(self): | 232 | def test_grad_scaling_clipping_separate_unscale_2(self): |
| 233 | def run(data, model, optimizer, scaler, loss_fn, skip_iter, try_scaling_api): | 233 | def run(data, model, optimizer, scaler, loss_fn, skip_iter, try_scaling_api): |
| 234 | max_norm = 0.2 # A reasonable value that actually has an effect, based on printouts of grads | 234 | max_norm = 0.2 # A reasonable value that actually has an effect, based on printouts of grads |
| 235 | for i, (input_data, target) in enumerate(data): | 235 | for i, (input_data, target) in enumerate(data): |
| 236 | optimizer.zero_grad() | 236 | optimizer.zero_grad() |
| 237 | output = model(input_data) | 237 | output = model(input_data) |
| 238 | loss = loss_fn(output, target) | 238 | loss = loss_fn(output, target) |
| 239 | if try_scaling_api: | 239 | if try_scaling_api: |
| 240 | scaler.scale(loss).backward() | 240 | scaler.scale(loss).backward() |
| 241 | if i == skip_iter and scaler.is_enabled(): | 241 | if i == skip_iter and scaler.is_enabled(): |
| 242 | make_device_overflow_2(model) | 242 | make_device_overflow_2(model) |
| 243 | scaler.unscale_(optimizer) | 243 | scaler.unscale_(optimizer) |
| 244 | torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm) | 244 | torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm) |
| 245 | scaler.step(optimizer) | 245 | scaler.step(optimizer) |
| 246 | scaler.update() | 246 | scaler.update() |
| 247 | else: | 247 | else: |
| 248 | loss.backward() | 248 | loss.backward() |
| 249 | torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm) | 249 | torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm) |
| 250 | if (not scaler.is_enabled()) or (i != skip_iter): | 250 | if (not scaler.is_enabled()) or (i != skip_iter): |
| 251 | optimizer.step() | 251 | optimizer.step() |
| 252 | 252 | ||
| 253 | self._run_scaling_case(run, unskipped=3, skipped=1) | 253 | self._run_scaling_case(run, unskipped=3, skipped=1) |
| 254 | 254 | ||
| 255 | @SupportedDevices(['Ascend910A', 'Ascend910P']) | 255 | @SupportedDevices(['Ascend910A', 'Ascend910P']) |
| 256 | def test_grad_scaling_penalty_1(self): | 256 | def test_grad_scaling_penalty_1(self): |
| 257 | def run(data, model, optimizer, scaler, loss_fn, skip_iter, try_scaling_api): | 257 | def run(data, model, optimizer, scaler, loss_fn, skip_iter, try_scaling_api): |
| 258 | for i, (input_data, target) in enumerate(data): | 258 | for i, (input_data, target) in enumerate(data): |
| 259 | optimizer.zero_grad() | 259 | optimizer.zero_grad() |
| 260 | output = model(input_data) | 260 | output = model(input_data) |
| 261 | loss = loss_fn(output, target) | 261 | loss = loss_fn(output, target) |
| 262 | 262 | ||
| 263 | if try_scaling_api: | 263 | if try_scaling_api: |
| 264 | grad_params = torch.autograd.grad(scaler.scale(loss), | 264 | grad_params = torch.autograd.grad(scaler.scale(loss), |
| 265 | model.parameters(), create_graph=True) | 265 | model.parameters(), create_graph=True) |
| 266 | inv_scale = 1. / scaler.get_scale() | 266 | inv_scale = 1. / scaler.get_scale() |
| 267 | grad_params = [p * inv_scale for p in grad_params] | 267 | grad_params = [p * inv_scale for p in grad_params] |
| 268 | else: | 268 | else: |
| 269 | grad_params = torch.autograd.grad(loss, model.parameters(), create_graph=True) | 269 | grad_params = torch.autograd.grad(loss, model.parameters(), create_graph=True) |
| 270 | 270 | ||
| 271 | grad_norm = 0 | 271 | grad_norm = 0 |
| 272 | for grad in grad_params: | 272 | for grad in grad_params: |
| 273 | grad_norm += grad.pow(2).sum() | 273 | grad_norm += grad.pow(2).sum() |
| 274 | grad_norm = grad_norm.sqrt() | 274 | grad_norm = grad_norm.sqrt() |
| 275 | loss = loss + grad_norm | 275 | loss = loss + grad_norm |
| 276 | 276 | ||
| 277 | if try_scaling_api: | 277 | if try_scaling_api: |
| 278 | scaler.scale(loss).backward() | 278 | scaler.scale(loss).backward() |
| 279 | if i == skip_iter and scaler.is_enabled(): | 279 | if i == skip_iter and scaler.is_enabled(): |
| 280 | make_device_overflow_1() | 280 | make_device_overflow_1() |
| 281 | scaler.step(optimizer) | 281 | scaler.step(optimizer) |
| 282 | scaler.update() | 282 | scaler.update() |
| 283 | else: | 283 | else: |
| 284 | loss.backward() | 284 | loss.backward() |
| 285 | if (not scaler.is_enabled()) or (i != skip_iter): | 285 | if (not scaler.is_enabled()) or (i != skip_iter): |
| 286 | optimizer.step() | 286 | optimizer.step() |
| 287 | 287 | ||
| 288 | self._run_scaling_case(run, unskipped=3, skipped=1) | 288 | self._run_scaling_case(run, unskipped=3, skipped=1) |
| 289 | 289 | ||
| 290 | @SupportedDevices(['Ascend910B']) | 290 | @SupportedDevices(['Ascend910B']) |
| 291 | def test_grad_scaling_penalty_2(self): | 291 | def test_grad_scaling_penalty_2(self): |
| 292 | def run(data, model, optimizer, scaler, loss_fn, skip_iter, try_scaling_api): | 292 | def run(data, model, optimizer, scaler, loss_fn, skip_iter, try_scaling_api): |
| 293 | for i, (input_data, target) in enumerate(data): | 293 | for i, (input_data, target) in enumerate(data): |
| 294 | optimizer.zero_grad() | 294 | optimizer.zero_grad() |
| 295 | output = model(input_data) | 295 | output = model(input_data) |
| 296 | loss = loss_fn(output, target) | 296 | loss = loss_fn(output, target) |
| 297 | 297 | ||
| 298 | if try_scaling_api: | 298 | if try_scaling_api: |
| 299 | grad_params = torch.autograd.grad(scaler.scale(loss), | 299 | grad_params = torch.autograd.grad(scaler.scale(loss), |
| 300 | model.parameters(), create_graph=True) | 300 | model.parameters(), create_graph=True) |
| 301 | inv_scale = 1. / scaler.get_scale() | 301 | inv_scale = 1. / scaler.get_scale() |
| 302 | grad_params = [p * inv_scale for p in grad_params] | 302 | grad_params = [p * inv_scale for p in grad_params] |
| 303 | else: | 303 | else: |
| 304 | grad_params = torch.autograd.grad(loss, model.parameters(), create_graph=True) | 304 | grad_params = torch.autograd.grad(loss, model.parameters(), create_graph=True) |
| 305 | 305 | ||
| 306 | grad_norm = 0 | 306 | grad_norm = 0 |
| 307 | for grad in grad_params: | 307 | for grad in grad_params: |
| 308 | grad_norm += grad.pow(2).sum() | 308 | grad_norm += grad.pow(2).sum() |
| 309 | grad_norm = grad_norm.sqrt() | 309 | grad_norm = grad_norm.sqrt() |
| 310 | loss = loss + grad_norm | 310 | loss = loss + grad_norm |
| 311 | 311 | ||
| 312 | if try_scaling_api: | 312 | if try_scaling_api: |
| 313 | scaler.scale(loss).backward() | 313 | scaler.scale(loss).backward() |
| 314 | if i == skip_iter and scaler.is_enabled(): | 314 | if i == skip_iter and scaler.is_enabled(): |
| 315 | make_device_overflow_2(model) | 315 | make_device_overflow_2(model) |
| 316 | scaler.step(optimizer) | 316 | scaler.step(optimizer) |
| 317 | scaler.update() | 317 | scaler.update() |
| 318 | else: | 318 | else: |
| 319 | loss.backward() | 319 | loss.backward() |
| 320 | if (not scaler.is_enabled()) or (i != skip_iter): | 320 | if (not scaler.is_enabled()) or (i != skip_iter): |
| 321 | optimizer.step() | 321 | optimizer.step() |
| 322 | 322 | ||
| 323 | self._run_scaling_case(run, unskipped=3, skipped=1) | 323 | self._run_scaling_case(run, unskipped=3, skipped=1) |
| 324 | 324 | ||
| 325 | def test_grad_scaling_accumulation(self): | 325 | def test_grad_scaling_accumulation(self): |
| 326 | def run(data, model, optimizer, scaler, loss_fn, skip_iter, try_scaling_api): | 326 | def run(data, model, optimizer, scaler, loss_fn, skip_iter, try_scaling_api): |
| 327 | iters_to_accumulate = 2 | 327 | iters_to_accumulate = 2 |
| 328 | for i, (input_data, target) in enumerate(data): | 328 | for i, (input_data, target) in enumerate(data): |
| 329 | output = model(input_data) | 329 | output = model(input_data) |
| 330 | loss = loss_fn(output, target) | 330 | loss = loss_fn(output, target) |
| 331 | loss = loss / iters_to_accumulate | 331 | loss = loss / iters_to_accumulate |
| 332 | if try_scaling_api: | 332 | if try_scaling_api: |
| 333 | scaler.scale(loss).backward() | 333 | scaler.scale(loss).backward() |
| 334 | else: | 334 | else: |
| 335 | loss.backward() | 335 | loss.backward() |
| 336 | if (i + 1) % iters_to_accumulate == 0: | 336 | if (i + 1) % iters_to_accumulate == 0: |
| 337 | if try_scaling_api: | 337 | if try_scaling_api: |
| 338 | scaler.step(optimizer) | 338 | scaler.step(optimizer) |
| 339 | scaler.update() | 339 | scaler.update() |
| 340 | optimizer.zero_grad() | 340 | optimizer.zero_grad() |
| 341 | else: | 341 | else: |
| 342 | optimizer.step() | 342 | optimizer.step() |
| 343 | optimizer.zero_grad() | 343 | optimizer.zero_grad() |
| 344 | 344 | ||
| 345 | self._run_scaling_case(run, unskipped=2, skipped=0) | 345 | self._run_scaling_case(run, unskipped=2, skipped=0) |
| 346 | 346 | ||
| 347 | @SupportedDevices(['Ascend910A', 'Ascend910P']) | 347 | @SupportedDevices(['Ascend910A', 'Ascend910P']) |
| 348 | def test_grad_scaling_multiple_1(self): | 348 | def test_grad_scaling_multiple_1(self): |
| 349 | # Tests gradient scaling with 2 models and 2 optimizers that both receive gradients from 2 losses. | 349 | # Tests gradient scaling with 2 models and 2 optimizers that both receive gradients from 2 losses. |
| 350 | # Some of the logic here cannot reuse the generic helper functions created for the 1-optimizer cases. | 350 | # Some of the logic here cannot reuse the generic helper functions created for the 1-optimizer cases. |
| 351 | for enabled in True, False: | 351 | for enabled in True, False: |
| 352 | mod_control0, mod_scaling0, opt_control0, opt_scaling0, data, loss_fn, skip_iter = \ | 352 | mod_control0, mod_scaling0, opt_control0, opt_scaling0, data, loss_fn, skip_iter = \ |
| 353 | self._create_scaling_case() | 353 | self._create_scaling_case() |
| 354 | mod_control1, mod_scaling1, opt_control1, opt_scaling1 = \ | 354 | mod_control1, mod_scaling1, opt_control1, opt_scaling1 = \ |
| 355 | self._create_scaling_models_optimizers() | 355 | self._create_scaling_models_optimizers() |
| 356 | 356 | ||
| 357 | scaler = GradScaler(init_scale=128., growth_factor=2.0, enabled=enabled, growth_interval=1) | 357 | scaler = GradScaler(init_scale=128., growth_factor=2.0, enabled=enabled, growth_interval=1) |
| 358 | 358 | ||
| 359 | def run(model0, model1, optimizer0, optimizer1, try_scaling_api): | 359 | def run(model0, model1, optimizer0, optimizer1, try_scaling_api): |
| 360 | for i, (input_data, target) in enumerate(data): | 360 | for i, (input_data, target) in enumerate(data): |
| 361 | optimizer0.zero_grad() | 361 | optimizer0.zero_grad() |
| 362 | optimizer1.zero_grad() | 362 | optimizer1.zero_grad() |
| 363 | output0 = model0(input_data) | 363 | output0 = model0(input_data) |
| 364 | output1 = model1(input_data) | 364 | output1 = model1(input_data) |
| 365 | loss0 = loss_fn(0.3 * output0 + 0.7 * output1, target) | 365 | loss0 = loss_fn(0.3 * output0 + 0.7 * output1, target) |
| 366 | loss1 = loss_fn(0.6 * output0 - 0.4 * output1, target) | 366 | loss1 = loss_fn(0.6 * output0 - 0.4 * output1, target) |
| 367 | 367 | ||
| 368 | if try_scaling_api: | 368 | if try_scaling_api: |
| 369 | scaler.scale(loss0).backward(retain_graph=True) | 369 | scaler.scale(loss0).backward(retain_graph=True) |
| 370 | scaler.scale(loss1).backward() | 370 | scaler.scale(loss1).backward() |
| 371 | if i == skip_iter and scaler.is_enabled(): | 371 | if i == skip_iter and scaler.is_enabled(): |
| 372 | make_device_overflow_1() | 372 | make_device_overflow_1() |
| 373 | 373 | ||
| 374 | # As an additional stress test, separately unscale for one of the optimizers. | 374 | # As an additional stress test, separately unscale for one of the optimizers. |
| 375 | scaler.unscale_(optimizer0) | 375 | scaler.unscale_(optimizer0) |
| 376 | 376 | ||
| 377 | scaler.step(optimizer0) | 377 | scaler.step(optimizer0) |
| 378 | scaler.step(optimizer1) | 378 | scaler.step(optimizer1) |
| 379 | scaler.update() | 379 | scaler.update() |
| 380 | else: | 380 | else: |
| 381 | loss0.backward(retain_graph=True) | 381 | loss0.backward(retain_graph=True) |
| 382 | loss1.backward() | 382 | loss1.backward() |
| 383 | if (not scaler.is_enabled()) or (i != skip_iter): | 383 | if (not scaler.is_enabled()) or (i != skip_iter): |
| 384 | optimizer0.step() | 384 | optimizer0.step() |
| 385 | optimizer1.step() | 385 | optimizer1.step() |
| 386 | 386 | ||
| 387 | run(mod_control0, mod_control1, opt_control0, opt_control1, False) | 387 | run(mod_control0, mod_control1, opt_control0, opt_control1, False) |
| 388 | run(mod_scaling0, mod_scaling1, opt_scaling0, opt_scaling1, True) | 388 | run(mod_scaling0, mod_scaling1, opt_scaling0, opt_scaling1, True) |
| 389 | 389 | ||
| 390 | # The loss scale should have been multiplied by the growth factor 3 times and the backoff factor once. | 390 | # The loss scale should have been multiplied by the growth factor 3 times and the backoff factor once. |
| 391 | self.assertTrue(scaler.get_scale() == (128. * scaler.get_growth_factor()**3 * | 391 | self.assertTrue(scaler.get_scale() == (128. * scaler.get_growth_factor()**3 * |
| 392 | scaler.get_backoff_factor()**1) if enabled else 1.0) | 392 | scaler.get_backoff_factor()**1) if enabled else 1.0) |
| 393 | 393 | ||
| 394 | for c, s in zip(chain(mod_control0.parameters(), mod_control1.parameters()), | 394 | for c, s in zip(chain(mod_control0.parameters(), mod_control1.parameters()), |
| 395 | chain(mod_scaling0.parameters(), mod_scaling1.parameters())): | 395 | chain(mod_scaling0.parameters(), mod_scaling1.parameters())): |
| 396 | c = c.cpu().to(torch.float).detach().numpy() | 396 | c = c.cpu().to(torch.float).detach().numpy() |
| 397 | s = s.cpu().to(torch.float).detach().numpy() | 397 | s = s.cpu().to(torch.float).detach().numpy() |
| 398 | self.assertRtolEqual(c, s, 1e-7) | 398 | self.assertRtolEqual(c, s, 1e-7) |
| 399 | 399 | ||
| 400 | @SupportedDevices(['Ascend910B']) | 400 | @SupportedDevices(['Ascend910B']) |
| 401 | def test_grad_scaling_multiple_2(self): | 401 | def test_grad_scaling_multiple_2(self): |
| 402 | # Tests gradient scaling with 2 models and 2 optimizers that both receive gradients from 2 losses. | 402 | # Tests gradient scaling with 2 models and 2 optimizers that both receive gradients from 2 losses. |
| 403 | # Some of the logic here cannot reuse the generic helper functions created for the 1-optimizer cases. | 403 | # Some of the logic here cannot reuse the generic helper functions created for the 1-optimizer cases. |
| 404 | for enabled in True, False: | 404 | for enabled in True, False: |
| 405 | mod_control0, mod_scaling0, opt_control0, opt_scaling0, data, loss_fn, skip_iter = \ | 405 | mod_control0, mod_scaling0, opt_control0, opt_scaling0, data, loss_fn, skip_iter = \ |
| 406 | self._create_scaling_case() | 406 | self._create_scaling_case() |
| 407 | mod_control1, mod_scaling1, opt_control1, opt_scaling1 = \ | 407 | mod_control1, mod_scaling1, opt_control1, opt_scaling1 = \ |
| 408 | self._create_scaling_models_optimizers() | 408 | self._create_scaling_models_optimizers() |
| 409 | 409 | ||
| 410 | scaler = GradScaler(init_scale=128., growth_factor=2.0, enabled=enabled, growth_interval=1) | 410 | scaler = GradScaler(init_scale=128., growth_factor=2.0, enabled=enabled, growth_interval=1) |
| 411 | 411 | ||
| 412 | def run(model0, model1, optimizer0, optimizer1, try_scaling_api): | 412 | def run(model0, model1, optimizer0, optimizer1, try_scaling_api): |
| 413 | for i, (input_data, target) in enumerate(data): | 413 | for i, (input_data, target) in enumerate(data): |
| 414 | optimizer0.zero_grad() | 414 | optimizer0.zero_grad() |
| 415 | optimizer1.zero_grad() | 415 | optimizer1.zero_grad() |
| 416 | output0 = model0(input_data) | 416 | output0 = model0(input_data) |
| 417 | output1 = model1(input_data) | 417 | output1 = model1(input_data) |
| 418 | loss0 = loss_fn(0.3 * output0 + 0.7 * output1, target) | 418 | loss0 = loss_fn(0.3 * output0 + 0.7 * output1, target) |
| 419 | loss1 = loss_fn(0.6 * output0 - 0.4 * output1, target) | 419 | loss1 = loss_fn(0.6 * output0 - 0.4 * output1, target) |
| 420 | 420 | ||
| 421 | if try_scaling_api: | 421 | if try_scaling_api: |
| 422 | scaler.scale(loss0).backward(retain_graph=True) | 422 | scaler.scale(loss0).backward(retain_graph=True) |
| 423 | scaler.scale(loss1).backward() | 423 | scaler.scale(loss1).backward() |
| 424 | if i == skip_iter and scaler.is_enabled(): | 424 | if i == skip_iter and scaler.is_enabled(): |
| 425 | make_device_overflow_2(model0) | 425 | make_device_overflow_2(model0) |
| 426 | 426 | ||
| 427 | # As an additional stress test, separately unscale for one of the optimizers. | 427 | # As an additional stress test, separately unscale for one of the optimizers. |
| 428 | scaler.unscale_(optimizer0) | 428 | scaler.unscale_(optimizer0) |
| 429 | 429 | ||
| 430 | scaler.step(optimizer0) | 430 | scaler.step(optimizer0) |
| 431 | scaler.step(optimizer1) | 431 | scaler.step(optimizer1) |
| 432 | scaler.update() | 432 | scaler.update() |
| 433 | else: | 433 | else: |
| 434 | loss0.backward(retain_graph=True) | 434 | loss0.backward(retain_graph=True) |
| 435 | loss1.backward() | 435 | loss1.backward() |
| 436 | if (not scaler.is_enabled()) or (i != skip_iter): | 436 | if (not scaler.is_enabled()) or (i != skip_iter): |
| 437 | optimizer0.step() | 437 | optimizer0.step() |
| 438 | optimizer1.step() | 438 | optimizer1.step() |
| 439 | 439 | ||
| 440 | run(mod_control0, mod_control1, opt_control0, opt_control1, False) | 440 | run(mod_control0, mod_control1, opt_control0, opt_control1, False) |
| 441 | run(mod_scaling0, mod_scaling1, opt_scaling0, opt_scaling1, True) | 441 | run(mod_scaling0, mod_scaling1, opt_scaling0, opt_scaling1, True) |
| 442 | 442 | ||
| 443 | # The loss scale should have been multiplied by the growth factor 3 times and the backoff factor once. | 443 | # The loss scale should have been multiplied by the growth factor 3 times and the backoff factor once. |
| 444 | self.assertTrue(scaler.get_scale() == (128. * scaler.get_growth_factor() ** 3 * | 444 | self.assertTrue(scaler.get_scale() == (128. * scaler.get_growth_factor() ** 3 * |
| 445 | scaler.get_backoff_factor() ** 1) if enabled else 1.0) | 445 | scaler.get_backoff_factor() ** 1) if enabled else 1.0) |
| 446 | 446 | ||
| 447 | for c, s in zip(chain(mod_control0.parameters(), mod_control1.parameters()), | 447 | for c, s in zip(chain(mod_control0.parameters(), mod_control1.parameters()), |
| 448 | chain(mod_scaling0.parameters(), mod_scaling1.parameters())): | 448 | chain(mod_scaling0.parameters(), mod_scaling1.parameters())): |
| 449 | c = c.cpu().to(torch.float).detach().numpy() | 449 | c = c.cpu().to(torch.float).detach().numpy() |
| 450 | s = s.cpu().to(torch.float).detach().numpy() | 450 | s = s.cpu().to(torch.float).detach().numpy() |
| 451 | self.assertRtolEqual(c, s, 1e-7) | 451 | self.assertRtolEqual(c, s, 1e-7) |
| 452 | 452 | ||
| 453 | def test_autocast_custom_enabled(self): | 453 | def test_autocast_custom_enabled(self): |
| 454 | class MyMM(torch.autograd.Function): | 454 | class MyMM(torch.autograd.Function): |
| 455 | @staticmethod | 455 | @staticmethod |
| 456 | @torch.npu.amp.custom_fwd | 456 | @torch.npu.amp.custom_fwd |
| 457 | def forward(ctx, a, b): | 457 | def forward(ctx, a, b): |
| 458 | self.assertTrue(ctx._dtype is torch.get_autocast_dtype("npu")) | 458 | self.assertTrue(ctx._dtype is torch.get_autocast_dtype("npu")) |
| 459 | self.assertTrue(a.dtype is torch.float32) | 459 | self.assertTrue(a.dtype is torch.float32) |
| 460 | self.assertTrue(b.dtype is torch.float32) | 460 | self.assertTrue(b.dtype is torch.float32) |
| 461 | self.assertTrue(torch.npu.is_autocast_enabled()) | 461 | self.assertTrue(torch.npu.is_autocast_enabled()) |
| 462 | ctx.save_for_backward(a, b) | 462 | ctx.save_for_backward(a, b) |
| 463 | return a.mm(b) | 463 | return a.mm(b) |
| 464 | 464 | ||
| 465 | @staticmethod | 465 | @staticmethod |
| 466 | @torch.npu.amp.custom_bwd | 466 | @torch.npu.amp.custom_bwd |
| 467 | def backward(ctx, grad): | 467 | def backward(ctx, grad): |
| 468 | self.assertTrue(ctx._dtype is torch.get_autocast_dtype("npu")) | 468 | self.assertTrue(ctx._dtype is torch.get_autocast_dtype("npu")) |
| 469 | self.assertTrue(torch.npu.is_autocast_enabled()) | 469 | self.assertTrue(torch.npu.is_autocast_enabled()) |
| 470 | a, b = ctx.saved_tensors | 470 | a, b = ctx.saved_tensors |
| 471 | return grad.mm(b.t()), a.t().mm(grad) | 471 | return grad.mm(b.t()), a.t().mm(grad) |
| 472 | 472 | ||
| 473 | mymm = MyMM.apply | 473 | mymm = MyMM.apply |
| 474 | 474 | ||
| 475 | x = torch.randn((8, 8), device="npu", dtype=torch.float32, requires_grad=True) | 475 | x = torch.randn((8, 8), device="npu", dtype=torch.float32, requires_grad=True) |
| 476 | y = torch.randn((8, 8), device="npu", dtype=torch.float32, requires_grad=True) | 476 | y = torch.randn((8, 8), device="npu", dtype=torch.float32, requires_grad=True) |
| 477 | 477 | ||
| 478 | with torch.npu.amp.autocast(): | 478 | with torch.npu.amp.autocast(): |
| 479 | output = mymm(x, y) | 479 | output = mymm(x, y) |
| 480 | self.assertTrue(output.dtype is torch.float16) | 480 | self.assertTrue(output.dtype is torch.float16) |
| 481 | loss = output.sum() | 481 | loss = output.sum() |
| 482 | loss.backward() | 482 | loss.backward() |
| 483 | 483 | ||
| 484 | def test_autocast_custom_cast_inputs(self): | 484 | def test_autocast_custom_cast_inputs(self): |
| 485 | class MyMM(torch.autograd.Function): | 485 | class MyMM(torch.autograd.Function): |
| 486 | @staticmethod | 486 | @staticmethod |
| 487 | @torch.npu.amp.custom_fwd(cast_inputs=torch.float32) | 487 | @torch.npu.amp.custom_fwd(cast_inputs=torch.float32) |
| 488 | def forward(ctx, a, container, expect_type): | 488 | def forward(ctx, a, container, expect_type): |
| 489 | self.assertTrue(ctx._dtype is torch.get_autocast_dtype("npu")) | 489 | self.assertTrue(ctx._dtype is torch.get_autocast_dtype("npu")) |
| 490 | b = container[1][0] | 490 | b = container[1][0] |
| 491 | self.assertTrue(a.dtype is expect_type) | 491 | self.assertTrue(a.dtype is expect_type) |
| 492 | self.assertTrue(b.dtype is expect_type) | 492 | self.assertTrue(b.dtype is expect_type) |
| 493 | self.assertFalse(torch.npu.is_autocast_enabled()) | 493 | self.assertFalse(torch.npu.is_autocast_enabled()) |
| 494 | ctx.save_for_backward(a, b) | 494 | ctx.save_for_backward(a, b) |
| 495 | return a.mm(b) | 495 | return a.mm(b) |
| 496 | 496 | ||
| 497 | @staticmethod | 497 | @staticmethod |
| 498 | @torch.npu.amp.custom_bwd | 498 | @torch.npu.amp.custom_bwd |
| 499 | def backward(ctx, grad): | 499 | def backward(ctx, grad): |
| 500 | self.assertTrue(ctx._dtype is torch.get_autocast_dtype("npu")) | 500 | self.assertTrue(ctx._dtype is torch.get_autocast_dtype("npu")) |
| 501 | a, b = ctx.saved_tensors | 501 | a, b = ctx.saved_tensors |
| 502 | return grad.mm(b.t()), None, None | 502 | return grad.mm(b.t()), None, None |
| 503 | 503 | ||
| 504 | mymm = MyMM.apply | 504 | mymm = MyMM.apply |
| 505 | 505 | ||
| 506 | x = torch.randn((8, 8), device="npu", dtype=torch.float16, requires_grad=True) | 506 | x = torch.randn((8, 8), device="npu", dtype=torch.float16, requires_grad=True) |
| 507 | # Puts one input tensor in a nested container. y's contained Tensor won't receive a gradient, | 507 | # Puts one input tensor in a nested container. y's contained Tensor won't receive a gradient, |
| 508 | # because torch.autograd.Function can't hand gradients back to non-Tensor forward arguments. | 508 | # because torch.autograd.Function can't hand gradients back to non-Tensor forward arguments. |
| 509 | # Sets requires_grad=False explicitly so we don't lie about expecting a gradient. | 509 | # Sets requires_grad=False explicitly so we don't lie about expecting a gradient. |
| 510 | y = (0, {0: torch.randn((8, 8), device="npu", dtype=torch.float16, requires_grad=False)}) | 510 | y = (0, {0: torch.randn((8, 8), device="npu", dtype=torch.float16, requires_grad=False)}) |
| 511 | 511 | ||
| 512 | with torch.autocast('npu', ): | 512 | with torch.autocast('npu', ): |
| 513 | output = mymm(x, y, torch.float32) | 513 | output = mymm(x, y, torch.float32) |
| 514 | self.assertTrue(output.dtype is torch.float32) | 514 | self.assertTrue(output.dtype is torch.float32) |
| 515 | loss = output.sum() | 515 | loss = output.sum() |
| 516 | loss.backward() | 516 | loss.backward() |
| 517 | 517 | ||
| 518 | # Tests if custom_fwd becomes a no-op when mymm runs outside an autocast-enabled region. | 518 | # Tests if custom_fwd becomes a no-op when mymm runs outside an autocast-enabled region. |
| 519 | output = mymm(x, y, torch.float16) | 519 | output = mymm(x, y, torch.float16) |
| 520 | self.assertTrue(output.dtype is torch.float16) | 520 | self.assertTrue(output.dtype is torch.float16) |
| 521 | loss = output.sum() | 521 | loss = output.sum() |
| 522 | loss.backward() | 522 | loss.backward() |
| 523 | 523 | ||
| 524 | 524 | ||
| 525 | if __name__ == "__main__": | 525 | if __name__ == "__main__": |
| 526 | run_tests() | 526 | run_tests() |
| @@ -1,138 +1,138 @@ | |||
| 1 | import unittest | 1 | import unittest |
| 2 | import torch | 2 | import torch |
| 3 | import numpy as np | 3 | import numpy as np |
| 4 | 4 | ||
| 5 | import torch_npu | 5 | import torch_npu |
| 6 | from torch_npu.testing.testcase import TestCase, run_tests | 6 | from torch_npu.testing.testcase import TestCase, run_tests |
| 7 | from torch_npu.testing.common_utils import create_common_tensor | 7 | from torch_npu.testing.common_utils import create_common_tensor |
| 8 | 8 | ||
| 9 | 9 | ||
| 10 | class TestCopyKernelMemoryFormat(TestCase): | 10 | class TestCopyKernelMemoryFormat(TestCase): |
| 11 | def test_h2d_copy_contiguous_tensor(self): | 11 | def test_h2d_copy_contiguous_tensor(self): |
| 12 | dtype_list = [np.float16, np.float32, np.int32, np.int64] | 12 | dtype_list = [np.float16, np.float32, np.int32, np.int64] |
| 13 | shape_list = [[10, 20], [32, 64, 128], [2, 3, 4, 5]] | 13 | shape_list = [[10, 20], [32, 64, 128], [2, 3, 4, 5]] |
| 14 | shape_format = [ | 14 | shape_format = [ |
| 15 | [dtype, 2, shape] for dtype in dtype_list for shape in shape_list | 15 | [dtype, 2, shape] for dtype in dtype_list for shape in shape_list |
| 16 | ] | 16 | ] |
| 17 | 17 | ||
| 18 | for item in shape_format: | 18 | for item in shape_format: |
| 19 | cpu_input, npu_input = create_common_tensor(item, -100, 100) | 19 | cpu_input, npu_input = create_common_tensor(item, -100, 100) |
| 20 | npu_input_copy = cpu_input.npu() | 20 | npu_input_copy = cpu_input.npu() |
| 21 | self.assertRtolEqual(npu_input_copy.cpu().numpy(), cpu_input.numpy()) | 21 | self.assertRtolEqual(npu_input_copy.cpu().numpy(), cpu_input.numpy()) |
| 22 | 22 | ||
| 23 | def test_h2d_copy_non_contiguous_tensor(self): | 23 | def test_h2d_copy_non_contiguous_tensor(self): |
| 24 | dtype_list = [np.float16, np.float32] | 24 | dtype_list = [np.float16, np.float32] |
| 25 | shape_list = [[32, 64], [16, 32, 64]] | 25 | shape_list = [[32, 64], [16, 32, 64]] |
| 26 | shape_format = [ | 26 | shape_format = [ |
| 27 | [dtype, 2, shape] for dtype in dtype_list for shape in shape_list | 27 | [dtype, 2, shape] for dtype in dtype_list for shape in shape_list |
| 28 | ] | 28 | ] |
| 29 | 29 | ||
| 30 | for item in shape_format: | 30 | for item in shape_format: |
| 31 | cpu_input, npu_input = create_common_tensor(item, -100, 100) | 31 | cpu_input, npu_input = create_common_tensor(item, -100, 100) |
| 32 | cpu_transposed = cpu_input.transpose(-1, -2) | 32 | cpu_transposed = cpu_input.transpose(-1, -2) |
| 33 | npu_transposed = cpu_transposed.npu() | 33 | npu_transposed = cpu_transposed.npu() |
| 34 | npu_contiguous = npu_transposed.contiguous() | 34 | npu_contiguous = npu_transposed.contiguous() |
| 35 | self.assertRtolEqual(npu_contiguous.cpu().numpy(), cpu_transposed.contiguous().numpy()) | 35 | self.assertRtolEqual(npu_contiguous.cpu().numpy(), cpu_transposed.contiguous().numpy()) |
| 36 | 36 | ||
| 37 | def test_d2h_copy_contiguous_tensor(self): | 37 | def test_d2h_copy_contiguous_tensor(self): |
| 38 | dtype_list = [np.float16, np.float32, np.int32, np.int64] | 38 | dtype_list = [np.float16, np.float32, np.int32, np.int64] |
| 39 | shape_list = [[10, 20], [32, 64, 128], [2, 3, 4, 5]] | 39 | shape_list = [[10, 20], [32, 64, 128], [2, 3, 4, 5]] |
| 40 | shape_format = [ | 40 | shape_format = [ |
| 41 | [dtype, 2, shape] for dtype in dtype_list for shape in shape_list | 41 | [dtype, 2, shape] for dtype in dtype_list for shape in shape_list |
| 42 | ] | 42 | ] |
| 43 | 43 | ||
| 44 | for item in shape_format: | 44 | for item in shape_format: |
| 45 | cpu_input, npu_input = create_common_tensor(item, -100, 100) | 45 | cpu_input, npu_input = create_common_tensor(item, -100, 100) |
| 46 | cpu_output = npu_input.cpu() | 46 | cpu_output = npu_input.cpu() |
| 47 | self.assertRtolEqual(cpu_output.numpy(), cpu_input.numpy()) | 47 | self.assertRtolEqual(cpu_output.numpy(), cpu_input.numpy()) |
| 48 | 48 | ||
| 49 | def test_d2h_copy_non_contiguous_tensor(self): | 49 | def test_d2h_copy_non_contiguous_tensor(self): |
| 50 | dtype_list = [np.float16, np.float32] | 50 | dtype_list = [np.float16, np.float32] |
| 51 | shape_list = [[32, 64], [16, 32, 64]] | 51 | shape_list = [[32, 64], [16, 32, 64]] |
| 52 | shape_format = [ | 52 | shape_format = [ |
| 53 | [dtype, 2, shape] for dtype in dtype_list for shape in shape_list | 53 | [dtype, 2, shape] for dtype in dtype_list for shape in shape_list |
| 54 | ] | 54 | ] |
| 55 | 55 | ||
| 56 | for item in shape_format: | 56 | for item in shape_format: |
| 57 | cpu_input, npu_input = create_common_tensor(item, -100, 100) | 57 | cpu_input, npu_input = create_common_tensor(item, -100, 100) |
| 58 | npu_transposed = npu_input.transpose(-1, -2) | 58 | npu_transposed = npu_input.transpose(-1, -2) |
| 59 | cpu_output = npu_transposed.cpu() | 59 | cpu_output = npu_transposed.cpu() |
| 60 | self.assertRtolEqual(cpu_output.numpy(), cpu_input.transpose(-1, -2).contiguous().numpy()) | 60 | self.assertRtolEqual(cpu_output.numpy(), cpu_input.transpose(-1, -2).contiguous().numpy()) |
| 61 | 61 | ||
| 62 | def test_h2d_copy_different_dtype(self): | 62 | def test_h2d_copy_different_dtype(self): |
| 63 | src_dtype_list = [np.float32, np.float16] | 63 | src_dtype_list = [np.float32, np.float16] |
| 64 | dst_dtype_list = [torch.float16, torch.float32] | 64 | dst_dtype_list = [torch.float16, torch.float32] |
| 65 | shape = [32, 64] | 65 | shape = [32, 64] |
| 66 | 66 | ||
| 67 | for src_dtype, dst_dtype in zip(src_dtype_list, dst_dtype_list): | 67 | for src_dtype, dst_dtype in zip(src_dtype_list, dst_dtype_list): |
| 68 | cpu_input = torch.randn(shape, dtype=torch.float32) * 100 | 68 | cpu_input = torch.randn(shape, dtype=torch.float32) * 100 |
| 69 | cpu_input = cpu_input.to(torch.from_numpy(np.array([])).dtype if src_dtype == np.float32 else torch.float16) | 69 | cpu_input = cpu_input.to(torch.from_numpy(np.array([])).dtype if src_dtype == np.float32 else torch.float16) |
| 70 | 70 | ||
| 71 | npu_input = cpu_input.npu() | 71 | npu_input = cpu_input.npu() |
| 72 | npu_output = npu_input.to(dst_dtype) | 72 | npu_output = npu_input.to(dst_dtype) |
| 73 | 73 | ||
| 74 | cpu_output = cpu_input.to(dst_dtype) | 74 | cpu_output = cpu_input.to(dst_dtype) |
| 75 | self.assertRtolEqual(npu_output.cpu().numpy(), cpu_output.numpy()) | 75 | self.assertRtolEqual(npu_output.cpu().numpy(), cpu_output.numpy()) |
| 76 | 76 | ||
| 77 | def test_d2h_copy_different_dtype(self): | 77 | def test_d2h_copy_different_dtype(self): |
| 78 | dtype_pairs = [ | 78 | dtype_pairs = [ |
| 79 | (np.float16, torch.float32), | 79 | (np.float16, torch.float32), |
| 80 | (np.float32, torch.float16), | 80 | (np.float32, torch.float16), |
| 81 | ] | 81 | ] |
| 82 | shape = [32, 64] | 82 | shape = [32, 64] |
| 83 | 83 | ||
| 84 | for src_dtype, dst_dtype in dtype_pairs: | 84 | for src_dtype, dst_dtype in dtype_pairs: |
| 85 | cpu_input, npu_input = create_common_tensor([src_dtype, 0, shape], -100, 100) | 85 | cpu_input, npu_input = create_common_tensor([src_dtype, 0, shape], -100, 100) |
| 86 | cpu_output = npu_input.cpu().to(dst_dtype) | 86 | cpu_output = npu_input.cpu().to(dst_dtype) |
| 87 | 87 | ||
| 88 | expected = cpu_input.to(dst_dtype) | 88 | expected = cpu_input.to(dst_dtype) |
| 89 | self.assertRtolEqual(cpu_output.numpy(), expected.numpy()) | 89 | self.assertRtolEqual(cpu_output.numpy(), expected.numpy()) |
| 90 | 90 | ||
| 91 | def test_h2d_copy_slice_tensor(self): | 91 | def test_h2d_copy_slice_tensor(self): |
| 92 | shape = [64, 128] | 92 | shape = [64, 128] |
| 93 | cpu_input = torch.randn(shape) | 93 | cpu_input = torch.randn(shape) |
| 94 | 94 | ||
| 95 | cpu_slice = cpu_input[10:30, 20:60] | 95 | cpu_slice = cpu_input[10:30, 20:60] |
| 96 | npu_slice = cpu_slice.npu() | 96 | npu_slice = cpu_slice.npu() |
| 97 | 97 | ||
| 98 | npu_contiguous = npu_slice.contiguous() | 98 | npu_contiguous = npu_slice.contiguous() |
| 99 | self.assertRtolEqual(npu_contiguous.cpu().numpy(), cpu_slice.contiguous().numpy()) | 99 | self.assertRtolEqual(npu_contiguous.cpu().numpy(), cpu_slice.contiguous().numpy()) |
| 100 | 100 | ||
| 101 | def test_d2h_copy_slice_tensor(self): | 101 | def test_d2h_copy_slice_tensor(self): |
| 102 | shape = [64, 128] | 102 | shape = [64, 128] |
| 103 | cpu_input = torch.randn(shape) | 103 | cpu_input = torch.randn(shape) |
| 104 | npu_input = cpu_input.npu() | 104 | npu_input = cpu_input.npu() |
| 105 | 105 | ||
| 106 | npu_slice = npu_input[10:30, 20:60] | 106 | npu_slice = npu_input[10:30, 20:60] |
| 107 | cpu_slice = npu_slice.cpu() | 107 | cpu_slice = npu_slice.cpu() |
| 108 | self.assertRtolEqual(cpu_slice.numpy(), cpu_input[10:30, 20:60].contiguous().numpy()) | 108 | self.assertRtolEqual(cpu_slice.numpy(), cpu_input[10:30, 20:60].contiguous().numpy()) |
| 109 | 109 | ||
| 110 | def test_d2h_copy_broadcast_tensor(self): | 110 | def test_d2h_copy_broadcast_tensor(self): |
| 111 | shape = [1, 64, 1] | 111 | shape = [1, 64, 1] |
| 112 | cpu_input = torch.randn(shape) | 112 | cpu_input = torch.randn(shape) |
| 113 | npu_input = cpu_input.npu() | 113 | npu_input = cpu_input.npu() |
| 114 | 114 | ||
| 115 | npu_broadcast = npu_input.expand(4, 64, 128) | 115 | npu_broadcast = npu_input.expand(4, 64, 128) |
| 116 | cpu_output = npu_broadcast.cpu() | 116 | cpu_output = npu_broadcast.cpu() |
| 117 | self.assertRtolEqual(cpu_output.numpy(), cpu_input.expand(4, 64, 128).contiguous().numpy()) | 117 | self.assertRtolEqual(cpu_output.numpy(), cpu_input.expand(4, 64, 128).contiguous().numpy()) |
| 118 | 118 | ||
| 119 | def test_h2d_copy_permute_tensor(self): | 119 | def test_h2d_copy_permute_tensor(self): |
| 120 | shape = [32, 64, 128] | 120 | shape = [32, 64, 128] |
| 121 | cpu_input = torch.randn(shape) | 121 | cpu_input = torch.randn(shape) |
| 122 | cpu_permuted = cpu_input.permute(2, 0, 1) | 122 | cpu_permuted = cpu_input.permute(2, 0, 1) |
| 123 | npu_permuted = cpu_permuted.npu() | 123 | npu_permuted = cpu_permuted.npu() |
| 124 | npu_contiguous = npu_permuted.contiguous() | 124 | npu_contiguous = npu_permuted.contiguous() |
| 125 | self.assertRtolEqual(npu_contiguous.cpu().numpy(), cpu_permuted.contiguous().numpy()) | 125 | self.assertRtolEqual(npu_contiguous.cpu().numpy(), cpu_permuted.contiguous().numpy()) |
| 126 | 126 | ||
| 127 | def test_d2h_copy_permute_tensor(self): | 127 | def test_d2h_copy_permute_tensor(self): |
| 128 | shape = [32, 64, 128] | 128 | shape = [32, 64, 128] |
| 129 | cpu_input = torch.randn(shape) | 129 | cpu_input = torch.randn(shape) |
| 130 | npu_input = cpu_input.npu() | 130 | npu_input = cpu_input.npu() |
| 131 | 131 | ||
| 132 | npu_permuted = npu_input.permute(2, 0, 1) | 132 | npu_permuted = npu_input.permute(2, 0, 1) |
| 133 | cpu_output = npu_permuted.cpu() | 133 | cpu_output = npu_permuted.cpu() |
| 134 | self.assertRtolEqual(cpu_output.numpy(), cpu_input.permute(2, 0, 1).contiguous().numpy()) | 134 | self.assertRtolEqual(cpu_output.numpy(), cpu_input.permute(2, 0, 1).contiguous().numpy()) |
| 135 | 135 | ||
| 136 | 136 | ||
| 137 | if __name__ == "__main__": | 137 | if __name__ == "__main__": |
| 138 | run_tests() | 138 | run_tests() |
| @@ -1,105 +1,105 @@ | |||
| 1 | import os | 1 | import os |
| 2 | import gc | 2 | import gc |
| 3 | import unittest | 3 | import unittest |
| 4 | 4 | ||
| 5 | import torch | 5 | import torch |
| 6 | import torch_npu | 6 | import torch_npu |
| 7 | from torch_npu.testing.testcase import TestCase, run_tests | 7 | from torch_npu.testing.testcase import TestCase, run_tests |
| 8 | from torch.testing._internal.common_utils import TestCase, run_tests, TEST_PRIVATEUSE1 | 8 | from torch.testing._internal.common_utils import TestCase, run_tests, TEST_PRIVATEUSE1 |
| 9 | 9 | ||
| 10 | os.environ["PYTORCH_NPU_ALLOC_CONF"] = "expandable_segments:True" | 10 | os.environ["PYTORCH_NPU_ALLOC_CONF"] = "expandable_segments:True" |
| 11 | 11 | ||
| 12 | device_name = torch_npu.npu.get_device_name(0) | 12 | device_name = torch_npu.npu.get_device_name(0) |
| 13 | 13 | ||
| 14 | 14 | ||
| 15 | class Test_expandable_segments(TestCase): | 15 | class Test_expandable_segments(TestCase): |
| 16 | @unittest.skipIf(device_name == "Ascend910B4", "Skip when device name is Ascend910B4") | 16 | @unittest.skipIf(device_name == "Ascend910B4", "Skip when device name is Ascend910B4") |
| 17 | def test_empty_virt_addr_cache(self): | 17 | def test_empty_virt_addr_cache(self): |
| 18 | gc.collect() | 18 | gc.collect() |
| 19 | torch_npu.npu.empty_cache() | 19 | torch_npu.npu.empty_cache() |
| 20 | prev = 0 | 20 | prev = 0 |
| 21 | 21 | ||
| 22 | x = torch.empty((7500, 1024, 1024), device="npu") | 22 | x = torch.empty((7500, 1024, 1024), device="npu") |
| 23 | del x | 23 | del x |
| 24 | last_r = torch_npu.npu.memory_reserved() | 24 | last_r = torch_npu.npu.memory_reserved() |
| 25 | 25 | ||
| 26 | torch_npu.npu.empty_virt_addr_cache() | 26 | torch_npu.npu.empty_virt_addr_cache() |
| 27 | new_r = torch_npu.npu.memory_reserved() | 27 | new_r = torch_npu.npu.memory_reserved() |
| 28 | self.assertEqual(new_r, prev) | 28 | self.assertEqual(new_r, prev) |
| 29 | self.assertEqual(torch_npu.npu.max_memory_reserved(), last_r) | 29 | self.assertEqual(torch_npu.npu.max_memory_reserved(), last_r) |
| 30 | 30 | ||
| 31 | # test re-alloc after empty virtual address | 31 | # test re-alloc after empty virtual address |
| 32 | try: | 32 | try: |
| 33 | y = torch.empty((7500, 1024, 1024), device="npu") | 33 | y = torch.empty((7500, 1024, 1024), device="npu") |
| 34 | self.assertGreater(torch_npu.npu.memory_allocated(), prev) | 34 | self.assertGreater(torch_npu.npu.memory_allocated(), prev) |
| 35 | finally: | 35 | finally: |
| 36 | if y is not None: | 36 | if y is not None: |
| 37 | del y | 37 | del y |
| 38 | self.assertEqual(torch_npu.npu.memory_allocated(), prev) | 38 | self.assertEqual(torch_npu.npu.memory_allocated(), prev) |
| 39 | torch_npu.npu.empty_virt_addr_cache() | 39 | torch_npu.npu.empty_virt_addr_cache() |
| 40 | # empty unmapped physical handles with empty_cache() | 40 | # empty unmapped physical handles with empty_cache() |
| 41 | torch_npu.npu.empty_cache() | 41 | torch_npu.npu.empty_cache() |
| 42 | self.assertEqual(torch_npu.npu.memory_reserved(), prev) | 42 | self.assertEqual(torch_npu.npu.memory_reserved(), prev) |
| 43 | 43 | ||
| 44 | @unittest.skipIf(TEST_PRIVATEUSE1, "NPU not available for graph capture") | 44 | @unittest.skipIf(TEST_PRIVATEUSE1, "NPU not available for graph capture") |
| 45 | def test_set_segment_state_to_checkpoint_when_expandable_segments(self): | 45 | def test_set_segment_state_to_checkpoint_when_expandable_segments(self): |
| 46 | def tensor_metadata(x): | 46 | def tensor_metadata(x): |
| 47 | return { | 47 | return { |
| 48 | "nbytes": x.untyped_storage().nbytes(), | 48 | "nbytes": x.untyped_storage().nbytes(), |
| 49 | "data_ptr": x.untyped_storage().data_ptr(), | 49 | "data_ptr": x.untyped_storage().data_ptr(), |
| 50 | "size": x.shape, | 50 | "size": x.shape, |
| 51 | "stride": x.stride(), | 51 | "stride": x.stride(), |
| 52 | "dtype": x.dtype, | 52 | "dtype": x.dtype, |
| 53 | "device": x.device, | 53 | "device": x.device, |
| 54 | "storage_offset": x.storage_offset(), } | 54 | "storage_offset": x.storage_offset(), } |
| 55 | 55 | ||
| 56 | def reconstruct_from_tensor_metadata(metadata): | 56 | def reconstruct_from_tensor_metadata(metadata): |
| 57 | s = torch._C._construct_storage_from_data_pointer( | 57 | s = torch._C._construct_storage_from_data_pointer( |
| 58 | metadata["data_ptr"], metadata["device"], metadata["nbytes"]) | 58 | metadata["data_ptr"], metadata["device"], metadata["nbytes"]) |
| 59 | t = torch.empty([0], device=metadata["device"], dtype=metadata["dtype"]) | 59 | t = torch.empty([0], device=metadata["device"], dtype=metadata["dtype"]) |
| 60 | t.set_(source=s, storage_offset=metadata["storage_offset"], | 60 | t.set_(source=s, storage_offset=metadata["storage_offset"], |
| 61 | size=metadata["size"], stride=metadata["stride"], ) | 61 | size=metadata["size"], stride=metadata["stride"], ) |
| 62 | return t | 62 | return t |
| 63 | 63 | ||
| 64 | def cudagraphify(fn, inputs, pool, stream): | 64 | def cudagraphify(fn, inputs, pool, stream): |
| 65 | torch.npu.synchronize() | 65 | torch.npu.synchronize() |
| 66 | gc.collect() | 66 | gc.collect() |
| 67 | torch.npu.empty_cache() | 67 | torch.npu.empty_cache() |
| 68 | 68 | ||
| 69 | graph = torch.npu.NPUGraph() | 69 | graph = torch.npu.NPUGraph() |
| 70 | with torch.npu.graph(graph, stream=stream, pool=pool): | 70 | with torch.npu.graph(graph, stream=stream, pool=pool): |
| 71 | static_outputs = fn(*inputs) | 71 | static_outputs = fn(*inputs) |
| 72 | return graph, static_outputs | 72 | return graph, static_outputs |
| 73 | 73 | ||
| 74 | def foo(x, idx): | 74 | def foo(x, idx): |
| 75 | r1 = x.expand([1, 2097152 // 8]).sqrt() | 75 | r1 = x.expand([1, 2097152 // 8]).sqrt() |
| 76 | r2 = x.expand([idx, 2097152]).clone() | 76 | r2 = x.expand([idx, 2097152]).clone() |
| 77 | return r1, r2 | 77 | return r1, r2 |
| 78 | 78 | ||
| 79 | # init | 79 | # init |
| 80 | pool_id = torch.npu.graph_pool_handle() | 80 | pool_id = torch.npu.graph_pool_handle() |
| 81 | com_stream = torch.npu.Stream() | 81 | com_stream = torch.npu.Stream() |
| 82 | com_device = torch_npu.npu.current_device() | 82 | com_device = torch_npu.npu.current_device() |
| 83 | inp = torch.tensor([7]).npu() | 83 | inp = torch.tensor([7]).npu() |
| 84 | 84 | ||
| 85 | # start capture graph1 | 85 | # start capture graph1 |
| 86 | graph1, outputs1 = cudagraphify(foo, [inp, 1], pool=pool_id, stream=com_stream) | 86 | graph1, outputs1 = cudagraphify(foo, [inp, 1], pool=pool_id, stream=com_stream) |
| 87 | graph1_state = torch_npu._C._npu_getCheckpointState(com_device, pool_id) | 87 | graph1_state = torch_npu._C._npu_getCheckpointState(com_device, pool_id) |
| 88 | output1_metadata = [tensor_metadata(t) for t in outputs1] | 88 | output1_metadata = [tensor_metadata(t) for t in outputs1] |
| 89 | outputs1 = None | 89 | outputs1 = None |
| 90 | 90 | ||
| 91 | # start capture graph2 | 91 | # start capture graph2 |
| 92 | graph2, outputs2 = cudagraphify(foo, [inp, 2], pool=pool_id, stream=com_stream) | 92 | graph2, outputs2 = cudagraphify(foo, [inp, 2], pool=pool_id, stream=com_stream) |
| 93 | graph2_state = torch_npu._C._npu_getCheckpointState(com_device, pool_id) | 93 | graph2_state = torch_npu._C._npu_getCheckpointState(com_device, pool_id) |
| 94 | graph2.replay() | 94 | graph2.replay() |
| 95 | outputs2 = None | 95 | outputs2 = None |
| 96 | 96 | ||
| 97 | # replay graph1 | 97 | # replay graph1 |
| 98 | graph1.replay() | 98 | graph1.replay() |
| 99 | reconstructed_tensors1 = [reconstruct_from_tensor_metadata(metadata) for metadata in output1_metadata] | 99 | reconstructed_tensors1 = [reconstruct_from_tensor_metadata(metadata) for metadata in output1_metadata] |
| 100 | output1_new_storage = [output.untyped_storage()._cdata for output in reconstructed_tensors1] | 100 | output1_new_storage = [output.untyped_storage()._cdata for output in reconstructed_tensors1] |
| 101 | torch_npu._C._npu_setCheckpointPoolState(com_device, graph1_state, [], output1_new_storage) | 101 | torch_npu._C._npu_setCheckpointPoolState(com_device, graph1_state, [], output1_new_storage) |
| 102 | 102 | ||
| 103 | 103 | ||
| 104 | if __name__ == '__main__': | 104 | if __name__ == '__main__': |
| 105 | run_tests() | 105 | run_tests() |
| @@ -118,4 +118,3 @@ class TestKernelCheck(TestCase): | |||
| 118 | 118 | ||
| 119 | if __name__ == "__main__": | 119 | if __name__ == "__main__": |
| 120 | run_tests() | 120 | run_tests() |
| 121 | |||
| @@ -1,19 +1,19 @@ | |||
| 1 | import unittest | 1 | import unittest |
| 2 | 2 | ||
| 3 | import os | 3 | import os |
| 4 | import torch | 4 | import torch |
| 5 | from torch.testing._internal.common_utils import TestCase, run_tests | 5 | from torch.testing._internal.common_utils import TestCase, run_tests |
| 6 | from torch_npu.npu.utils import get_cann_version | 6 | from torch_npu.npu.utils import get_cann_version |
| 7 | 7 | ||
| 8 | 8 | ||
| 9 | class TestPinMemoryHostRegister(TestCase): | 9 | class TestPinMemoryHostRegister(TestCase): |
| 10 | 10 | ||
| 11 | @unittest.skipUnless(get_cann_version("RUNTIME") >= "8.5.0" and get_cann_version(module="DRIVER") >= "25.5.0", "This feature is not supported in older versions.") | 11 | @unittest.skipUnless(get_cann_version("RUNTIME") >= "8.5.0" and get_cann_version(module="DRIVER") >= "25.5.0", "This feature is not supported in older versions.") |
| 12 | def test_pin_memory_host_register(self): | 12 | def test_pin_memory_host_register(self): |
| 13 | os.environ["PYTORCH_NPU_ALLOC_CONF"] = "pinned_mem_register:True" | 13 | os.environ["PYTORCH_NPU_ALLOC_CONF"] = "pinned_mem_register:True" |
| 14 | cpu_tensor = torch.ones([2, 3]) | 14 | cpu_tensor = torch.ones([2, 3]) |
| 15 | pin_tensor = cpu_tensor.pin_memory() | 15 | pin_tensor = cpu_tensor.pin_memory() |
| 16 | self.assertTrue(pin_tensor.is_pinned()) | 16 | self.assertTrue(pin_tensor.is_pinned()) |
| 17 | 17 | ||
| 18 | if __name__ == '__main__': | 18 | if __name__ == '__main__': |
| 19 | run_tests() | 19 | run_tests() |
| @@ -1,119 +1,119 @@ | |||
| 1 | import os | 1 | import os |
| 2 | import time | 2 | import time |
| 3 | import copy | 3 | import copy |
| 4 | 4 | ||
| 5 | import torch | 5 | import torch |
| 6 | import torch.nn as nn | 6 | import torch.nn as nn |
| 7 | import torch.optim as optim | 7 | import torch.optim as optim |
| 8 | 8 | ||
| 9 | import torch_npu | 9 | import torch_npu |
| 10 | from torch_npu.testing.testcase import TestCase, run_tests | 10 | from torch_npu.testing.testcase import TestCase, run_tests |
| 11 | from torch_npu.utils._path_manager import PathManager | 11 | from torch_npu.utils._path_manager import PathManager |
| 12 | 12 | ||
| 13 | 13 | ||
| 14 | class TestAsyncSave(TestCase): | 14 | class TestAsyncSave(TestCase): |
| 15 | test_save_path = os.path.join( | 15 | test_save_path = os.path.join( |
| 16 | os.path.realpath(os.path.dirname(__file__)), "test_save_async") | 16 | os.path.realpath(os.path.dirname(__file__)), "test_save_async") |
| 17 | 17 | ||
| 18 | @classmethod | 18 | @classmethod |
| 19 | def setUpClass(cls): | 19 | def setUpClass(cls): |
| 20 | PathManager.make_dir_safety(TestAsyncSave.test_save_path) | 20 | PathManager.make_dir_safety(TestAsyncSave.test_save_path) |
| 21 | 21 | ||
| 22 | @classmethod | 22 | @classmethod |
| 23 | def tearDownClass(cls): | 23 | def tearDownClass(cls): |
| 24 | PathManager.remove_path_safety(TestAsyncSave.test_save_path) | 24 | PathManager.remove_path_safety(TestAsyncSave.test_save_path) |
| 25 | 25 | ||
| 26 | def wait_for_save_completion(self, file_path, timeout_sec=60, poll_interval_sec=0.5): | 26 | def wait_for_save_completion(self, file_path, timeout_sec=60, poll_interval_sec=0.5): |
| 27 | start_time = time.time() | 27 | start_time = time.time() |
| 28 | 28 | ||
| 29 | while time.time() - start_time < timeout_sec: | 29 | while time.time() - start_time < timeout_sec: |
| 30 | if os.path.exists(file_path): | 30 | if os.path.exists(file_path): |
| 31 | current_size = os.path.getsize(file_path) | 31 | current_size = os.path.getsize(file_path) |
| 32 | time.sleep(poll_interval_sec) | 32 | time.sleep(poll_interval_sec) |
| 33 | new_size = os.path.getsize(file_path) | 33 | new_size = os.path.getsize(file_path) |
| 34 | 34 | ||
| 35 | if current_size == new_size: | 35 | if current_size == new_size: |
| 36 | return True | 36 | return True |
| 37 | else: | 37 | else: |
| 38 | time.sleep(poll_interval_sec) | 38 | time.sleep(poll_interval_sec) |
| 39 | 39 | ||
| 40 | return False | 40 | return False |
| 41 | 41 | ||
| 42 | def test_save_async_tensor(self): | 42 | def test_save_async_tensor(self): |
| 43 | save_tensor = torch.rand(1024, dtype=torch.float32).npu() | 43 | save_tensor = torch.rand(1024, dtype=torch.float32).npu() |
| 44 | async_save_path = os.path.join(TestAsyncSave.test_save_path, "async_save_tensor.pt") | 44 | async_save_path = os.path.join(TestAsyncSave.test_save_path, "async_save_tensor.pt") |
| 45 | torch_npu.utils.save_async(save_tensor, async_save_path) | 45 | torch_npu.utils.save_async(save_tensor, async_save_path) |
| 46 | 46 | ||
| 47 | if self.wait_for_save_completion(async_save_path): | 47 | if self.wait_for_save_completion(async_save_path): |
| 48 | tensor_async = torch.load(async_save_path, weights_only=False) | 48 | tensor_async = torch.load(async_save_path, weights_only=False) |
| 49 | self.assertEqual(tensor_async, save_tensor) | 49 | self.assertEqual(tensor_async, save_tensor) |
| 50 | else: | 50 | else: |
| 51 | self.assertTrue(False, f"{async_save_path} is not exist!") | 51 | self.assertTrue(False, f"{async_save_path} is not exist!") |
| 52 | 52 | ||
| 53 | def test_save_async(self): | 53 | def test_save_async(self): |
| 54 | loss1 = [1.6099495, 1.6099086, 1.6098710] | 54 | loss1 = [1.6099495, 1.6099086, 1.6098710] |
| 55 | loss2 = [] | 55 | loss2 = [] |
| 56 | model_list = [] | 56 | model_list = [] |
| 57 | checkpoint_list = [] | 57 | checkpoint_list = [] |
| 58 | model_origin = nn.Sequential( | 58 | model_origin = nn.Sequential( |
| 59 | nn.Linear(100, 50), | 59 | nn.Linear(100, 50), |
| 60 | nn.ReLU(), | 60 | nn.ReLU(), |
| 61 | nn.Linear(50, 20), | 61 | nn.Linear(50, 20), |
| 62 | nn.ReLU(), | 62 | nn.ReLU(), |
| 63 | nn.Linear(20, 5), | 63 | nn.Linear(20, 5), |
| 64 | nn.ReLU() | 64 | nn.ReLU() |
| 65 | ) | 65 | ) |
| 66 | 66 | ||
| 67 | input_data = torch.ones(6400, 100).npu() | 67 | input_data = torch.ones(6400, 100).npu() |
| 68 | labels = torch.arange(5).repeat(1280).npu() | 68 | labels = torch.arange(5).repeat(1280).npu() |
| 69 | 69 | ||
| 70 | criterion = nn.CrossEntropyLoss() | 70 | criterion = nn.CrossEntropyLoss() |
| 71 | model = model_origin.npu() | 71 | model = model_origin.npu() |
| 72 | optimerizer = optim.SGD(model.parameters(), lr=0.1) | 72 | optimerizer = optim.SGD(model.parameters(), lr=0.1) |
| 73 | for step in range(3): | 73 | for step in range(3): |
| 74 | outputs = model(input_data) | 74 | outputs = model(input_data) |
| 75 | loss = criterion(outputs, labels) | 75 | loss = criterion(outputs, labels) |
| 76 | 76 | ||
| 77 | optimerizer.zero_grad() | 77 | optimerizer.zero_grad() |
| 78 | loss.backward() | 78 | loss.backward() |
| 79 | 79 | ||
| 80 | optimerizer.step() | 80 | optimerizer.step() |
| 81 | 81 | ||
| 82 | loss2.append(loss) | 82 | loss2.append(loss) |
| 83 | checkpoint = { | 83 | checkpoint = { |
| 84 | "model": model.state_dict(), | 84 | "model": model.state_dict(), |
| 85 | "optimizer": optimerizer.state_dict() | 85 | "optimizer": optimerizer.state_dict() |
| 86 | } | 86 | } |
| 87 | checkpoint_list.append(copy.deepcopy(checkpoint)) | 87 | checkpoint_list.append(copy.deepcopy(checkpoint)) |
| 88 | model_list.append(copy.deepcopy(model)) | 88 | model_list.append(copy.deepcopy(model)) |
| 89 | checkpoint_async_path = os.path.join(TestAsyncSave.test_save_path, f"checkpoint_async_{step}.path") | 89 | checkpoint_async_path = os.path.join(TestAsyncSave.test_save_path, f"checkpoint_async_{step}.path") |
| 90 | model_async_path = os.path.join(TestAsyncSave.test_save_path, f"model_async_{step}.path") | 90 | model_async_path = os.path.join(TestAsyncSave.test_save_path, f"model_async_{step}.path") |
| 91 | torch_npu.utils.save_async(checkpoint, checkpoint_async_path, model=model) | 91 | torch_npu.utils.save_async(checkpoint, checkpoint_async_path, model=model) |
| 92 | torch_npu.utils.save_async(model, model_async_path, model=model) | 92 | torch_npu.utils.save_async(model, model_async_path, model=model) |
| 93 | 93 | ||
| 94 | for i in range(3): | 94 | for i in range(3): |
| 95 | self.assertEqual(loss1[i], loss2[i].item()) | 95 | self.assertEqual(loss1[i], loss2[i].item()) |
| 96 | checkpoint_async_path = os.path.join(TestAsyncSave.test_save_path, f"checkpoint_async_{i}.path") | 96 | checkpoint_async_path = os.path.join(TestAsyncSave.test_save_path, f"checkpoint_async_{i}.path") |
| 97 | if self.wait_for_save_completion(checkpoint_async_path): | 97 | if self.wait_for_save_completion(checkpoint_async_path): |
| 98 | checkpoint_async = torch.load(checkpoint_async_path, weights_only=False) | 98 | checkpoint_async = torch.load(checkpoint_async_path, weights_only=False) |
| 99 | self.assertEqual(checkpoint_list[i], checkpoint_async, prec=2e-3) | 99 | self.assertEqual(checkpoint_list[i], checkpoint_async, prec=2e-3) |
| 100 | else: | 100 | else: |
| 101 | self.assertTrue(False, f"{checkpoint_async_path} is not exist!") | 101 | self.assertTrue(False, f"{checkpoint_async_path} is not exist!") |
| 102 | model_async_path = os.path.join(TestAsyncSave.test_save_path, f"model_async_{i}.path") | 102 | model_async_path = os.path.join(TestAsyncSave.test_save_path, f"model_async_{i}.path") |
| 103 | if self.wait_for_save_completion(model_async_path): | 103 | if self.wait_for_save_completion(model_async_path): |
| 104 | model_async = torch.load(model_async_path, weights_only=False) | 104 | model_async = torch.load(model_async_path, weights_only=False) |
| 105 | else: | 105 | else: |
| 106 | self.assertTrue(False, f"{model_async_path} is not exist!") | 106 | self.assertTrue(False, f"{model_async_path} is not exist!") |
| 107 | state_dict_sync = model_list[i].state_dict() | 107 | state_dict_sync = model_list[i].state_dict() |
| 108 | state_dict_async = model_async.state_dict() | 108 | state_dict_async = model_async.state_dict() |
| 109 | 109 | ||
| 110 | key_sync = sorted(state_dict_sync.keys()) | 110 | key_sync = sorted(state_dict_sync.keys()) |
| 111 | key_async = sorted(state_dict_async.keys()) | 111 | key_async = sorted(state_dict_async.keys()) |
| 112 | 112 | ||
| 113 | self.assertEqual(key_sync, key_async) | 113 | self.assertEqual(key_sync, key_async) |
| 114 | for key in key_async: | 114 | for key in key_async: |
| 115 | self.assertEqual(state_dict_async[key], state_dict_sync[key], prec=2e-3) | 115 | self.assertEqual(state_dict_async[key], state_dict_sync[key], prec=2e-3) |
| 116 | 116 | ||
| 117 | if __name__ == '__main__': | 117 | if __name__ == '__main__': |
| 118 | torch.npu.set_device(0) | 118 | torch.npu.set_device(0) |
| 119 | run_tests() | 119 | run_tests() |
| @@ -81,4 +81,3 @@ class TestProfilerConfig(TestCase): | |||
| 81 | 81 | ||
| 82 | if __name__ == "__main__": | 82 | if __name__ == "__main__": |
| 83 | run_tests() | 83 | run_tests() |
| 84 | |||
| @@ -39,4 +39,3 @@ class TestNoneInstrusiveProfile(TestCase): | |||
| 39 | 39 | ||
| 40 | if __name__ == "__main__": | 40 | if __name__ == "__main__": |
| 41 | run_tests() | 41 | run_tests() |
| 42 | |||
| @@ -32154,5 +32154,7 @@ | |||
| 32154 | "test_to_complex_npu (__main__.TestNNDeviceTypePRIVATEUSE1)": ["", [""]], | 32154 | "test_to_complex_npu (__main__.TestNNDeviceTypePRIVATEUSE1)": ["", [""]], |
| 32155 | "test_npu_roi_align_1 (__main__.TestPsRoiPooling)": ["", [""]], | 32155 | "test_npu_roi_align_1 (__main__.TestPsRoiPooling)": ["", [""]], |
| 32156 | "test_silu (__main__.TestActivations)": ["", [""]], | 32156 | "test_silu (__main__.TestActivations)": ["", [""]], |
| 32157 | "test_codegen_upcast_to_fp32_emits_cast_bfloat16_upcast_flag_True (__main__.TestCodegenUpcastToFP32)": ["", [""]] | 32157 | "test_codegen_upcast_to_fp32_emits_cast_bfloat16_upcast_flag_True (__main__.TestCodegenUpcastToFP32)": ["", [""]], |
| 32158 | "test_data_parallel_rnn (__main__.TestDataParallel)": ["", ["Disabled during A1 to A2 chip transition"]], | ||
| 32159 | "test_alltoall_single_2p_size_dist (__main__.HcclAlltoAllSingleTest)": ["", ["Disabled during A1 to A2 chip transition"]] | ||
| 32158 | } | 32160 | } |
| @@ -1,523 +1,523 @@ | |||
| 1 | from itertools import count | 1 | from itertools import count |
| 2 | from typing import List, Union, Optional, Tuple, Any, Dict | 2 | from typing import List, Union, Optional, Tuple, Any, Dict |
| 3 | import os | 3 | import os |
| 4 | import sympy | 4 | import sympy |
| 5 | import textwrap | 5 | import textwrap |
| 6 | 6 | ||
| 7 | import torch | 7 | import torch |
| 8 | import torch.fx | 8 | import torch.fx |
| 9 | 9 | ||
| 10 | from torch._functorch.aot_autograd import set_model_name, get_aot_compilation_context | 10 | from torch._functorch.aot_autograd import set_model_name, get_aot_compilation_context |
| 11 | from torch._inductor.codegen.simd import ( | 11 | from torch._inductor.codegen.simd import ( |
| 12 | log, | 12 | log, |
| 13 | SIMDKernel, | 13 | SIMDKernel, |
| 14 | SIMDKernelFeatures, | 14 | SIMDKernelFeatures, |
| 15 | MultiKernel, | 15 | MultiKernel, |
| 16 | code_hash | 16 | code_hash |
| 17 | ) | 17 | ) |
| 18 | from torch._inductor.codegen.triton import ( | 18 | from torch._inductor.codegen.triton import ( |
| 19 | SIMDScheduling, | 19 | SIMDScheduling, |
| 20 | FixedTritonConfig, | 20 | FixedTritonConfig, |
| 21 | ) | 21 | ) |
| 22 | from torch._inductor import config, ir, scheduler, metrics | 22 | from torch._inductor import config, ir, scheduler, metrics |
| 23 | from torch._inductor.codecache import get_path | 23 | from torch._inductor.codecache import get_path |
| 24 | from torch._dynamo.utils import counters | 24 | from torch._dynamo.utils import counters |
| 25 | from torch._inductor.codegen.common import ( | 25 | from torch._inductor.codegen.common import ( |
| 26 | IndentedBuffer, | 26 | IndentedBuffer, |
| 27 | Kernel, | 27 | Kernel, |
| 28 | ) | 28 | ) |
| 29 | from torch._inductor.virtualized import V | 29 | from torch._inductor.virtualized import V |
| 30 | from torch._inductor.codegen.triton import ( | 30 | from torch._inductor.codegen.triton import ( |
| 31 | TritonKernel | 31 | TritonKernel |
| 32 | ) | 32 | ) |
| 33 | from torch._inductor.utils import ( | 33 | from torch._inductor.utils import ( |
| 34 | get_fused_kernel_name, | 34 | get_fused_kernel_name, |
| 35 | get_kernel_metadata, | 35 | get_kernel_metadata, |
| 36 | ) | 36 | ) |
| 37 | from torch._inductor.scheduler import Scheduler | 37 | from torch._inductor.scheduler import Scheduler |
| 38 | from torch_mlir.compiler_utils import OutputType | 38 | from torch_mlir.compiler_utils import OutputType |
| 39 | 39 | ||
| 40 | from torch.fx.experimental.proxy_tensor import make_fx | 40 | from torch.fx.experimental.proxy_tensor import make_fx |
| 41 | from torch._dynamo.device_interface import get_interface_for_device | 41 | from torch._dynamo.device_interface import get_interface_for_device |
| 42 | from ..torch_mlir_patch import stateless_fx_import | 42 | from ..torch_mlir_patch import stateless_fx_import |
| 43 | from ...npu.inductor_patch.lowering import map_strings_to_operators | 43 | from ...npu.inductor_patch.lowering import map_strings_to_operators |
| 44 | from ...npu.utils import ( | 44 | from ...npu.utils import ( |
| 45 | parse_fx_example_inputs, | 45 | parse_fx_example_inputs, |
| 46 | npu_cast_to_prim_cast, | 46 | npu_cast_to_prim_cast, |
| 47 | get_fx_graph_code, | 47 | get_fx_graph_code, |
| 48 | scalarize_tensor_ops_on_scalars, | 48 | scalarize_tensor_ops_on_scalars, |
| 49 | to_folder, | 49 | to_folder, |
| 50 | modify_gm_for_acc_comp, | 50 | modify_gm_for_acc_comp, |
| 51 | get_num_call_functions, | 51 | get_num_call_functions, |
| 52 | is_fx_dynamic, | 52 | is_fx_dynamic, |
| 53 | view_to_reshape | 53 | view_to_reshape |
| 54 | ) | 54 | ) |
| 55 | from ... import config as anir_config | 55 | from ... import config as anir_config |
| 56 | from ...npu.inductor_patch.lowering import merge_fx_graphs | 56 | from ...npu.inductor_patch.lowering import merge_fx_graphs |
| 57 | 57 | ||
| 58 | 58 | ||
| 59 | id_iter = count() | 59 | id_iter = count() |
| 60 | 60 | ||
| 61 | 61 | ||
| 62 | class NpuTritonKernel(TritonKernel): | 62 | class NpuTritonKernel(TritonKernel): |
| 63 | def __init__(self, | 63 | def __init__(self, |
| 64 | tiling: Dict[str, sympy.Expr], | 64 | tiling: Dict[str, sympy.Expr], |
| 65 | min_elem_per_thread=0, | 65 | min_elem_per_thread=0, |
| 66 | optimize_mask=True, | 66 | optimize_mask=True, |
| 67 | fixed_config: Optional[FixedTritonConfig] = None, | 67 | fixed_config: Optional[FixedTritonConfig] = None, |
| 68 | **kwargs, | 68 | **kwargs, |
| 69 | ): | 69 | ): |
| 70 | super().__init__( | 70 | super().__init__( |
| 71 | tiling, | 71 | tiling, |
| 72 | min_elem_per_thread=min_elem_per_thread, | 72 | min_elem_per_thread=min_elem_per_thread, |
| 73 | optimize_mask=optimize_mask, | 73 | optimize_mask=optimize_mask, |
| 74 | fixed_config = fixed_config, | 74 | fixed_config = fixed_config, |
| 75 | **kwargs, | 75 | **kwargs, |
| 76 | ) | 76 | ) |
| 77 | 77 | ||
| 78 | @staticmethod | 78 | @staticmethod |
| 79 | def inductor_meta_common(): | 79 | def inductor_meta_common(): |
| 80 | return {} | 80 | return {} |
| 81 | 81 | ||
| 82 | def call_kernel(self, call_args, name: str): | 82 | def call_kernel(self, call_args, name: str): |
| 83 | wrapper = V.graph.wrapper_code | 83 | wrapper = V.graph.wrapper_code |
| 84 | for call_arg in call_args: | 84 | for call_arg in call_args: |
| 85 | if call_arg.startswith('_uwu_'): | 85 | if call_arg.startswith('_uwu_'): |
| 86 | expression = map_strings_to_operators(call_arg) | 86 | expression = map_strings_to_operators(call_arg) |
| 87 | wrapper.writeline(f'{call_arg} = {expression}') | 87 | wrapper.writeline(f'{call_arg} = {expression}') |
| 88 | if len(call_args) > 0: | 88 | if len(call_args) > 0: |
| 89 | wrapper.generate_kernel_call( | 89 | wrapper.generate_kernel_call( |
| 90 | name, | 90 | name, |
| 91 | call_args, | 91 | call_args, |
| 92 | ) | 92 | ) |
| 93 | 93 | ||
| 94 | 94 | ||
| 95 | def _nc_key(nc): | 95 | def _nc_key(nc): |
| 96 | if isinstance(nc, dict): | 96 | if isinstance(nc, dict): |
| 97 | return (tuple(nc.get("inputs", [])), tuple(nc.get("outputs", []))) | 97 | return (tuple(nc.get("inputs", [])), tuple(nc.get("outputs", []))) |
| 98 | return tuple(nc) | 98 | return tuple(nc) |
| 99 | 99 | ||
| 100 | 100 | ||
| 101 | def find_common_positions(list1, list2): | 101 | def find_common_positions(list1, list2): |
| 102 | common_elements = set(list1) & set(list2) | 102 | common_elements = set(list1) & set(list2) |
| 103 | merged_list = list1 + list2 | 103 | merged_list = list1 + list2 |
| 104 | positions = [index for index, element in enumerate(merged_list) if element in common_elements] | 104 | positions = [index for index, element in enumerate(merged_list) if element in common_elements] |
| 105 | 105 | ||
| 106 | return merged_list, positions | 106 | return merged_list, positions |
| 107 | 107 | ||
| 108 | 108 | ||
| 109 | def refresh_input_meta_with_buffer_layout(node): | 109 | def refresh_input_meta_with_buffer_layout(node): |
| 110 | val = node.meta.get('val') | 110 | val = node.meta.get('val') |
| 111 | try: | 111 | try: |
| 112 | layout = getattr(V.graph.try_get_buffer(node.target), "layout", None) | 112 | layout = getattr(V.graph.try_get_buffer(node.target), "layout", None) |
| 113 | except Exception: | 113 | except Exception: |
| 114 | layout = None | 114 | layout = None |
| 115 | if not torch.is_tensor(val) or layout is None: | 115 | if not torch.is_tensor(val) or layout is None: |
| 116 | return val | 116 | return val |
| 117 | 117 | ||
| 118 | size, stride = tuple(layout.size), tuple(layout.stride) | 118 | size, stride = tuple(layout.size), tuple(layout.stride) |
| 119 | if any(not isinstance(x, int) for x in (*size, *stride)): | 119 | if any(not isinstance(x, int) for x in (*size, *stride)): |
| 120 | return val | 120 | return val |
| 121 | 121 | ||
| 122 | if val.size() != size or val.stride() != stride: | 122 | if val.size() != size or val.stride() != stride: |
| 123 | with V.graph.fake_mode: | 123 | with V.graph.fake_mode: |
| 124 | val = torch.empty_strided( | 124 | val = torch.empty_strided( |
| 125 | size, | 125 | size, |
| 126 | stride, | 126 | stride, |
| 127 | dtype=val.dtype, | 127 | dtype=val.dtype, |
| 128 | device=val.device, | 128 | device=val.device, |
| 129 | requires_grad=val.requires_grad, | 129 | requires_grad=val.requires_grad, |
| 130 | ) | 130 | ) |
| 131 | node.meta['val'] = val | 131 | node.meta['val'] = val |
| 132 | return val | 132 | return val |
| 133 | 133 | ||
| 134 | 134 | ||
| 135 | def create_fx_from_snodes_by_traced_graph(snodes: List[scheduler.SchedulerNode], triton_kernel: TritonKernel): | 135 | def create_fx_from_snodes_by_traced_graph(snodes: List[scheduler.SchedulerNode], triton_kernel: TritonKernel): |
| 136 | call_inputs = [] | 136 | call_inputs = [] |
| 137 | for snode in snodes: | 137 | for snode in snodes: |
| 138 | snode.node.data.traced_graph.last_node.name = snode.node.get_name() | 138 | snode.node.data.traced_graph.last_node.name = snode.node.get_name() |
| 139 | if len(snodes) == 1: | 139 | if len(snodes) == 1: |
| 140 | traced_graph = snodes[0].node.data.traced_graph | 140 | traced_graph = snodes[0].node.data.traced_graph |
| 141 | else: | 141 | else: |
| 142 | traced_graph = merge_fx_graphs([snode.node.data.traced_graph for snode in snodes]) | 142 | traced_graph = merge_fx_graphs([snode.node.data.traced_graph for snode in snodes]) |
| 143 | inputs = [] | 143 | inputs = [] |
| 144 | for node in traced_graph.graph.nodes: | 144 | for node in traced_graph.graph.nodes: |
| 145 | if node.op == 'placeholder': | 145 | if node.op == 'placeholder': |
| 146 | call_inputs.append(node.target) | 146 | call_inputs.append(node.target) |
| 147 | inputs.append(refresh_input_meta_with_buffer_layout(node)) | 147 | inputs.append(refresh_input_meta_with_buffer_layout(node)) |
| 148 | non_contiguous_indices = {} | 148 | non_contiguous_indices = {} |
| 149 | non_contiguous_indices["inputs"] = [i for i, inp in enumerate(inputs) if torch.is_tensor(inp) and not inp.is_contiguous()] | 149 | non_contiguous_indices["inputs"] = [i for i, inp in enumerate(inputs) if torch.is_tensor(inp) and not inp.is_contiguous()] |
| 150 | num_inputs = len(call_inputs) | 150 | num_inputs = len(call_inputs) |
| 151 | call_outputs = [] | 151 | call_outputs = [] |
| 152 | for snode in snodes: | 152 | for snode in snodes: |
| 153 | if snode.has_aliasing_or_mutation(): | 153 | if snode.has_aliasing_or_mutation(): |
| 154 | for buf in snode.get_outputs(): | 154 | for buf in snode.get_outputs(): |
| 155 | if len(buf.get_mutations()): | 155 | if len(buf.get_mutations()): |
| 156 | call_outputs.extend(buf.get_mutations()) | 156 | call_outputs.extend(buf.get_mutations()) |
| 157 | elif len(buf.get_aliases()): | 157 | elif len(buf.get_aliases()): |
| 158 | call_outputs.append(buf.get_name()) | 158 | call_outputs.append(buf.get_name()) |
| 159 | elif snode.node.get_name() not in (V.graph.removed_buffers | V.graph.inplaced_to_remove): | 159 | elif snode.node.get_name() not in (V.graph.removed_buffers | V.graph.inplaced_to_remove): |
| 160 | call_outputs.append(snode.node.get_name()) | 160 | call_outputs.append(snode.node.get_name()) |
| 161 | num_outputs = len(call_outputs) | 161 | num_outputs = len(call_outputs) |
| 162 | call_args, mutated_indices = find_common_positions(call_inputs, call_outputs) | 162 | call_args, mutated_indices = find_common_positions(call_inputs, call_outputs) |
| 163 | outputs = traced_graph.last_node if isinstance(traced_graph.last_node, List) \ | 163 | outputs = traced_graph.last_node if isinstance(traced_graph.last_node, List) \ |
| 164 | else [traced_graph.last_node] | 164 | else [traced_graph.last_node] |
| 165 | outputs = [output for output in outputs if output.name not in (V.graph.removed_buffers | V.graph.inplaced_to_remove)] | 165 | outputs = [output for output in outputs if output.name not in (V.graph.removed_buffers | V.graph.inplaced_to_remove)] |
| 166 | traced_graph.graph.output(tuple(outputs)) | 166 | traced_graph.graph.output(tuple(outputs)) |
| 167 | traced_graph.graph.lint() | 167 | traced_graph.graph.lint() |
| 168 | orig_module = torch.nn.Module() | 168 | orig_module = torch.nn.Module() |
| 169 | gm = torch.fx.GraphModule(orig_module, traced_graph.graph) | 169 | gm = torch.fx.GraphModule(orig_module, traced_graph.graph) |
| 170 | gm.recompile() | 170 | gm.recompile() |
| 171 | 171 | ||
| 172 | def runnable_gm(*args): | 172 | def runnable_gm(*args): |
| 173 | return torch.fx.Interpreter(gm).run(*args) | 173 | return torch.fx.Interpreter(gm).run(*args) |
| 174 | with V.graph.fake_mode: | 174 | with V.graph.fake_mode: |
| 175 | gm = make_fx(runnable_gm)(*inputs) | 175 | gm = make_fx(runnable_gm)(*inputs) |
| 176 | view_to_reshape(gm) | 176 | view_to_reshape(gm) |
| 177 | non_contiguous_indices["outputs"] = [i + num_inputs | 177 | non_contiguous_indices["outputs"] = [i + num_inputs |
| 178 | for i, call_output in enumerate(call_outputs) | 178 | for i, call_output in enumerate(call_outputs) |
| 179 | if not V.graph.try_get_buffer(call_output).layout.is_contiguous()] | 179 | if not V.graph.try_get_buffer(call_output).layout.is_contiguous()] |
| 180 | return (gm, call_args, {"num_outputs": num_outputs, | 180 | return (gm, call_args, {"num_outputs": num_outputs, |
| 181 | "non_contiguous_indices": non_contiguous_indices, | 181 | "non_contiguous_indices": non_contiguous_indices, |
| 182 | "mutated_indices": mutated_indices, }) | 182 | "mutated_indices": mutated_indices, }) |
| 183 | 183 | ||
| 184 | 184 | ||
| 185 | class NpuMetaKernel(Kernel): | 185 | class NpuMetaKernel(Kernel): |
| 186 | def __init__(self, gm: torch.fx.GraphModule, snodes: list[scheduler.SchedulerNode], call_args: list[str], non_contiguous_indices: list[int], num_outputs: list[int] = None, mutated_indices: list[int] = None): | 186 | def __init__(self, gm: torch.fx.GraphModule, snodes: list[scheduler.SchedulerNode], call_args: list[str], non_contiguous_indices: list[int], num_outputs: list[int] = None, mutated_indices: list[int] = None): |
| 187 | super().__init__() | 187 | super().__init__() |
| 188 | if gm is None: | 188 | if gm is None: |
| 189 | self._gm = None | 189 | self._gm = None |
| 190 | self._gm_with_prim_cast = None | 190 | self._gm_with_prim_cast = None |
| 191 | self._is_dynamic = False | 191 | self._is_dynamic = False |
| 192 | else: | 192 | else: |
| 193 | self._gm = gm | 193 | self._gm = gm |
| 194 | self._gm_with_prim_cast = self.build_gm_with_prim_cast(gm) | 194 | self._gm_with_prim_cast = self.build_gm_with_prim_cast(gm) |
| 195 | self._is_dynamic = is_fx_dynamic(self._gm) | 195 | self._is_dynamic = is_fx_dynamic(self._gm) |
| 196 | 196 | ||
| 197 | if anir_config.online_acc_comp: | 197 | if anir_config.online_acc_comp: |
| 198 | modify_gm_for_acc_comp(self._gm) | 198 | modify_gm_for_acc_comp(self._gm) |
| 199 | 199 | ||
| 200 | self._snodes = snodes | 200 | self._snodes = snodes |
| 201 | self._call_args = call_args | 201 | self._call_args = call_args |
| 202 | self.non_contiguous_indices = non_contiguous_indices | 202 | self.non_contiguous_indices = non_contiguous_indices |
| 203 | self.num_outputs = num_outputs | 203 | self.num_outputs = num_outputs |
| 204 | self.mutated_indices = mutated_indices | 204 | self.mutated_indices = mutated_indices |
| 205 | 205 | ||
| 206 | def get_mlir_output_type(self): | 206 | def get_mlir_output_type(self): |
| 207 | return OutputType.RAW | 207 | return OutputType.RAW |
| 208 | 208 | ||
| 209 | def imports_for_benchmark_kernel(self): | 209 | def imports_for_benchmark_kernel(self): |
| 210 | return textwrap.dedent( | 210 | return textwrap.dedent( |
| 211 | """ | 211 | """ |
| 212 | from torch._dynamo.testing import rand_strided | 212 | from torch._dynamo.testing import rand_strided |
| 213 | {} | 213 | {} |
| 214 | import torch | 214 | import torch |
| 215 | """.format( | 215 | """.format( |
| 216 | V.graph.device_ops.import_get_raw_stream_as("get_raw_stream") | 216 | V.graph.device_ops.import_get_raw_stream_as("get_raw_stream") |
| 217 | ) | 217 | ) |
| 218 | ) | 218 | ) |
| 219 | 219 | ||
| 220 | def build_gm_with_prim_cast(self, gm): | 220 | def build_gm_with_prim_cast(self, gm): |
| 221 | return npu_cast_to_prim_cast(gm) | 221 | return npu_cast_to_prim_cast(gm) |
| 222 | 222 | ||
| 223 | def codegen_kernel(self, name=None): | 223 | def codegen_kernel(self, name=None): |
| 224 | code = IndentedBuffer() | 224 | code = IndentedBuffer() |
| 225 | 225 | ||
| 226 | scalarize_tensor_ops_on_scalars(self._gm_with_prim_cast) | 226 | scalarize_tensor_ops_on_scalars(self._gm_with_prim_cast) |
| 227 | 227 | ||
| 228 | set_model_name(f'MODEL_NAME') | 228 | set_model_name(f'MODEL_NAME') |
| 229 | *_, model_name, nth_graph = get_aot_compilation_context() | 229 | *_, model_name, nth_graph = get_aot_compilation_context() |
| 230 | 230 | ||
| 231 | import torch_mlir | 231 | import torch_mlir |
| 232 | 232 | ||
| 233 | mlir_module = stateless_fx_import( | 233 | mlir_module = stateless_fx_import( |
| 234 | self._gm_with_prim_cast, | 234 | self._gm_with_prim_cast, |
| 235 | model_name=model_name, | 235 | model_name=model_name, |
| 236 | output_type=self.get_mlir_output_type(), | 236 | output_type=self.get_mlir_output_type(), |
| 237 | import_symbolic_shape_expressions=False | 237 | import_symbolic_shape_expressions=False |
| 238 | ) | 238 | ) |
| 239 | from torch_mlir.compiler_utils import run_pipeline_with_repro_report | 239 | from torch_mlir.compiler_utils import run_pipeline_with_repro_report |
| 240 | 240 | ||
| 241 | run_pipeline_with_repro_report( | 241 | run_pipeline_with_repro_report( |
| 242 | mlir_module, | 242 | mlir_module, |
| 243 | f"builtin.module(torch-lower-to-backend-contract)", | 243 | f"builtin.module(torch-lower-to-backend-contract)", |
| 244 | "Lowering TorchFX IR -> Torch Backend IR", | 244 | "Lowering TorchFX IR -> Torch Backend IR", |
| 245 | ) | 245 | ) |
| 246 | code.splice(f'{str(mlir_module)}') | 246 | code.splice(f'{str(mlir_module)}') |
| 247 | 247 | ||
| 248 | return code.getvalue() | 248 | return code.getvalue() |
| 249 | 249 | ||
| 250 | def get_call_args(self): | 250 | def get_call_args(self): |
| 251 | return self._call_args | 251 | return self._call_args |
| 252 | 252 | ||
| 253 | def call_kernel(self, name: str, node=None): | 253 | def call_kernel(self, name: str, node=None): |
| 254 | wrapper = V.graph.wrapper_code | 254 | wrapper = V.graph.wrapper_code |
| 255 | call_args = self.get_call_args() | 255 | call_args = self.get_call_args() |
| 256 | 256 | ||
| 257 | for call_arg in call_args: | 257 | for call_arg in call_args: |
| 258 | if call_arg.startswith('_uwu'): | 258 | if call_arg.startswith('_uwu'): |
| 259 | expression = map_strings_to_operators(call_arg) | 259 | expression = map_strings_to_operators(call_arg) |
| 260 | wrapper.writeline(f'{call_arg} = {expression}') | 260 | wrapper.writeline(f'{call_arg} = {expression}') |
| 261 | 261 | ||
| 262 | if len(call_args) > 0: | 262 | if len(call_args) > 0: |
| 263 | wrapper.generate_kernel_call(name, call_args) | 263 | wrapper.generate_kernel_call(name, call_args) |
| 264 | 264 | ||
| 265 | def codegen_debug_performance(self, fd): | 265 | def codegen_debug_performance(self, fd): |
| 266 | from ...npu.utils import generate_compiler_repro_string, generate_fake_inputs | 266 | from ...npu.utils import generate_compiler_repro_string, generate_fake_inputs |
| 267 | name_to_example_inputs = parse_fx_example_inputs(self._gm) | 267 | name_to_example_inputs = parse_fx_example_inputs(self._gm) |
| 268 | call_args_str = ", ".join(list(name_to_example_inputs.keys())) | 268 | call_args_str = ", ".join(list(name_to_example_inputs.keys())) |
| 269 | fd.write(generate_compiler_repro_string(self._gm)) | 269 | fd.write(generate_compiler_repro_string(self._gm)) |
| 270 | fd.write("\n") | 270 | fd.write("\n") |
| 271 | fd.write("if __name__ == '__main__':\n") | 271 | fd.write("if __name__ == '__main__':\n") |
| 272 | fd.write(" from torch._inductor.utils import print_performance\n") | 272 | fd.write(" from torch._inductor.utils import print_performance\n") |
| 273 | fd.write(f" with torch.no_grad():\n") | 273 | fd.write(f" with torch.no_grad():\n") |
| 274 | fd.write(generate_fake_inputs(name_to_example_inputs)) | 274 | fd.write(generate_fake_inputs(name_to_example_inputs)) |
| 275 | fd.write('\n') | 275 | fd.write('\n') |
| 276 | fd.write(f" fn = lambda: mod({call_args_str})\n") | 276 | fd.write(f" fn = lambda: mod({call_args_str})\n") |
| 277 | fd.write(f" print_performance(fn, times=10, repeat=10)\n") | 277 | fd.write(f" print_performance(fn, times=10, repeat=10)\n") |
| 278 | 278 | ||
| 279 | 279 | ||
| 280 | class NpuMetaScheduling(SIMDScheduling): | 280 | class NpuMetaScheduling(SIMDScheduling): |
| 281 | kernel_type = NpuTritonKernel | 281 | kernel_type = NpuTritonKernel |
| 282 | meta_kernel_type = NpuMetaKernel | 282 | meta_kernel_type = NpuMetaKernel |
| 283 | 283 | ||
| 284 | def __init__(self, sched: Scheduler): | 284 | def __init__(self, sched: Scheduler): |
| 285 | super().__init__(sched) | 285 | super().__init__(sched) |
| 286 | self.orig_fnode_name_to_fnode = {} | 286 | self.orig_fnode_name_to_fnode = {} |
| 287 | 287 | ||
| 288 | def _postprocess_src_code(self, src_code, mlir_kernel, kernel_name): | 288 | def _postprocess_src_code(self, src_code, mlir_kernel, kernel_name): |
| 289 | return src_code, {} | 289 | return src_code, {} |
| 290 | 290 | ||
| 291 | def define_kernel(self, src_code, mlir_kernel, traced_graph, mode=None): | 291 | def define_kernel(self, src_code, mlir_kernel, traced_graph, mode=None): |
| 292 | if mode is None: | 292 | if mode is None: |
| 293 | mode = anir_config._get_compile_mode() | 293 | mode = anir_config._get_compile_mode() |
| 294 | 294 | ||
| 295 | wrapper = V.graph.wrapper_code | 295 | wrapper = V.graph.wrapper_code |
| 296 | 296 | ||
| 297 | kernel_key = (src_code, tuple(mlir_kernel.non_contiguous_indices)) | 297 | kernel_key = (src_code, tuple(mlir_kernel.non_contiguous_indices)) |
| 298 | 298 | ||
| 299 | if kernel_key in wrapper.src_to_kernel: | 299 | if kernel_key in wrapper.src_to_kernel: |
| 300 | cached_val = wrapper.src_to_kernel[kernel_key] | 300 | cached_val = wrapper.src_to_kernel[kernel_key] |
| 301 | if isinstance(cached_val, str): | 301 | if isinstance(cached_val, str): |
| 302 | return cached_val | 302 | return cached_val |
| 303 | else: | 303 | else: |
| 304 | log.warning(f"Found invalid cache entry for kernel {mlir_kernel}. Recompiling.") | 304 | log.warning(f"Found invalid cache entry for kernel {mlir_kernel}. Recompiling.") |
| 305 | del wrapper.src_to_kernel[kernel_key] | 305 | del wrapper.src_to_kernel[kernel_key] |
| 306 | 306 | ||
| 307 | fused_name = ( | 307 | fused_name = ( |
| 308 | get_fused_kernel_name(mlir_kernel._snodes, config.triton.descriptive_names) | 308 | get_fused_kernel_name(mlir_kernel._snodes, config.triton.descriptive_names) |
| 309 | if config.triton.descriptive_names | 309 | if config.triton.descriptive_names |
| 310 | else "" | 310 | else "" |
| 311 | ) | 311 | ) |
| 312 | 312 | ||
| 313 | if mode in ["complete_fallback", "auto_fallback"]: | 313 | if mode in ["complete_fallback", "auto_fallback"]: |
| 314 | fx_graph_suffix = f"{next(id_iter)}" | 314 | fx_graph_suffix = f"{next(id_iter)}" |
| 315 | prefix = self._get_kernel_prefix() | 315 | prefix = self._get_kernel_prefix() |
| 316 | kernel_name = "_".join([prefix, fused_name, fx_graph_suffix]) | 316 | kernel_name = "_".join([prefix, fused_name, fx_graph_suffix]) |
| 317 | else: | 317 | else: |
| 318 | kernel_suffix = wrapper.next_kernel_suffix() | 318 | kernel_suffix = wrapper.next_kernel_suffix() |
| 319 | prefix = self._get_kernel_prefix() | 319 | prefix = self._get_kernel_prefix() |
| 320 | kernel_name = "_".join([prefix, fused_name, kernel_suffix]) | 320 | kernel_name = "_".join([prefix, fused_name, kernel_suffix]) |
| 321 | compile_src_code, extra_kernel_meta = self._postprocess_src_code(src_code, mlir_kernel, kernel_name) | 321 | compile_src_code, extra_kernel_meta = self._postprocess_src_code(src_code, mlir_kernel, kernel_name) |
| 322 | 322 | ||
| 323 | current_device = V.graph.get_current_device_or_throw() | 323 | current_device = V.graph.get_current_device_or_throw() |
| 324 | traced_graph_hash = code_hash(traced_graph.print_readable(print_output=False) + kernel_name) | 324 | traced_graph_hash = code_hash(traced_graph.print_readable(print_output=False) + kernel_name) |
| 325 | num_call_functions = get_num_call_functions(mlir_kernel._gm) | 325 | num_call_functions = get_num_call_functions(mlir_kernel._gm) |
| 326 | 326 | ||
| 327 | if num_call_functions <= 1 or kernel_name in anir_config.force_fallback_kernel_names: | 327 | if num_call_functions <= 1 or kernel_name in anir_config.force_fallback_kernel_names: |
| 328 | mode = "complete_fallback" | 328 | mode = "complete_fallback" |
| 329 | 329 | ||
| 330 | kernel_meta = self._prepare_kernel_meta( | 330 | kernel_meta = self._prepare_kernel_meta( |
| 331 | mlir_kernel, current_device, kernel_name, traced_graph_hash, num_call_functions, compile_src_code | 331 | mlir_kernel, current_device, kernel_name, traced_graph_hash, num_call_functions, compile_src_code |
| 332 | ) | 332 | ) |
| 333 | kernel_meta.update(extra_kernel_meta) | 333 | kernel_meta.update(extra_kernel_meta) |
| 334 | 334 | ||
| 335 | wrapper.src_to_kernel[kernel_key] = kernel_name | 335 | wrapper.src_to_kernel[kernel_key] = kernel_name |
| 336 | 336 | ||
| 337 | subs_name = kernel_name if config.triton.unique_kernel_names else f"{self._get_kernel_prefix()}_" | 337 | subs_name = kernel_name if config.triton.unique_kernel_names else f"{self._get_kernel_prefix()}_" |
| 338 | 338 | ||
| 339 | compile_wrapper = IndentedBuffer() | 339 | compile_wrapper = IndentedBuffer() |
| 340 | metadata_comment = "" | 340 | metadata_comment = "" |
| 341 | 341 | ||
| 342 | if mode == "auto_fallback": | 342 | if mode == "auto_fallback": |
| 343 | src_code = src_code.replace("MODEL_NAME", kernel_name) | 343 | src_code = src_code.replace("MODEL_NAME", kernel_name) |
| 344 | self._handle_auto_fallback_mode( | 344 | self._handle_auto_fallback_mode( |
| 345 | compile_wrapper, src_code, kernel_name, subs_name, kernel_meta, wrapper, metadata_comment, mlir_kernel | 345 | compile_wrapper, src_code, kernel_name, subs_name, kernel_meta, wrapper, metadata_comment, mlir_kernel |
| 346 | ) | 346 | ) |
| 347 | elif mode == "complete_fallback": | 347 | elif mode == "complete_fallback": |
| 348 | self._handle_complete_fallback_mode( | 348 | self._handle_complete_fallback_mode( |
| 349 | compile_wrapper, kernel_name, kernel_meta, wrapper, metadata_comment, mlir_kernel | 349 | compile_wrapper, kernel_name, kernel_meta, wrapper, metadata_comment, mlir_kernel |
| 350 | ) | 350 | ) |
| 351 | else: | 351 | else: |
| 352 | src_code = src_code.replace("MODEL_NAME", kernel_name) | 352 | src_code = src_code.replace("MODEL_NAME", kernel_name) |
| 353 | self._handle_default_mode( | 353 | self._handle_default_mode( |
| 354 | compile_wrapper, src_code, kernel_name, subs_name, kernel_meta, wrapper, metadata_comment, mlir_kernel | 354 | compile_wrapper, src_code, kernel_name, subs_name, kernel_meta, wrapper, metadata_comment, mlir_kernel |
| 355 | ) | 355 | ) |
| 356 | 356 | ||
| 357 | if mode in ["complete_fallback", "auto_fallback"]: | 357 | if mode in ["complete_fallback", "auto_fallback"]: |
| 358 | self._dump_fx_graph_for_fallback( | 358 | self._dump_fx_graph_for_fallback( |
| 359 | mlir_kernel, current_device, traced_graph_hash, kernel_name, compile_wrapper.getvalue() | 359 | mlir_kernel, current_device, traced_graph_hash, kernel_name, compile_wrapper.getvalue() |
| 360 | ) | 360 | ) |
| 361 | 361 | ||
| 362 | return kernel_name | 362 | return kernel_name |
| 363 | 363 | ||
| 364 | def _get_kernel_prefix(self) -> str: | 364 | def _get_kernel_prefix(self) -> str: |
| 365 | return "mlir" | 365 | return "mlir" |
| 366 | 366 | ||
| 367 | def _prepare_kernel_meta(self, mlir_kernel, device, name, hash_val, num_calls, src_code) -> Dict[str, Any]: | 367 | def _prepare_kernel_meta(self, mlir_kernel, device, name, hash_val, num_calls, src_code) -> Dict[str, Any]: |
| 368 | device_interface = get_interface_for_device(device.type) | 368 | device_interface = get_interface_for_device(device.type) |
| 369 | device = torch.device(device.type, device.index) | 369 | device = torch.device(device.type, device.index) |
| 370 | device_name = device_interface.get_compute_capability(device) | 370 | device_name = device_interface.get_compute_capability(device) |
| 371 | return { | 371 | return { |
| 372 | 'device_str': device.type, | 372 | 'device_str': device.type, |
| 373 | 'device_index': device.index, | 373 | 'device_index': device.index, |
| 374 | 'device_name': device_name, | 374 | 'device_name': device_name, |
| 375 | 'num_outputs': mlir_kernel.num_outputs, | 375 | 'num_outputs': mlir_kernel.num_outputs, |
| 376 | 'non_contiguous_indices': mlir_kernel.non_contiguous_indices, | 376 | 'non_contiguous_indices': mlir_kernel.non_contiguous_indices, |
| 377 | 'dynamic': mlir_kernel._is_dynamic, | 377 | 'dynamic': mlir_kernel._is_dynamic, |
| 378 | 'mutated_indices': mlir_kernel.mutated_indices, | 378 | 'mutated_indices': mlir_kernel.mutated_indices, |
| 379 | 'traced_graph_cache': anir_config.traced_graph_cache or "traced_graph_cache", | 379 | 'traced_graph_cache': anir_config.traced_graph_cache or "traced_graph_cache", |
| 380 | 'traced_graph_hash': hash_val, | 380 | 'traced_graph_hash': hash_val, |
| 381 | 'num_call_functions': num_calls, | 381 | 'num_call_functions': num_calls, |
| 382 | 'is_reduction': 'linalg.reduce' in src_code, | 382 | 'is_reduction': 'linalg.reduce' in src_code, |
| 383 | 'are_deterministic_algorithms_enabled': torch.are_deterministic_algorithms_enabled(), | 383 | 'are_deterministic_algorithms_enabled': torch.are_deterministic_algorithms_enabled(), |
| 384 | } | 384 | } |
| 385 | 385 | ||
| 386 | def _handle_auto_fallback_mode(self, compile_wrapper, src_code, name, subs_name, meta, wrapper, metadata_comment, mlir_kernel=None): | 386 | def _handle_auto_fallback_mode(self, compile_wrapper, src_code, name, subs_name, meta, wrapper, metadata_comment, mlir_kernel=None): |
| 387 | _basename, _, kernel_path = get_path(code_hash(src_code.strip()), "py") | 387 | _basename, _, kernel_path = get_path(code_hash(src_code.strip()), "py") |
| 388 | 388 | ||
| 389 | compile_wrapper.writeline(f"async_compile.{self._get_compile_api()}({subs_name!r}, '''") | 389 | compile_wrapper.writeline(f"async_compile.{self._get_compile_api()}({subs_name!r}, '''") |
| 390 | compile_wrapper.splice(src_code, strip=True) | 390 | compile_wrapper.splice(src_code, strip=True) |
| 391 | compile_wrapper.writeline(f"''', kernel_meta={meta})") | 391 | compile_wrapper.writeline(f"''', kernel_meta={meta})") |
| 392 | 392 | ||
| 393 | metadata_comment = f"# kernel path: {kernel_path}" | 393 | metadata_comment = f"# kernel path: {kernel_path}" |
| 394 | 394 | ||
| 395 | origins, detailed_origins = get_kernel_metadata(mlir_kernel._snodes, wrapper) | 395 | origins, detailed_origins = get_kernel_metadata(mlir_kernel._snodes, wrapper) |
| 396 | metadata_comment += "\n" + origins + "\n" + detailed_origins | 396 | metadata_comment += "\n" + origins + "\n" + detailed_origins |
| 397 | 397 | ||
| 398 | wrapper.define_kernel(name, compile_wrapper.getvalue(), metadata_comment) | 398 | wrapper.define_kernel(name, compile_wrapper.getvalue(), metadata_comment) |
| 399 | 399 | ||
| 400 | if metrics.is_metric_table_enabled("kernel_metadata"): | 400 | if metrics.is_metric_table_enabled("kernel_metadata"): |
| 401 | metrics.log_kernel_metadata(name, kernel_path, src_code) | 401 | metrics.log_kernel_metadata(name, kernel_path, src_code) |
| 402 | 402 | ||
| 403 | def _handle_complete_fallback_mode(self, compile_wrapper, name, meta, wrapper, metadata_comment, mlir_kernel): | 403 | def _handle_complete_fallback_mode(self, compile_wrapper, name, meta, wrapper, metadata_comment, mlir_kernel): |
| 404 | compile_wrapper.writeline(f"async_compile.import_fx({name!r}, kernel_meta={meta})") | 404 | compile_wrapper.writeline(f"async_compile.import_fx({name!r}, kernel_meta={meta})") |
| 405 | metadata_comment = f'"""\n{mlir_kernel._gm.print_readable(print_output=False)}\n"""' | 405 | metadata_comment = f'"""\n{mlir_kernel._gm.print_readable(print_output=False)}\n"""' |
| 406 | wrapper.define_kernel(name, compile_wrapper.getvalue(), metadata_comment) | 406 | wrapper.define_kernel(name, compile_wrapper.getvalue(), metadata_comment) |
| 407 | 407 | ||
| 408 | def _handle_default_mode(self, compile_wrapper, src_code, name, subs_name, meta, wrapper, metadata_comment): | 408 | def _handle_default_mode(self, compile_wrapper, src_code, name, subs_name, meta, wrapper, metadata_comment): |
| 409 | self._handle_auto_fallback_mode(compile_wrapper, src_code, name, subs_name, meta, wrapper, metadata_comment) | 409 | self._handle_auto_fallback_mode(compile_wrapper, src_code, name, subs_name, meta, wrapper, metadata_comment) |
| 410 | 410 | ||
| 411 | def _get_compile_api(self) -> str: | 411 | def _get_compile_api(self) -> str: |
| 412 | return "auto_fallback" | 412 | return "auto_fallback" |
| 413 | 413 | ||
| 414 | def _dump_fx_graph_for_fallback(self, mlir_kernel, device, graph_hash, kernel_name, compile_code): | 414 | def _dump_fx_graph_for_fallback(self, mlir_kernel, device, graph_hash, kernel_name, compile_code): |
| 415 | 415 | ||
| 416 | cache_root = os.getenv("TORCHINDUCTOR_CACHE_DIR") | 416 | cache_root = os.getenv("TORCHINDUCTOR_CACHE_DIR") |
| 417 | dump_path = os.path.join( | 417 | dump_path = os.path.join( |
| 418 | cache_root, | 418 | cache_root, |
| 419 | anir_config.traced_graph_cache or "traced_graph_cache", | 419 | anir_config.traced_graph_cache or "traced_graph_cache", |
| 420 | str(device.index), | 420 | str(device.index), |
| 421 | graph_hash | 421 | graph_hash |
| 422 | ) | 422 | ) |
| 423 | 423 | ||
| 424 | if not os.path.exists(dump_path): | 424 | if not os.path.exists(dump_path): |
| 425 | os.makedirs(dump_path, exist_ok=True) | 425 | os.makedirs(dump_path, exist_ok=True) |
| 426 | to_folder(mlir_kernel._gm, dump_path, graph_hash=graph_hash, module_name=graph_hash) | 426 | to_folder(mlir_kernel._gm, dump_path, graph_hash=graph_hash, module_name=graph_hash) |
| 427 | 427 | ||
| 428 | if anir_config.fx_subgraph_dump_path is not None: | 428 | if anir_config.fx_subgraph_dump_path is not None: |
| 429 | subgraph_dump_path = os.path.join(anir_config.fx_subgraph_dump_path, str(device.index), kernel_name) | 429 | subgraph_dump_path = os.path.join(anir_config.fx_subgraph_dump_path, str(device.index), kernel_name) |
| 430 | os.makedirs(subgraph_dump_path, exist_ok=True) | 430 | os.makedirs(subgraph_dump_path, exist_ok=True) |
| 431 | 431 | ||
| 432 | num_args = len(mlir_kernel._gm.code.split('forward(', )[1].split(')')[0].split(', ')) - 1 | 432 | num_args = len(mlir_kernel._gm.code.split('forward(', )[1].split(')')[0].split(', ')) - 1 |
| 433 | fx_graph_code = get_fx_graph_code(mlir_kernel._gm.code, num_args, runnable=False, kernel_code=compile_code, kernel_name=kernel_name) | 433 | fx_graph_code = get_fx_graph_code(mlir_kernel._gm.code, num_args, runnable=False, kernel_code=compile_code, kernel_name=kernel_name) |
| 434 | runnable_fx_graph_code = get_fx_graph_code(mlir_kernel._gm.code, num_args, runnable=True, kernel_code=compile_code, kernel_name=kernel_name) | 434 | runnable_fx_graph_code = get_fx_graph_code(mlir_kernel._gm.code, num_args, runnable=True, kernel_code=compile_code, kernel_name=kernel_name) |
| 435 | 435 | ||
| 436 | with open(os.path.join(subgraph_dump_path, f'{kernel_name}.py'), 'w') as f: | 436 | with open(os.path.join(subgraph_dump_path, f'{kernel_name}.py'), 'w') as f: |
| 437 | f.write(fx_graph_code) | 437 | f.write(fx_graph_code) |
| 438 | with open(os.path.join(subgraph_dump_path, f'runnable_{kernel_name}.py'), 'w') as f: | 438 | with open(os.path.join(subgraph_dump_path, f'runnable_{kernel_name}.py'), 'w') as f: |
| 439 | f.write(runnable_fx_graph_code) | 439 | f.write(runnable_fx_graph_code) |
| 440 | 440 | ||
| 441 | 441 | ||
| 442 | def codegen_node_schedule(self, kernel_features: SIMDKernelFeatures, nodes): | 442 | def codegen_node_schedule(self, kernel_features: SIMDKernelFeatures, nodes): |
| 443 | node_schedule = kernel_features.node_schedule | 443 | node_schedule = kernel_features.node_schedule |
| 444 | 444 | ||
| 445 | tiling = self.select_tiling( | 445 | tiling = self.select_tiling( |
| 446 | node_schedule, kernel_features.numel, kernel_features.reduction_numel | 446 | node_schedule, kernel_features.numel, kernel_features.reduction_numel |
| 447 | ) | 447 | ) |
| 448 | 448 | ||
| 449 | kernels = self.create_kernel_choices( | 449 | kernels = self.create_kernel_choices( |
| 450 | kernel_features, [tiling], {"features": kernel_features} | 450 | kernel_features, [tiling], {"features": kernel_features} |
| 451 | ) | 451 | ) |
| 452 | for kernel in kernels: | 452 | for kernel in kernels: |
| 453 | super().codegen_node_schedule_with_kernel(node_schedule, kernel) | 453 | super().codegen_node_schedule_with_kernel(node_schedule, kernel) |
| 454 | MultiKernel.merge_workspaces_inplace(kernels) | 454 | MultiKernel.merge_workspaces_inplace(kernels) |
| 455 | for kernel in kernels: | 455 | for kernel in kernels: |
| 456 | V.graph.removed_buffers |= kernel.removed_buffers | 456 | V.graph.removed_buffers |= kernel.removed_buffers |
| 457 | V.graph.inplaced_to_remove |= kernel.inplaced_to_remove | 457 | V.graph.inplaced_to_remove |= kernel.inplaced_to_remove |
| 458 | if not anir_config.traced_graph_cache: | 458 | if not anir_config.traced_graph_cache: |
| 459 | anir_config.traced_graph_cache = "traced_graph_cache" | 459 | anir_config.traced_graph_cache = "traced_graph_cache" |
| 460 | os.makedirs( | 460 | os.makedirs( |
| 461 | os.path.join(os.getenv("TORCHINDUCTOR_CACHE_DIR"), anir_config.traced_graph_cache), | 461 | os.path.join(os.getenv("TORCHINDUCTOR_CACHE_DIR"), anir_config.traced_graph_cache), |
| 462 | exist_ok=True, | 462 | exist_ok=True, |
| 463 | ) | 463 | ) |
| 464 | traced_graph, call_args, compile_kwargs = create_fx_from_snodes_by_traced_graph( | 464 | traced_graph, call_args, compile_kwargs = create_fx_from_snodes_by_traced_graph( |
| 465 | nodes, kernel | 465 | nodes, kernel |
| 466 | ) | 466 | ) |
| 467 | mlir_kernel = self.meta_kernel_type(traced_graph, nodes, call_args, **compile_kwargs) | 467 | mlir_kernel = self.meta_kernel_type(traced_graph, nodes, call_args, **compile_kwargs) |
| 468 | with V.set_kernel_handler(mlir_kernel): | 468 | with V.set_kernel_handler(mlir_kernel): |
| 469 | src_code = mlir_kernel.codegen_kernel() | 469 | src_code = mlir_kernel.codegen_kernel() |
| 470 | kernel_name = self.define_kernel(src_code, mlir_kernel, traced_graph) | 470 | kernel_name = self.define_kernel(src_code, mlir_kernel, traced_graph) |
| 471 | log.debug("Generating kernel code with kernel_name: %s", kernel_name) | 471 | log.debug("Generating kernel code with kernel_name: %s", kernel_name) |
| 472 | kernel.kernel_name = kernel_name | 472 | kernel.kernel_name = kernel_name |
| 473 | kernel.code_hash = code_hash(src_code) | 473 | kernel.code_hash = code_hash(src_code) |
| 474 | del kernel | 474 | del kernel |
| 475 | 475 | ||
| 476 | final_kernel: Union[SIMDKernel, MultiKernel] | 476 | final_kernel: Union[SIMDKernel, MultiKernel] |
| 477 | if len(kernels) > 1: | 477 | if len(kernels) > 1: |
| 478 | raise RuntimeError("MultiKernel not Implemented for this backend!") | 478 | raise RuntimeError("MultiKernel not Implemented for this backend!") |
| 479 | else: | 479 | else: |
| 480 | (final_kernel,) = kernels | 480 | (final_kernel,) = kernels |
| 481 | 481 | ||
| 482 | with V.set_kernel_handler(final_kernel): | 482 | with V.set_kernel_handler(final_kernel): |
| 483 | for node in kernel_features.scheduler_nodes(): | 483 | for node in kernel_features.scheduler_nodes(): |
| 484 | node.mark_run() | 484 | node.mark_run() |
| 485 | 485 | ||
| 486 | self.codegen_comment(node_schedule) | 486 | self.codegen_comment(node_schedule) |
| 487 | final_kernel.call_kernel(call_args, final_kernel.kernel_name) | 487 | final_kernel.call_kernel(call_args, final_kernel.kernel_name) |
| 488 | 488 | ||
| 489 | if config.nan_asserts: | 489 | if config.nan_asserts: |
| 490 | final_kernel.codegen_nan_check() | 490 | final_kernel.codegen_nan_check() |
| 491 | if config.warn_mix_layout: | 491 | if config.warn_mix_layout: |
| 492 | final_kernel.warn_mix_layout(kernels[0].kernel_name) | 492 | final_kernel.warn_mix_layout(kernels[0].kernel_name) |
| 493 | 493 | ||
| 494 | if ( | 494 | if ( |
| 495 | V.graph.wrapper_code.supports_intermediate_hooks | 495 | V.graph.wrapper_code.supports_intermediate_hooks |
| 496 | and config.generate_intermediate_hooks | 496 | and config.generate_intermediate_hooks |
| 497 | ): | 497 | ): |
| 498 | live_outs = kernels[0].args.live_output_buffers() | 498 | live_outs = kernels[0].args.live_output_buffers() |
| 499 | for node in kernel_features.scheduler_nodes(): | 499 | for node in kernel_features.scheduler_nodes(): |
| 500 | name = node.get_name() | 500 | name = node.get_name() |
| 501 | if name not in live_outs: | 501 | if name not in live_outs: |
| 502 | continue | 502 | continue |
| 503 | if node.node is None: | 503 | if node.node is None: |
| 504 | raise RuntimeError("assert node.node is not None") | 504 | raise RuntimeError("assert node.node is not None") |
| 505 | 505 | ||
| 506 | origin_node = node.node.get_origin_node() | 506 | origin_node = node.node.get_origin_node() |
| 507 | if origin_node is not None: | 507 | if origin_node is not None: |
| 508 | counters["inductor"]["intermediate_hooks"] += 1 | 508 | counters["inductor"]["intermediate_hooks"] += 1 |
| 509 | V.graph.wrapper_code.writeline( | 509 | V.graph.wrapper_code.writeline( |
| 510 | f"run_intermediate_hooks({origin_node.name!r}, {name})" | 510 | f"run_intermediate_hooks({origin_node.name!r}, {name})" |
| 511 | ) | 511 | ) |
| 512 | 512 | ||
| 513 | self.scheduler.free_buffers() | 513 | self.scheduler.free_buffers() |
| 514 | 514 | ||
| 515 | def codegen_node(self, node: Union[scheduler.SchedulerNode, object]): | 515 | def codegen_node(self, node: Union[scheduler.SchedulerNode, object]): |
| 516 | nodes: List[scheduler.SchedulerNode] = node.get_nodes() | 516 | nodes: List[scheduler.SchedulerNode] = node.get_nodes() |
| 517 | 517 | ||
| 518 | _, (numel, rnumel) = max(nodes, key=lambda x: int(x.is_reduction())).group | 518 | _, (numel, rnumel) = max(nodes, key=lambda x: int(x.is_reduction())).group |
| 519 | 519 | ||
| 520 | node_schedule = self.generate_node_schedule(nodes, numel, rnumel) | 520 | node_schedule = self.generate_node_schedule(nodes, numel, rnumel) |
| 521 | kernel_features = SIMDKernelFeatures(node_schedule, numel, rnumel) | 521 | kernel_features = SIMDKernelFeatures(node_schedule, numel, rnumel) |
| 522 | 522 | ||
| 523 | return self.codegen_node_schedule(kernel_features, nodes) | 523 | return self.codegen_node_schedule(kernel_features, nodes) |
| @@ -1,330 +1,330 @@ | |||
| 1 | import os | 1 | import os |
| 2 | import sys | 2 | import sys |
| 3 | import importlib | 3 | import importlib |
| 4 | import shutil | 4 | import shutil |
| 5 | from itertools import count | 5 | from itertools import count |
| 6 | from typing import Any, Callable, Dict, List, Optional, Tuple, Iterator | 6 | from typing import Any, Callable, Dict, List, Optional, Tuple, Iterator |
| 7 | 7 | ||
| 8 | import torch | 8 | import torch |
| 9 | from torch._inductor.compile_fx import clone_preserve_strides | 9 | from torch._inductor.compile_fx import clone_preserve_strides |
| 10 | 10 | ||
| 11 | from .. import config as anir_config | 11 | from .. import config as anir_config |
| 12 | from .utils import replace_placeholders | 12 | from .utils import replace_placeholders |
| 13 | 13 | ||
| 14 | 14 | ||
| 15 | _dump_id_iter: Iterator[int] = count() | 15 | _dump_id_iter: Iterator[int] = count() |
| 16 | 16 | ||
| 17 | class MetaCompiler: | 17 | class MetaCompiler: |
| 18 | def __init__( | 18 | def __init__( |
| 19 | self, | 19 | self, |
| 20 | kernel_name: str = "", | 20 | kernel_name: str = "", |
| 21 | multiprocess_compile: bool = False, | 21 | multiprocess_compile: bool = False, |
| 22 | no_more_compile: bool = False, | 22 | no_more_compile: bool = False, |
| 23 | kernel_meta: Optional[Dict[str, Any]] = None, | 23 | kernel_meta: Optional[Dict[str, Any]] = None, |
| 24 | autotune: bool = True, | 24 | autotune: bool = True, |
| 25 | ): | 25 | ): |
| 26 | kernel_meta = kernel_meta or {} | 26 | kernel_meta = kernel_meta or {} |
| 27 | 27 | ||
| 28 | self.kernel_name = kernel_name | 28 | self.kernel_name = kernel_name |
| 29 | self.kernel_meta = kernel_meta | 29 | self.kernel_meta = kernel_meta |
| 30 | 30 | ||
| 31 | self.dynamic = kernel_meta.get("dynamic") | 31 | self.dynamic = kernel_meta.get("dynamic") |
| 32 | self.mutated_indices = kernel_meta.get("mutated_indices") or [] | 32 | self.mutated_indices = kernel_meta.get("mutated_indices") or [] |
| 33 | self.kernel_hash = kernel_meta.get("kernel_hash") | 33 | self.kernel_hash = kernel_meta.get("kernel_hash") |
| 34 | self.signature = kernel_meta.get("signature") | 34 | self.signature = kernel_meta.get("signature") |
| 35 | self.ranks = kernel_meta.get("ranks") | 35 | self.ranks = kernel_meta.get("ranks") |
| 36 | self.num_outputs = kernel_meta.get("num_outputs") | 36 | self.num_outputs = kernel_meta.get("num_outputs") |
| 37 | self.num_call_functions = kernel_meta.get("num_call_functions") | 37 | self.num_call_functions = kernel_meta.get("num_call_functions") |
| 38 | self.device_index = kernel_meta.get("device_index", 0) | 38 | self.device_index = kernel_meta.get("device_index", 0) |
| 39 | self.traced_graph_hash = kernel_meta.get("traced_graph_hash") | 39 | self.traced_graph_hash = kernel_meta.get("traced_graph_hash") |
| 40 | 40 | ||
| 41 | self.multiprocess_compile = multiprocess_compile | 41 | self.multiprocess_compile = multiprocess_compile |
| 42 | self.no_more_compile = no_more_compile | 42 | self.no_more_compile = no_more_compile |
| 43 | self.autotune = autotune | 43 | self.autotune = autotune |
| 44 | 44 | ||
| 45 | self._fallback_call: Optional[Callable[..., Any]] = None | 45 | self._fallback_call: Optional[Callable[..., Any]] = None |
| 46 | self._nc_input_indices: Optional[List[int]] = None | 46 | self._nc_input_indices: Optional[List[int]] = None |
| 47 | self._nc_output_indices: Optional[List[int]] = None | 47 | self._nc_output_indices: Optional[List[int]] = None |
| 48 | 48 | ||
| 49 | self.autotuned: bool = False | 49 | self.autotuned: bool = False |
| 50 | self.launchers: list[Callable[..., Any]] = [] | 50 | self.launchers: list[Callable[..., Any]] = [] |
| 51 | self.kernel_paths: list[Any] = [] | 51 | self.kernel_paths: list[Any] = [] |
| 52 | self.is_fallback_kernels: list[bool] = [] | 52 | self.is_fallback_kernels: list[bool] = [] |
| 53 | 53 | ||
| 54 | def register_launcher( | 54 | def register_launcher( |
| 55 | self, | 55 | self, |
| 56 | launcher: Callable[..., Any], | 56 | launcher: Callable[..., Any], |
| 57 | kernel_path: Any = None, | 57 | kernel_path: Any = None, |
| 58 | is_fallback_kernel: bool = False, | 58 | is_fallback_kernel: bool = False, |
| 59 | ) -> None: | 59 | ) -> None: |
| 60 | """Register a compiled or fallback launcher for runtime execution.""" | 60 | """Register a compiled or fallback launcher for runtime execution.""" |
| 61 | self.launchers.append(launcher) | 61 | self.launchers.append(launcher) |
| 62 | self.kernel_paths.append(kernel_path) | 62 | self.kernel_paths.append(kernel_path) |
| 63 | self.is_fallback_kernels.append(is_fallback_kernel) | 63 | self.is_fallback_kernels.append(is_fallback_kernel) |
| 64 | 64 | ||
| 65 | def get_primary_launcher_index(self) -> int: | 65 | def get_primary_launcher_index(self) -> int: |
| 66 | if not self.launchers: | 66 | if not self.launchers: |
| 67 | raise RuntimeError("No valid launcher") | 67 | raise RuntimeError("No valid launcher") |
| 68 | for idx, is_fallback_kernel in enumerate(self.is_fallback_kernels): | 68 | for idx, is_fallback_kernel in enumerate(self.is_fallback_kernels): |
| 69 | if not is_fallback_kernel: | 69 | if not is_fallback_kernel: |
| 70 | return idx | 70 | return idx |
| 71 | return 0 | 71 | return 0 |
| 72 | 72 | ||
| 73 | def get_primary_launcher(self) -> Callable[..., Any]: | 73 | def get_primary_launcher(self) -> Callable[..., Any]: |
| 74 | return self.launchers[self.get_primary_launcher_index()] | 74 | return self.launchers[self.get_primary_launcher_index()] |
| 75 | 75 | ||
| 76 | def data_dump(self, *args, dump_path=None): | 76 | def data_dump(self, *args, dump_path=None): |
| 77 | if not dump_path: | 77 | if not dump_path: |
| 78 | dump_path = os.path.join(anir_config.fx_subgraph_dump_path, str(self.device_index), self.kernel_name) | 78 | dump_path = os.path.join(anir_config.fx_subgraph_dump_path, str(self.device_index), self.kernel_name) |
| 79 | data_dump_path = os.path.join(dump_path, 'data.pth') | 79 | data_dump_path = os.path.join(dump_path, 'data.pth') |
| 80 | args_cpu = [arg.cpu() if isinstance(arg, torch.Tensor) else arg for arg in args] | 80 | args_cpu = [arg.cpu() if isinstance(arg, torch.Tensor) else arg for arg in args] |
| 81 | torch.save(args_cpu, data_dump_path) | 81 | torch.save(args_cpu, data_dump_path) |
| 82 | 82 | ||
| 83 | def data_dump_fake(self, *args, dump_path=None): | 83 | def data_dump_fake(self, *args, dump_path=None): |
| 84 | if not dump_path: | 84 | if not dump_path: |
| 85 | dump_path = os.path.join(anir_config.fx_subgraph_dump_path, str(self.device_index), self.kernel_name) | 85 | dump_path = os.path.join(anir_config.fx_subgraph_dump_path, str(self.device_index), self.kernel_name) |
| 86 | runable_py_path = os.path.join(dump_path, f'runnable_{self.kernel_name}.py') | 86 | runable_py_path = os.path.join(dump_path, f'runnable_{self.kernel_name}.py') |
| 87 | fake_inputs = [f'rand_strided({arg.shape}, {arg.stride()}, device="{arg.device.type}", dtype={arg.dtype})' \ | 87 | fake_inputs = [f'rand_strided({arg.shape}, {arg.stride()}, device="{arg.device.type}", dtype={arg.dtype})' \ |
| 88 | if isinstance(arg, torch.Tensor) else str(arg) for arg in args[:-self.num_outputs]] | 88 | if isinstance(arg, torch.Tensor) else str(arg) for arg in args[:-self.num_outputs]] |
| 89 | fake_outputs = [f'empty_strided({arg.shape}, {arg.stride()}, device="{arg.device.type}", dtype={arg.dtype})' \ | 89 | fake_outputs = [f'empty_strided({arg.shape}, {arg.stride()}, device="{arg.device.type}", dtype={arg.dtype})' \ |
| 90 | if isinstance(arg, torch.Tensor) else str(arg) for arg in args[-self.num_outputs:]] | 90 | if isinstance(arg, torch.Tensor) else str(arg) for arg in args[-self.num_outputs:]] |
| 91 | replacements = {"FAKE_ARGS_PLACEHOLDER": f"args = [{', '.join(fake_inputs + fake_outputs)}]"} | 91 | replacements = {"FAKE_ARGS_PLACEHOLDER": f"args = [{', '.join(fake_inputs + fake_outputs)}]"} |
| 92 | replace_placeholders(runable_py_path, replacements) | 92 | replace_placeholders(runable_py_path, replacements) |
| 93 | 93 | ||
| 94 | def fx_subgraph_dump(self, suffix): | 94 | def fx_subgraph_dump(self, suffix): |
| 95 | subgraph_dump_path = os.path.join(anir_config.fx_subgraph_dump_path, str(self.device_index), self.kernel_name) | 95 | subgraph_dump_path = os.path.join(anir_config.fx_subgraph_dump_path, str(self.device_index), self.kernel_name) |
| 96 | failed_fx_subgraph_dump_path = anir_config.fx_subgraph_dump_path + f'_{suffix}' | 96 | failed_fx_subgraph_dump_path = anir_config.fx_subgraph_dump_path + f'_{suffix}' |
| 97 | failed_subgraph_dump_path = os.path.join(failed_fx_subgraph_dump_path, str(self.device_index), f'{next(_dump_id_iter)}_' + self.kernel_name) | 97 | failed_subgraph_dump_path = os.path.join(failed_fx_subgraph_dump_path, str(self.device_index), f'{next(_dump_id_iter)}_' + self.kernel_name) |
| 98 | if os.path.exists(failed_subgraph_dump_path): | 98 | if os.path.exists(failed_subgraph_dump_path): |
| 99 | shutil.rmtree(failed_subgraph_dump_path) | 99 | shutil.rmtree(failed_subgraph_dump_path) |
| 100 | shutil.copytree(subgraph_dump_path, failed_subgraph_dump_path) | 100 | shutil.copytree(subgraph_dump_path, failed_subgraph_dump_path) |
| 101 | return failed_subgraph_dump_path | 101 | return failed_subgraph_dump_path |
| 102 | 102 | ||
| 103 | def acc_compare_and_dump(self, *args, **kwargs): | 103 | def acc_compare_and_dump(self, *args, **kwargs): |
| 104 | from torch.testing._comparison import _make_mismatch_msg | 104 | from torch.testing._comparison import _make_mismatch_msg |
| 105 | self.register_fx_fallback(self.kernel_meta) | 105 | self.register_fx_fallback(self.kernel_meta) |
| 106 | launcher_fx = self.launchers[1] | 106 | launcher_fx = self.launchers[1] |
| 107 | launcher = self.launchers[0] | 107 | launcher = self.launchers[0] |
| 108 | 108 | ||
| 109 | fx_outputs = [clone_preserve_strides(arg).to(torch.float32) if arg.dtype == torch.bfloat16 \ | 109 | fx_outputs = [clone_preserve_strides(arg).to(torch.float32) if arg.dtype == torch.bfloat16 \ |
| 110 | else clone_preserve_strides(arg) for arg in args[-self.num_outputs:]] | 110 | else clone_preserve_strides(arg) for arg in args[-self.num_outputs:]] |
| 111 | fx_inputs = [clone_preserve_strides(arg) if isinstance(arg, torch.Tensor) else arg for arg in args[:-self.num_outputs]] | 111 | fx_inputs = [clone_preserve_strides(arg) if isinstance(arg, torch.Tensor) else arg for arg in args[:-self.num_outputs]] |
| 112 | fx_inputs = [inp.float() if isinstance(inp, torch.Tensor) and inp.dtype == torch.bfloat16 else inp for inp in fx_inputs] | 112 | fx_inputs = [inp.float() if isinstance(inp, torch.Tensor) and inp.dtype == torch.bfloat16 else inp for inp in fx_inputs] |
| 113 | 113 | ||
| 114 | fx_args = fx_inputs + fx_outputs | 114 | fx_args = fx_inputs + fx_outputs |
| 115 | launcher_fx(*fx_args, **kwargs) | 115 | launcher_fx(*fx_args, **kwargs) |
| 116 | 116 | ||
| 117 | if self.dynamic: | 117 | if self.dynamic: |
| 118 | args_new = self.prepare_runtime_args( | 118 | args_new = self.prepare_runtime_args( |
| 119 | list(args), | 119 | list(args), |
| 120 | ) | 120 | ) |
| 121 | else: | 121 | else: |
| 122 | args_new = args | 122 | args_new = args |
| 123 | 123 | ||
| 124 | output = launcher(*args_new, **kwargs) | 124 | output = launcher(*args_new, **kwargs) |
| 125 | 125 | ||
| 126 | has_acc_error = False | 126 | has_acc_error = False |
| 127 | num_inputs = len(args) - self.num_outputs | 127 | num_inputs = len(args) - self.num_outputs |
| 128 | for idx, (actual, expected) in enumerate(zip(args[num_inputs:], fx_outputs)): | 128 | for idx, (actual, expected) in enumerate(zip(args[num_inputs:], fx_outputs)): |
| 129 | if actual.dtype != expected.dtype: | 129 | if actual.dtype != expected.dtype: |
| 130 | expected = expected.to(actual.dtype) | 130 | expected = expected.to(actual.dtype) |
| 131 | acc_comp_tol = anir_config.acc_comp_tol.get(actual.dtype, anir_config.acc_comp_tol['default']) | 131 | acc_comp_tol = anir_config.acc_comp_tol.get(actual.dtype, anir_config.acc_comp_tol['default']) |
| 132 | rtol = acc_comp_tol['rtol'] | 132 | rtol = acc_comp_tol['rtol'] |
| 133 | atol = acc_comp_tol['atol'] | 133 | atol = acc_comp_tol['atol'] |
| 134 | matches = torch.isclose( | 134 | matches = torch.isclose( |
| 135 | actual, expected, rtol=rtol, atol=atol, equal_nan=True | 135 | actual, expected, rtol=rtol, atol=atol, equal_nan=True |
| 136 | ) | 136 | ) |
| 137 | if not matches.all(): | 137 | if not matches.all(): |
| 138 | abs_diff = abs(actual - expected) | 138 | abs_diff = abs(actual - expected) |
| 139 | rel_diff = abs_diff / abs(expected) | 139 | rel_diff = abs_diff / abs(expected) |
| 140 | rel_diff.masked_fill_(matches, 0) | 140 | rel_diff.masked_fill_(matches, 0) |
| 141 | number_of_elements = matches.numel() | 141 | number_of_elements = matches.numel() |
| 142 | total_mismatches = number_of_elements - int(torch.sum(matches)) | 142 | total_mismatches = number_of_elements - int(torch.sum(matches)) |
| 143 | extra = ( | 143 | extra = ( |
| 144 | f"Mismatched elements: {total_mismatches} / {number_of_elements} " | 144 | f"Mismatched elements: {total_mismatches} / {number_of_elements} " |
| 145 | f"({total_mismatches / number_of_elements:.1%})" | 145 | f"({total_mismatches / number_of_elements:.1%})" |
| 146 | ) | 146 | ) |
| 147 | msg = _make_mismatch_msg( | 147 | msg = _make_mismatch_msg( |
| 148 | default_identifier="Tensor-likes", | 148 | default_identifier="Tensor-likes", |
| 149 | identifier=None, | 149 | identifier=None, |
| 150 | extra=extra, | 150 | extra=extra, |
| 151 | abs_diff=abs_diff.max().item(), | 151 | abs_diff=abs_diff.max().item(), |
| 152 | abs_diff_idx=None, | 152 | abs_diff_idx=None, |
| 153 | atol=atol, | 153 | atol=atol, |
| 154 | rel_diff=rel_diff.max().item(), | 154 | rel_diff=rel_diff.max().item(), |
| 155 | rel_diff_idx=None, | 155 | rel_diff_idx=None, |
| 156 | rtol=rtol, | 156 | rtol=rtol, |
| 157 | ) | 157 | ) |
| 158 | print(f"Kernel Name: {self.kernel_name}\n{msg}", flush=True) | 158 | print(f"Kernel Name: {self.kernel_name}\n{msg}", flush=True) |
| 159 | has_acc_error = True | 159 | has_acc_error = True |
| 160 | 160 | ||
| 161 | del abs_diff | 161 | del abs_diff |
| 162 | del rel_diff | 162 | del rel_diff |
| 163 | del matches | 163 | del matches |
| 164 | del expected | 164 | del expected |
| 165 | 165 | ||
| 166 | if anir_config.fx_subgraph_dump_path: | 166 | if anir_config.fx_subgraph_dump_path: |
| 167 | data = args | 167 | data = args |
| 168 | if has_acc_error: | 168 | if has_acc_error: |
| 169 | data_dump_path = self.fx_subgraph_dump('acc_failed') | 169 | data_dump_path = self.fx_subgraph_dump('acc_failed') |
| 170 | self.data_dump_fake(*data, dump_path=data_dump_path) | 170 | self.data_dump_fake(*data, dump_path=data_dump_path) |
| 171 | del fx_inputs | 171 | del fx_inputs |
| 172 | torch.npu.synchronize() | 172 | torch.npu.synchronize() |
| 173 | self.launchers = [self.launchers[0]] | 173 | self.launchers = [self.launchers[0]] |
| 174 | self.is_fallback_kernels = [self.is_fallback_kernels[0]] | 174 | self.is_fallback_kernels = [self.is_fallback_kernels[0]] |
| 175 | 175 | ||
| 176 | return output | 176 | return output |
| 177 | 177 | ||
| 178 | def compile(self, *args, **kwargs): | 178 | def compile(self, *args, **kwargs): |
| 179 | raise NotImplementedError | 179 | raise NotImplementedError |
| 180 | 180 | ||
| 181 | def ensure_runtime_ready(self, *args, **kwargs) -> None: | 181 | def ensure_runtime_ready(self, *args, **kwargs) -> None: |
| 182 | if not self.autotuned: | 182 | if not self.autotuned: |
| 183 | if self.autotune: | 183 | if self.autotune: |
| 184 | self.register_fx_fallback(self.kernel_meta) | 184 | self.register_fx_fallback(self.kernel_meta) |
| 185 | self.autotune_to_one_config(*args, **kwargs) | 185 | self.autotune_to_one_config(*args, **kwargs) |
| 186 | self.autotuned = True | 186 | self.autotuned = True |
| 187 | 187 | ||
| 188 | def prepare_runtime_args( | 188 | def prepare_runtime_args( |
| 189 | self, | 189 | self, |
| 190 | args_list: List[Any], | 190 | args_list: List[Any], |
| 191 | ) -> List[Any]: | 191 | ) -> List[Any]: |
| 192 | args_new = () | 192 | args_new = () |
| 193 | for arg in args_list: | 193 | for arg in args_list: |
| 194 | if not torch.is_tensor(arg): | 194 | if not torch.is_tensor(arg): |
| 195 | args_new = args_new + (arg,) | 195 | args_new = args_new + (arg,) |
| 196 | continue | 196 | continue |
| 197 | args_new = args_new + (arg, arg, 0) + arg.size() + arg.stride() | 197 | args_new = args_new + (arg, arg, 0) + arg.size() + arg.stride() |
| 198 | args_list = list(args_new) | 198 | args_list = list(args_new) |
| 199 | return args_list | 199 | return args_list |
| 200 | 200 | ||
| 201 | def autotune_to_one_config(self, *args, **kwargs): | 201 | def autotune_to_one_config(self, *args, **kwargs): |
| 202 | return None | 202 | return None |
| 203 | 203 | ||
| 204 | def _normalize_contiguous_args( | 204 | def _normalize_contiguous_args( |
| 205 | self, args: List[Any] | 205 | self, args: List[Any] |
| 206 | ) -> Tuple[List[Any], Optional[List[torch.Tensor]]]: | 206 | ) -> Tuple[List[Any], Optional[List[torch.Tensor]]]: |
| 207 | meta = self.kernel_meta or {} | 207 | meta = self.kernel_meta or {} |
| 208 | num_outputs = int(meta.get("num_outputs") or 0) | 208 | num_outputs = int(meta.get("num_outputs") or 0) |
| 209 | num_call_functions = int(meta.get("num_call_functions") or 0) | 209 | num_call_functions = int(meta.get("num_call_functions") or 0) |
| 210 | 210 | ||
| 211 | if num_outputs < 0 or len(args) < num_outputs: | 211 | if num_outputs < 0 or len(args) < num_outputs: |
| 212 | return args, None | 212 | return args, None |
| 213 | 213 | ||
| 214 | num_inputs = len(args) - num_outputs | 214 | num_inputs = len(args) - num_outputs |
| 215 | 215 | ||
| 216 | if self._nc_input_indices is None: | 216 | if self._nc_input_indices is None: |
| 217 | self._nc_input_indices = [] | 217 | self._nc_input_indices = [] |
| 218 | if num_call_functions > 0: | 218 | if num_call_functions > 0: |
| 219 | input_slice = args[:num_inputs] if num_outputs > 0 else args | 219 | input_slice = args[:num_inputs] if num_outputs > 0 else args |
| 220 | for idx, arg in enumerate(input_slice): | 220 | for idx, arg in enumerate(input_slice): |
| 221 | if not isinstance(arg, torch.Tensor) or arg.is_contiguous(): | 221 | if not isinstance(arg, torch.Tensor) or arg.is_contiguous(): |
| 222 | continue | 222 | continue |
| 223 | args[idx] = arg.contiguous() | 223 | args[idx] = arg.contiguous() |
| 224 | self._nc_input_indices.append(idx) | 224 | self._nc_input_indices.append(idx) |
| 225 | else: | 225 | else: |
| 226 | for idx in self._nc_input_indices: | 226 | for idx in self._nc_input_indices: |
| 227 | if idx < len(args) and isinstance(args[idx], torch.Tensor): | 227 | if idx < len(args) and isinstance(args[idx], torch.Tensor): |
| 228 | args[idx] = args[idx].contiguous() | 228 | args[idx] = args[idx].contiguous() |
| 229 | 229 | ||
| 230 | if num_outputs == 0: | 230 | if num_outputs == 0: |
| 231 | return args, None | 231 | return args, None |
| 232 | 232 | ||
| 233 | original_outputs: Optional[List[torch.Tensor]] = None | 233 | original_outputs: Optional[List[torch.Tensor]] = None |
| 234 | if self._nc_output_indices is None: | 234 | if self._nc_output_indices is None: |
| 235 | self._nc_output_indices = [] | 235 | self._nc_output_indices = [] |
| 236 | original_outputs = [] | 236 | original_outputs = [] |
| 237 | for j, out in enumerate(args[num_inputs:]): | 237 | for j, out in enumerate(args[num_inputs:]): |
| 238 | if not isinstance(out, torch.Tensor) or out.is_contiguous(): | 238 | if not isinstance(out, torch.Tensor) or out.is_contiguous(): |
| 239 | continue | 239 | continue |
| 240 | tmp = torch.empty(out.shape, dtype=out.dtype, device=out.device) | 240 | tmp = torch.empty(out.shape, dtype=out.dtype, device=out.device) |
| 241 | out_idx = num_inputs + j | 241 | out_idx = num_inputs + j |
| 242 | original_outputs.append(out) | 242 | original_outputs.append(out) |
| 243 | args[out_idx] = tmp | 243 | args[out_idx] = tmp |
| 244 | self._nc_output_indices.append(out_idx) | 244 | self._nc_output_indices.append(out_idx) |
| 245 | else: | 245 | else: |
| 246 | original_outputs = [] | 246 | original_outputs = [] |
| 247 | for out_idx in self._nc_output_indices: | 247 | for out_idx in self._nc_output_indices: |
| 248 | tmp = torch.empty( | 248 | tmp = torch.empty( |
| 249 | args[out_idx].shape, dtype=args[out_idx].dtype, device=args[out_idx].device | 249 | args[out_idx].shape, dtype=args[out_idx].dtype, device=args[out_idx].device |
| 250 | ) | 250 | ) |
| 251 | original_outputs.append(args[out_idx]) | 251 | original_outputs.append(args[out_idx]) |
| 252 | args[out_idx] = tmp | 252 | args[out_idx] = tmp |
| 253 | 253 | ||
| 254 | return args, original_outputs | 254 | return args, original_outputs |
| 255 | 255 | ||
| 256 | def _copy_back_non_contiguous_outputs( | 256 | def _copy_back_non_contiguous_outputs( |
| 257 | self, | 257 | self, |
| 258 | args_list: List[Any], | 258 | args_list: List[Any], |
| 259 | original_outputs: Optional[List[torch.Tensor]], | 259 | original_outputs: Optional[List[torch.Tensor]], |
| 260 | ) -> None: | 260 | ) -> None: |
| 261 | if original_outputs and self._nc_output_indices: | 261 | if original_outputs and self._nc_output_indices: |
| 262 | for orig, idx in zip(original_outputs, self._nc_output_indices): | 262 | for orig, idx in zip(original_outputs, self._nc_output_indices): |
| 263 | orig.copy_(args_list[idx]) | 263 | orig.copy_(args_list[idx]) |
| 264 | 264 | ||
| 265 | 265 | ||
| 266 | def _fx_graph_call_factory(self, module: torch.nn.Module, num_outputs: int) -> Callable[..., Any]: | 266 | def _fx_graph_call_factory(self, module: torch.nn.Module, num_outputs: int) -> Callable[..., Any]: |
| 267 | def module_call(*args, **kwargs): | 267 | def module_call(*args, **kwargs): |
| 268 | actual_args = args[:-num_outputs] if num_outputs > 0 else args | 268 | actual_args = args[:-num_outputs] if num_outputs > 0 else args |
| 269 | actual_outputs = module.forward(*actual_args) | 269 | actual_outputs = module.forward(*actual_args) |
| 270 | for out1, out2 in zip(actual_outputs, args[-num_outputs:]): | 270 | for out1, out2 in zip(actual_outputs, args[-num_outputs:]): |
| 271 | if isinstance(out1, torch.Tensor) and not out1.is_contiguous(): | 271 | if isinstance(out1, torch.Tensor) and not out1.is_contiguous(): |
| 272 | out1 = out1.contiguous() | 272 | out1 = out1.contiguous() |
| 273 | out2.data = out1.data | 273 | out2.data = out1.data |
| 274 | return module_call | 274 | return module_call |
| 275 | 275 | ||
| 276 | def _load_traced_graph_model(self) -> torch.nn.Module: | 276 | def _load_traced_graph_model(self) -> torch.nn.Module: |
| 277 | meta = self.kernel_meta or {} | 277 | meta = self.kernel_meta or {} |
| 278 | traced_graph_hash = meta.get("traced_graph_hash") | 278 | traced_graph_hash = meta.get("traced_graph_hash") |
| 279 | if not traced_graph_hash: | 279 | if not traced_graph_hash: |
| 280 | raise RuntimeError("traced_graph_hash missing, cannot build FX fallback") | 280 | raise RuntimeError("traced_graph_hash missing, cannot build FX fallback") |
| 281 | 281 | ||
| 282 | traced_graph_cache = meta.get("traced_graph_cache") | 282 | traced_graph_cache = meta.get("traced_graph_cache") |
| 283 | if traced_graph_cache is None: | 283 | if traced_graph_cache is None: |
| 284 | raise RuntimeError("traced_graph_cache missing, cannot locate traced graph dump") | 284 | raise RuntimeError("traced_graph_cache missing, cannot locate traced graph dump") |
| 285 | 285 | ||
| 286 | base_cache = os.getenv("TORCHINDUCTOR_CACHE_DIR", "") | 286 | base_cache = os.getenv("TORCHINDUCTOR_CACHE_DIR", "") |
| 287 | dump_root = os.path.join(base_cache, traced_graph_cache) | 287 | dump_root = os.path.join(base_cache, traced_graph_cache) |
| 288 | dump_path = os.path.join(dump_root, str(self.device_index), traced_graph_hash) | 288 | dump_path = os.path.join(dump_root, str(self.device_index), traced_graph_hash) |
| 289 | 289 | ||
| 290 | sys.path.append(dump_path) | 290 | sys.path.append(dump_path) |
| 291 | try: | 291 | try: |
| 292 | module = importlib.import_module(traced_graph_hash) | 292 | module = importlib.import_module(traced_graph_hash) |
| 293 | finally: | 293 | finally: |
| 294 | sys.path.remove(dump_path) | 294 | sys.path.remove(dump_path) |
| 295 | 295 | ||
| 296 | Model = getattr(module, traced_graph_hash, None) | 296 | Model = getattr(module, traced_graph_hash, None) |
| 297 | if Model is None: | 297 | if Model is None: |
| 298 | raise RuntimeError("Cannot find valid graph module class in traced graph dump") | 298 | raise RuntimeError("Cannot find valid graph module class in traced graph dump") |
| 299 | 299 | ||
| 300 | return Model() | 300 | return Model() |
| 301 | 301 | ||
| 302 | def register_fx_fallback(self, kernel_meta) -> None: | 302 | def register_fx_fallback(self, kernel_meta) -> None: |
| 303 | model = self._load_traced_graph_model() | 303 | model = self._load_traced_graph_model() |
| 304 | num_outputs = kernel_meta.get("num_outputs", 0) | 304 | num_outputs = kernel_meta.get("num_outputs", 0) |
| 305 | module_call = self._fx_graph_call_factory(model, num_outputs) | 305 | module_call = self._fx_graph_call_factory(model, num_outputs) |
| 306 | self.register_launcher( | 306 | self.register_launcher( |
| 307 | module_call, | 307 | module_call, |
| 308 | kernel_path=self.kernel_name + "_fx_fallback", | 308 | kernel_path=self.kernel_name + "_fx_fallback", |
| 309 | is_fallback_kernel=True, | 309 | is_fallback_kernel=True, |
| 310 | ) | 310 | ) |
| 311 | 311 | ||
| 312 | def run(self, *args, **kwargs): | 312 | def run(self, *args, **kwargs): |
| 313 | args_list = list(args) | 313 | args_list = list(args) |
| 314 | args_list, original_outputs = self._normalize_contiguous_args(args_list) | 314 | args_list, original_outputs = self._normalize_contiguous_args(args_list) |
| 315 | self.ensure_runtime_ready(*args_list, **kwargs) | 315 | self.ensure_runtime_ready(*args_list, **kwargs) |
| 316 | 316 | ||
| 317 | launcher_idx = self.get_primary_launcher_index() | 317 | launcher_idx = self.get_primary_launcher_index() |
| 318 | launcher = self.launchers[launcher_idx] | 318 | launcher = self.launchers[launcher_idx] |
| 319 | is_fallback_kernel = self.is_fallback_kernels[launcher_idx] | 319 | is_fallback_kernel = self.is_fallback_kernels[launcher_idx] |
| 320 | if anir_config.online_acc_comp and not is_fallback_kernel: | 320 | if anir_config.online_acc_comp and not is_fallback_kernel: |
| 321 | output = self.acc_compare_and_dump(*args_list, **kwargs) | 321 | output = self.acc_compare_and_dump(*args_list, **kwargs) |
| 322 | self._copy_back_non_contiguous_outputs(args_list, original_outputs) | 322 | self._copy_back_non_contiguous_outputs(args_list, original_outputs) |
| 323 | return output | 323 | return output |
| 324 | if self.dynamic and not is_fallback_kernel: | 324 | if self.dynamic and not is_fallback_kernel: |
| 325 | args_list = self.prepare_runtime_args( | 325 | args_list = self.prepare_runtime_args( |
| 326 | args_list, | 326 | args_list, |
| 327 | ) | 327 | ) |
| 328 | ret = launcher(*tuple(args_list), **kwargs) | 328 | ret = launcher(*tuple(args_list), **kwargs) |
| 329 | self._copy_back_non_contiguous_outputs(args_list, original_outputs) | 329 | self._copy_back_non_contiguous_outputs(args_list, original_outputs) |
| 330 | return ret | 330 | return ret |
| @@ -1,598 +1,598 @@ | |||
| 1 | <svg host="65bd71144e" xmlns="http://www.w3.org/2000/svg" style="background: transparent; background-color: transparent;" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="1111px" height="601px" viewBox="-0.5 -0.5 1111 601" content="<mxfile><diagram id="uiDLYcUB2_ILZagOPzox" name="Page-1">7Vxbc5s4FP41THd3Jh7ul0d8SZuZps0k6Wz75MEg29oC8mI5sffXrwTCIBAOtnGcZMxDgo4uCH0fR+ccSZa0QbT+nHiL+S0KQCipcrCWtKGkqoqlkL9UsMkEhmZlglkCA1amEDzA/wATyky6ggFYcgUxQiGGC17oozgGPuZkXpKgZ77YFIX8UxfeDNQED74X1qV/wwDPM6ltyIX8C4Czef5kRWY5kZcXZoLl3AvQc0mkjSRtkCCEs7toPQAhHbt8XLJ61w25244lIMZtKqhZhScvXLF3Y/3Cm/xlE7SKA0DLy5LWf55DDB4Wnk9znwm6RDbHUUhSCrn1QjiLyb1Png8SIngCCYZk5FyWMUEYo4hkTFGMr70IhpQDX0D4BGg5lsEgV1SWHqAQJWl3tOyij1phtMzK0Y4lgCS8Sdpr2pMpDMNStev0IvIlTtBvUMox04vkhN4EhHekTQyR8B2+VgpgRN++PuQMBVoNrEsiBsFngCKAkw0pkufKOUE2PNGfC3YpSi6cl6hl5hU9RunZtvECdXLDgBeTQKthDgLCd5ZECZ6jGYq9cFRI+zwrijJfER2UFIF/AMYbhiQFi2cKGa1k85PW7xl58hdrLk0M11xqw1IH8SYAU28VYp41ioA1jWAu0Srx2egYTOV4yQzkpexMRgduJ+IJCD0Mn3hFcgx4+gW8o8FzzgWeIVC/ZvqqdAA4WM1/VyjPuMoGwSUFFH2xTl89zyd3s/T/yJJcU+qTG1NyDMnVpJEtuSOpfy2NSHIoOTYt01fSMhbNcqw8S877Qd4g6wpr9bSzQwSDIGVoa6LsNwsEHrCnft4MewtFOCv4NphMO1Lvms2rd1UW6HdVoN71DrS7dVEQeymIfJotawjVPJeGsC/oHY+e0TV6aVU3SbxNqcACwRgvSy3fUUFJDZgOrwZ0Xe8ZvHlerWJrxu4q5CbrR8Gm7Qu1IphzIdjxBJPfLcEM/dQEywdMYOQsF158lJETIi8gz19ilABSbvjwtWS3ZK1/CLtlavvAb2W3TGyCqNyR3WJqPJ80MyfHq1guilZjDg57DHNyx2CvIEteDh8KXxUG9sWRdo2+ZAzrKoQf/xjFoAJeLjpOcfGwd4CsUbFINU2ErCMAVusC2Ga/J4BPxTebiSZCFTHx/N+z9Cu+8rPxopoCxhBDLxQqCxwtym7NZPscbcho9QeMFziR1P7mz+3Dk2p3SNVSJyHRInRQ16SW/Vepyy88sz1rQzDFb5ezeiNnO1FAeiUuZgkcJ13AUqMLltr7T1wUxqtsAqF0jBGbTAT+eXeOvkMd975OHX17JLmGwNFv8uY/4mR5Jie/Olmq9is6+XkI681z1ZKc/oWr5+aqYp6Rq2qHDoHcnsQPN7ekQTkNh5pS36E3riLZ9sdk1plcBr3CLEMVugzWqbilCLhVjW/EgUsXgQtLJ/CW8xRYhQeRR/y0wQewhvgnexK9T8MhPcsxWLqIiNDEppS4AwkkA0VZVoRMsuCKQ9RpObxC2tOk3SGWNFVtspEZL0Y8SqAbO+y0/QIj9TBGdQHVkJWeyreSBXFYxR0hEdKY1bMsZ3tZXMu64/Sc8qXzj8niQrXHHBA4yalb4vIARQsYElhG11J/QHTXI1F6phdR5ZP9/Xb34+rmPs+vB/fer3fc7Gl07h3LFVPO0PW6CrNP5Byr9ajHTRysfEze+oLmIWgaFWPH0gRTkmhG6gROvQanS8YVr+KPFLp6RTjNiqrXFZGFcbKvszl0xRuPnTlfMA5gAnw8ZoHPbZqFPxuM1zfo/OUzVTxZLniPrpWjt1MsiBu+pXcnzscjcz5cm7od1AsZpm7vDndkv2HhhuDiu+yvWSzjvL6L+YJqOY1fnG4CciX3OuPoxRfubvlMb8Un+VR8OiB+vZtPDEiaw1YlhNOVYEFk3+np+H5s2lL5/Rpd7dYL5S6ovFV6+UKMIwgYiojcic1VD25f1oG7MaaV6qKFUcf1VKvAWj0O7GfxjXGEAl4fLGGExygON9yHXrX8aCnSYFZOxjCE8UxKj4FM4aylpfReF2JfkTSmWvHA9Fe1k7R6jHc3bzCIFqGHQRvuFGUvtOmYNrbeijYiddMJbepxtV20CV5iS3BRMafjil4N8oi5Itr60QlX6kG7IgYrtzpa8XHMEfMVYa9sYBVYI4poGakTc8SqYe7S2gRd8sf4QIC+5j5DpQKoVV9JOVXkXav7vxcYD7T4TKMKo0Adi7RxJ0DW/b8LkAfaYMo5gdTrKrYGY/n0CRvE8sGRfJdEL98X8auU07BH4sDDJlnXKjscXj4fwrQOd8BUFqPS1XGQIzYZ6HVn6ubb8Mfg8fv9mNzc3I8Gj+Pb0e33+1/j2+9D0sbwEzV+x6lnFcH1p8uHeNBaQ8XAFW/A1072Jdb15+4PrxX3dbPO/dOcvavvANIrkSylugW8Yf/PId9M80KNYBmSrR3620WGwq3U5PRqXndsHR9Xdx2QKk55p7uCHNfDIO6BaAKCgLqvI02yZclWUnEWv+dEMw/P6Z6jWrHxEoT091VeXJpsWMgUjNZbG8AstiwewWwMFivMD83S9zBVc52Myol0q9ylbu1CH6rVkx6Cn0DRO1opI8niF3ay77/4mSJt9D8=</diagram></mxfile>"> | 1 | <svg host="65bd71144e" xmlns="http://www.w3.org/2000/svg" style="background: transparent; background-color: transparent;" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="1111px" height="601px" viewBox="-0.5 -0.5 1111 601" content="<mxfile><diagram id="uiDLYcUB2_ILZagOPzox" name="Page-1">7Vxbc5s4FP41THd3Jh7ul0d8SZuZps0k6Wz75MEg29oC8mI5sffXrwTCIBAOtnGcZMxDgo4uCH0fR+ccSZa0QbT+nHiL+S0KQCipcrCWtKGkqoqlkL9UsMkEhmZlglkCA1amEDzA/wATyky6ggFYcgUxQiGGC17oozgGPuZkXpKgZ77YFIX8UxfeDNQED74X1qV/wwDPM6ltyIX8C4Czef5kRWY5kZcXZoLl3AvQc0mkjSRtkCCEs7toPQAhHbt8XLJ61w25244lIMZtKqhZhScvXLF3Y/3Cm/xlE7SKA0DLy5LWf55DDB4Wnk9znwm6RDbHUUhSCrn1QjiLyb1Png8SIngCCYZk5FyWMUEYo4hkTFGMr70IhpQDX0D4BGg5lsEgV1SWHqAQJWl3tOyij1phtMzK0Y4lgCS8Sdpr2pMpDMNStev0IvIlTtBvUMox04vkhN4EhHekTQyR8B2+VgpgRN++PuQMBVoNrEsiBsFngCKAkw0pkufKOUE2PNGfC3YpSi6cl6hl5hU9RunZtvECdXLDgBeTQKthDgLCd5ZECZ6jGYq9cFRI+zwrijJfER2UFIF/AMYbhiQFi2cKGa1k85PW7xl58hdrLk0M11xqw1IH8SYAU28VYp41ioA1jWAu0Srx2egYTOV4yQzkpexMRgduJ+IJCD0Mn3hFcgx4+gW8o8FzzgWeIVC/ZvqqdAA4WM1/VyjPuMoGwSUFFH2xTl89zyd3s/T/yJJcU+qTG1NyDMnVpJEtuSOpfy2NSHIoOTYt01fSMhbNcqw8S877Qd4g6wpr9bSzQwSDIGVoa6LsNwsEHrCnft4MewtFOCv4NphMO1Lvms2rd1UW6HdVoN71DrS7dVEQeymIfJotawjVPJeGsC/oHY+e0TV6aVU3SbxNqcACwRgvSy3fUUFJDZgOrwZ0Xe8ZvHlerWJrxu4q5CbrR8Gm7Qu1IphzIdjxBJPfLcEM/dQEywdMYOQsF158lJETIi8gz19ilABSbvjwtWS3ZK1/CLtlavvAb2W3TGyCqNyR3WJqPJ80MyfHq1guilZjDg57DHNyx2CvIEteDh8KXxUG9sWRdo2+ZAzrKoQf/xjFoAJeLjpOcfGwd4CsUbFINU2ErCMAVusC2Ga/J4BPxTebiSZCFTHx/N+z9Cu+8rPxopoCxhBDLxQqCxwtym7NZPscbcho9QeMFziR1P7mz+3Dk2p3SNVSJyHRInRQ16SW/Vepyy88sz1rQzDFb5ezeiNnO1FAeiUuZgkcJ13AUqMLltr7T1wUxqtsAqF0jBGbTAT+eXeOvkMd975OHX17JLmGwNFv8uY/4mR5Jie/Olmq9is6+XkI681z1ZKc/oWr5+aqYp6Rq2qHDoHcnsQPN7ekQTkNh5pS36E3riLZ9sdk1plcBr3CLEMVugzWqbilCLhVjW/EgUsXgQtLJ/CW8xRYhQeRR/y0wQewhvgnexK9T8MhPcsxWLqIiNDEppS4AwkkA0VZVoRMsuCKQ9RpObxC2tOk3SGWNFVtspEZL0Y8SqAbO+y0/QIj9TBGdQHVkJWeyreSBXFYxR0hEdKY1bMsZ3tZXMu64/Sc8qXzj8niQrXHHBA4yalb4vIARQsYElhG11J/QHTXI1F6phdR5ZP9/Xb34+rmPs+vB/fer3fc7Gl07h3LFVPO0PW6CrNP5Byr9ajHTRysfEze+oLmIWgaFWPH0gRTkmhG6gROvQanS8YVr+KPFLp6RTjNiqrXFZGFcbKvszl0xRuPnTlfMA5gAnw8ZoHPbZqFPxuM1zfo/OUzVTxZLniPrpWjt1MsiBu+pXcnzscjcz5cm7od1AsZpm7vDndkv2HhhuDiu+yvWSzjvL6L+YJqOY1fnG4CciX3OuPoxRfubvlMb8Un+VR8OiB+vZtPDEiaw1YlhNOVYEFk3+np+H5s2lL5/Rpd7dYL5S6ovFV6+UKMIwgYiojcic1VD25f1oG7MaaV6qKFUcf1VKvAWj0O7GfxjXGEAl4fLGGExygON9yHXrX8aCnSYFZOxjCE8UxKj4FM4aylpfReF2JfkTSmWvHA9Fe1k7R6jHc3bzCIFqGHQRvuFGUvtOmYNrbeijYiddMJbepxtV20CV5iS3BRMafjil4N8oi5Itr60QlX6kG7IgYrtzpa8XHMEfMVYa9sYBVYI4poGakTc8SqYe7S2gRd8sf4QIC+5j5DpQKoVV9JOVXkXav7vxcYD7T4TKMKo0Adi7RxJ0DW/b8LkAfaYMo5gdTrKrYGY/n0CRvE8sGRfJdEL98X8auU07BH4sDDJlnXKjscXj4fwrQOd8BUFqPS1XGQIzYZ6HVn6ubb8Mfg8fv9mNzc3I8Gj+Pb0e33+1/j2+9D0sbwEzV+x6lnFcH1p8uHeNBaQ8XAFW/A1072Jdb15+4PrxX3dbPO/dOcvavvANIrkSylugW8Yf/PId9M80KNYBmSrR3620WGwq3U5PRqXndsHR9Xdx2QKk55p7uCHNfDIO6BaAKCgLqvI02yZclWUnEWv+dEMw/P6Z6jWrHxEoT091VeXJpsWMgUjNZbG8AstiwewWwMFivMD83S9zBVc52Myol0q9ylbu1CH6rVkx6Cn0DRO1opI8niF3ay77/4mSJt9D8=</diagram></mxfile>"> |
| 2 | <defs/> | 2 | <defs/> |
| 3 | <g> | 3 | <g> |
| 4 | <g> | 4 | <g> |
| 5 | <rect x="0" y="0" width="1110" height="600" fill="#ffffff" stroke="#666666" pointer-events="all" style="fill: light-dark(rgb(255, 255, 255), rgb(18, 18, 18)); stroke: light-dark(rgb(102, 102, 102), rgb(149, 149, 149));"/> | 5 | <rect x="0" y="0" width="1110" height="600" fill="#ffffff" stroke="#666666" pointer-events="all" style="fill: light-dark(rgb(255, 255, 255), rgb(18, 18, 18)); stroke: light-dark(rgb(102, 102, 102), rgb(149, 149, 149));"/> |
| 6 | </g> | 6 | </g> |
| 7 | <g> | 7 | <g> |
| 8 | <path d="M 500 210 L 690 210 L 690 263.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/> | 8 | <path d="M 500 210 L 690 210 L 690 263.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/> |
| 9 | <path d="M 690 268.88 L 686.5 261.88 L 690 263.63 L 693.5 261.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/> | 9 | <path d="M 690 268.88 L 686.5 261.88 L 690 263.63 L 693.5 261.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/> |
| 10 | </g> | 10 | </g> |
| 11 | <g> | 11 | <g> |
| 12 | <path d="M 380 210 L 220 210 L 220 263.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/> | 12 | <path d="M 380 210 L 220 210 L 220 263.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/> |
| 13 | <path d="M 220 268.88 L 216.5 261.88 L 220 263.63 L 223.5 261.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/> | 13 | <path d="M 220 268.88 L 216.5 261.88 L 220 263.63 L 223.5 261.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/> |
| 14 | </g> | 14 | </g> |
| 15 | <g> | 15 | <g> |
| 16 | <rect x="380" y="190" width="120" height="40" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all" style="fill: light-dark(rgb(218, 232, 252), rgb(29, 41, 59)); stroke: light-dark(rgb(108, 142, 191), rgb(92, 121, 163));"/> | 16 | <rect x="380" y="190" width="120" height="40" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all" style="fill: light-dark(rgb(218, 232, 252), rgb(29, 41, 59)); stroke: light-dark(rgb(108, 142, 191), rgb(92, 121, 163));"/> |
| 17 | </g> | 17 | </g> |
| 18 | <g> | 18 | <g> |
| 19 | <g transform="translate(-0.5 -0.5)"> | 19 | <g transform="translate(-0.5 -0.5)"> |
| 20 | <switch> | 20 | <switch> |
| 21 | <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> | 21 | <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> |
| 22 | <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 210px; margin-left: 381px;"> | 22 | <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 210px; margin-left: 381px;"> |
| 23 | <div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; "> | 23 | <div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; "> |
| 24 | <div style="display: inline-block; font-size: 12px; font-family: "Helvetica"; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: normal; word-wrap: normal; "> | 24 | <div style="display: inline-block; font-size: 12px; font-family: "Helvetica"; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: normal; word-wrap: normal; "> |
| 25 | <font style="font-size: 14px;"> | 25 | <font style="font-size: 14px;"> |
| 26 | 离散访存类算子 | 26 | 离散访存类算子 |
| 27 | </font> | 27 | </font> |
| 28 | </div> | 28 | </div> |
| 29 | </div> | 29 | </div> |
| 30 | </div> | 30 | </div> |
| 31 | </foreignObject> | 31 | </foreignObject> |
| 32 | <text x="440" y="214" fill="light-dark(#000000, #ffffff)" font-family=""Helvetica"" font-size="12px" text-anchor="middle" font-weight="bold"> | 32 | <text x="440" y="214" fill="light-dark(#000000, #ffffff)" font-family=""Helvetica"" font-size="12px" text-anchor="middle" font-weight="bold"> |
| 33 | 离散访存类算子 | 33 | 离散访存类算子 |
| 34 | </text> | 34 | </text> |
| 35 | </switch> | 35 | </switch> |
| 36 | </g> | 36 | </g> |
| 37 | </g> | 37 | </g> |
| 38 | <g> | 38 | <g> |
| 39 | <path d="M 690 394.5 L 690 508.13" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/> | 39 | <path d="M 690 394.5 L 690 508.13" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/> |
| 40 | <path d="M 690 513.38 L 686.5 506.38 L 690 508.13 L 693.5 506.38 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/> | 40 | <path d="M 690 513.38 L 686.5 506.38 L 690 508.13 L 693.5 506.38 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/> |
| 41 | </g> | 41 | </g> |
| 42 | <g> | 42 | <g> |
| 43 | <path d="M 690 394.5 L 690 434.5 L 835 434.5 L 835 508.13" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/> | 43 | <path d="M 690 394.5 L 690 434.5 L 835 434.5 L 835 508.13" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/> |
| 44 | <path d="M 835 513.38 L 831.5 506.38 L 835 508.13 L 838.5 506.38 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/> | 44 | <path d="M 835 513.38 L 831.5 506.38 L 835 508.13 L 838.5 506.38 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/> |
| 45 | </g> | 45 | </g> |
| 46 | <g> | 46 | <g> |
| 47 | <path d="M 690 394.5 L 690 434.5 L 545 434.5 L 545 508.13" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/> | 47 | <path d="M 690 394.5 L 690 434.5 L 545 434.5 L 545 508.13" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/> |
| 48 | <path d="M 545 513.38 L 541.5 506.38 L 545 508.13 L 548.5 506.38 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/> | 48 | <path d="M 545 513.38 L 541.5 506.38 L 545 508.13 L 548.5 506.38 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/> |
| 49 | </g> | 49 | </g> |
| 50 | <g> | 50 | <g> |
| 51 | <rect x="630" y="354.5" width="120" height="40" fill="#f8cecc" stroke="#b85450" pointer-events="all" style="fill: light-dark(rgb(248, 206, 204), rgb(81, 45, 43)); stroke: light-dark(rgb(184, 84, 80), rgb(215, 129, 126));"/> | 51 | <rect x="630" y="354.5" width="120" height="40" fill="#f8cecc" stroke="#b85450" pointer-events="all" style="fill: light-dark(rgb(248, 206, 204), rgb(81, 45, 43)); stroke: light-dark(rgb(184, 84, 80), rgb(215, 129, 126));"/> |
| 52 | </g> | 52 | </g> |
| 53 | <g> | 53 | <g> |
| 54 | <g transform="translate(-0.5 -0.5)"> | 54 | <g transform="translate(-0.5 -0.5)"> |
| 55 | <switch> | 55 | <switch> |
| 56 | <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> | 56 | <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> |
| 57 | <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 375px; margin-left: 631px;"> | 57 | <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 375px; margin-left: 631px;"> |
| 58 | <div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; "> | 58 | <div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; "> |
| 59 | <div style="display: inline-block; font-size: 12px; font-family: "Helvetica"; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: normal; word-wrap: normal; "> | 59 | <div style="display: inline-block; font-size: 12px; font-family: "Helvetica"; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: normal; word-wrap: normal; "> |
| 60 | <span style="font-size: 14px;"> | 60 | <span style="font-size: 14px;"> |
| 61 | load/store DSL | 61 | load/store DSL |
| 62 | </span> | 62 | </span> |
| 63 | </div> | 63 | </div> |
| 64 | </div> | 64 | </div> |
| 65 | </div> | 65 | </div> |
| 66 | </foreignObject> | 66 | </foreignObject> |
| 67 | <text x="690" y="378" fill="light-dark(#000000, #ffffff)" font-family=""Helvetica"" font-size="12px" text-anchor="middle" font-weight="bold"> | 67 | <text x="690" y="378" fill="light-dark(#000000, #ffffff)" font-family=""Helvetica"" font-size="12px" text-anchor="middle" font-weight="bold"> |
| 68 | load/store DSL | 68 | load/store DSL |
| 69 | </text> | 69 | </text> |
| 70 | </switch> | 70 | </switch> |
| 71 | </g> | 71 | </g> |
| 72 | </g> | 72 | </g> |
| 73 | <g> | 73 | <g> |
| 74 | <rect x="580" y="324.5" width="90" height="30" fill="none" stroke="none" pointer-events="all"/> | 74 | <rect x="580" y="324.5" width="90" height="30" fill="none" stroke="none" pointer-events="all"/> |
| 75 | </g> | 75 | </g> |
| 76 | <g> | 76 | <g> |
| 77 | <g transform="translate(-0.5 -0.5)"> | 77 | <g transform="translate(-0.5 -0.5)"> |
| 78 | <switch> | 78 | <switch> |
| 79 | <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> | 79 | <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> |
| 80 | <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 340px; margin-left: 625px;"> | 80 | <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 340px; margin-left: 625px;"> |
| 81 | <div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; "> | 81 | <div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; "> |
| 82 | <div style="display: inline-block; font-size: 12px; font-family: "Helvetica"; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: nowrap; "> | 82 | <div style="display: inline-block; font-size: 12px; font-family: "Helvetica"; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: nowrap; "> |
| 83 | tl.load/tl.store | 83 | tl.load/tl.store |
| 84 | </div> | 84 | </div> |
| 85 | </div> | 85 | </div> |
| 86 | </div> | 86 | </div> |
| 87 | </foreignObject> | 87 | </foreignObject> |
| 88 | <text x="625" y="343" fill="light-dark(#000000, #ffffff)" font-family=""Helvetica"" font-size="12px" text-anchor="middle" font-weight="bold"> | 88 | <text x="625" y="343" fill="light-dark(#000000, #ffffff)" font-family=""Helvetica"" font-size="12px" text-anchor="middle" font-weight="bold"> |
| 89 | tl.load/tl.store | 89 | tl.load/tl.store |
| 90 | </text> | 90 | </text> |
| 91 | </switch> | 91 | </switch> |
| 92 | </g> | 92 | </g> |
| 93 | </g> | 93 | </g> |
| 94 | <g> | 94 | <g> |
| 95 | <rect x="640" y="160" width="140" height="50" fill="none" stroke="none" pointer-events="all"/> | 95 | <rect x="640" y="160" width="140" height="50" fill="none" stroke="none" pointer-events="all"/> |
| 96 | </g> | 96 | </g> |
| 97 | <g> | 97 | <g> |
| 98 | <g transform="translate(-0.5 -0.5)"> | 98 | <g transform="translate(-0.5 -0.5)"> |
| 99 | <switch> | 99 | <switch> |
| 100 | <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> | 100 | <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> |
| 101 | <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 1px; height: 1px; padding-top: 185px; margin-left: 642px;"> | 101 | <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 1px; height: 1px; padding-top: 185px; margin-left: 642px;"> |
| 102 | <div style="box-sizing: border-box; font-size: 0; text-align: left; color: #000000; "> | 102 | <div style="box-sizing: border-box; font-size: 0; text-align: left; color: #000000; "> |
| 103 | <div style="display: inline-block; font-size: 14px; font-family: "Helvetica"; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; white-space: nowrap; "> | 103 | <div style="display: inline-block; font-size: 14px; font-family: "Helvetica"; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; white-space: nowrap; "> |
| 104 | <div> | 104 | <div> |
| 105 | <b style="background-color: initial;"> | 105 | <b style="background-color: initial;"> |
| 106 | tmp0 | 106 | tmp0 |
| 107 | </b> | 107 | </b> |
| 108 | =tl.load(inptr+y) | 108 | =tl.load(inptr+y) |
| 109 | <br/> | 109 | <br/> |
| 110 | </div> | 110 | </div> |
| 111 | index=x+8* | 111 | index=x+8* |
| 112 | <b> | 112 | <b> |
| 113 | tmp0 | 113 | tmp0 |
| 114 | </b> | 114 | </b> |
| 115 | </div> | 115 | </div> |
| 116 | </div> | 116 | </div> |
| 117 | </div> | 117 | </div> |
| 118 | </foreignObject> | 118 | </foreignObject> |
| 119 | <text x="642" y="189" fill="light-dark(#000000, #ffffff)" font-family=""Helvetica"" font-size="14px"> | 119 | <text x="642" y="189" fill="light-dark(#000000, #ffffff)" font-family=""Helvetica"" font-size="14px"> |
| 120 | tmp0=tl.load(inptr+y... | 120 | tmp0=tl.load(inptr+y... |
| 121 | </text> | 121 | </text> |
| 122 | </switch> | 122 | </switch> |
| 123 | </g> | 123 | </g> |
| 124 | </g> | 124 | </g> |
| 125 | <g> | 125 | <g> |
| 126 | <rect x="630" y="270" width="120" height="40" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all" style="fill: light-dark(rgb(218, 232, 252), rgb(29, 41, 59)); stroke: light-dark(rgb(108, 142, 191), rgb(92, 121, 163));"/> | 126 | <rect x="630" y="270" width="120" height="40" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all" style="fill: light-dark(rgb(218, 232, 252), rgb(29, 41, 59)); stroke: light-dark(rgb(108, 142, 191), rgb(92, 121, 163));"/> |
| 127 | </g> | 127 | </g> |
| 128 | <g> | 128 | <g> |
| 129 | <g transform="translate(-0.5 -0.5)"> | 129 | <g transform="translate(-0.5 -0.5)"> |
| 130 | <switch> | 130 | <switch> |
| 131 | <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> | 131 | <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> |
| 132 | <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 290px; margin-left: 631px;"> | 132 | <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 290px; margin-left: 631px;"> |
| 133 | <div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; "> | 133 | <div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; "> |
| 134 | <div style="display: inline-block; font-size: 12px; font-family: "Helvetica"; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: normal; word-wrap: normal; "> | 134 | <div style="display: inline-block; font-size: 12px; font-family: "Helvetica"; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: normal; word-wrap: normal; "> |
| 135 | <span style="text-wrap: nowrap;"> | 135 | <span style="text-wrap: nowrap;"> |
| 136 | <font style="font-size: 14px;"> | 136 | <font style="font-size: 14px;"> |
| 137 | 间接访存 | 137 | 间接访存 |
| 138 | </font> | 138 | </font> |
| 139 | </span> | 139 | </span> |
| 140 | </div> | 140 | </div> |
| 141 | </div> | 141 | </div> |
| 142 | </div> | 142 | </div> |
| 143 | </foreignObject> | 143 | </foreignObject> |
| 144 | <text x="690" y="294" fill="light-dark(#000000, #ffffff)" font-family=""Helvetica"" font-size="12px" text-anchor="middle" font-weight="bold"> | 144 | <text x="690" y="294" fill="light-dark(#000000, #ffffff)" font-family=""Helvetica"" font-size="12px" text-anchor="middle" font-weight="bold"> |
| 145 | 间接访存 | 145 | 间接访存 |
| 146 | </text> | 146 | </text> |
| 147 | </switch> | 147 | </switch> |
| 148 | </g> | 148 | </g> |
| 149 | </g> | 149 | </g> |
| 150 | <g> | 150 | <g> |
| 151 | <rect x="160" y="270" width="120" height="40" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all" style="fill: light-dark(rgb(218, 232, 252), rgb(29, 41, 59)); stroke: light-dark(rgb(108, 142, 191), rgb(92, 121, 163));"/> | 151 | <rect x="160" y="270" width="120" height="40" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all" style="fill: light-dark(rgb(218, 232, 252), rgb(29, 41, 59)); stroke: light-dark(rgb(108, 142, 191), rgb(92, 121, 163));"/> |
| 152 | </g> | 152 | </g> |
| 153 | <g> | 153 | <g> |
| 154 | <g transform="translate(-0.5 -0.5)"> | 154 | <g transform="translate(-0.5 -0.5)"> |
| 155 | <switch> | 155 | <switch> |
| 156 | <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> | 156 | <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> |
| 157 | <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 290px; margin-left: 161px;"> | 157 | <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 290px; margin-left: 161px;"> |
| 158 | <div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; "> | 158 | <div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; "> |
| 159 | <div style="display: inline-block; font-size: 12px; font-family: "Helvetica"; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: normal; word-wrap: normal; "> | 159 | <div style="display: inline-block; font-size: 12px; font-family: "Helvetica"; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: normal; word-wrap: normal; "> |
| 160 | <span style="text-wrap: nowrap;"> | 160 | <span style="text-wrap: nowrap;"> |
| 161 | <font style="font-size: 14px;"> | 161 | <font style="font-size: 14px;"> |
| 162 | 直接访存 | 162 | 直接访存 |
| 163 | </font> | 163 | </font> |
| 164 | </span> | 164 | </span> |
| 165 | </div> | 165 | </div> |
| 166 | </div> | 166 | </div> |
| 167 | </div> | 167 | </div> |
| 168 | </foreignObject> | 168 | </foreignObject> |
| 169 | <text x="220" y="294" fill="light-dark(#000000, #ffffff)" font-family=""Helvetica"" font-size="12px" text-anchor="middle" font-weight="bold"> | 169 | <text x="220" y="294" fill="light-dark(#000000, #ffffff)" font-family=""Helvetica"" font-size="12px" text-anchor="middle" font-weight="bold"> |
| 170 | 直接访存 | 170 | 直接访存 |
| 171 | </text> | 171 | </text> |
| 172 | </switch> | 172 | </switch> |
| 173 | </g> | 173 | </g> |
| 174 | </g> | 174 | </g> |
| 175 | <g> | 175 | <g> |
| 176 | <rect x="460" y="514.5" width="170" height="40" fill="#f8cecc" stroke="#b85450" pointer-events="all" style="fill: light-dark(rgb(248, 206, 204), rgb(81, 45, 43)); stroke: light-dark(rgb(184, 84, 80), rgb(215, 129, 126));"/> | 176 | <rect x="460" y="514.5" width="170" height="40" fill="#f8cecc" stroke="#b85450" pointer-events="all" style="fill: light-dark(rgb(248, 206, 204), rgb(81, 45, 43)); stroke: light-dark(rgb(184, 84, 80), rgb(215, 129, 126));"/> |
| 177 | </g> | 177 | </g> |
| 178 | <g> | 178 | <g> |
| 179 | <g transform="translate(-0.5 -0.5)"> | 179 | <g transform="translate(-0.5 -0.5)"> |
| 180 | <switch> | 180 | <switch> |
| 181 | <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> | 181 | <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> |
| 182 | <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 168px; height: 1px; padding-top: 535px; margin-left: 461px;"> | 182 | <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 168px; height: 1px; padding-top: 535px; margin-left: 461px;"> |
| 183 | <div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; "> | 183 | <div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; "> |
| 184 | <div style="display: inline-block; font-size: 12px; font-family: "Helvetica"; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: normal; word-wrap: normal; "> | 184 | <div style="display: inline-block; font-size: 12px; font-family: "Helvetica"; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: normal; word-wrap: normal; "> |
| 185 | <span style="font-size: 14px; text-wrap: nowrap;"> | 185 | <span style="font-size: 14px; text-wrap: nowrap;"> |
| 186 | SIMD 方案 | 186 | SIMD 方案 |
| 187 | </span> | 187 | </span> |
| 188 | </div> | 188 | </div> |
| 189 | </div> | 189 | </div> |
| 190 | </div> | 190 | </div> |
| 191 | </foreignObject> | 191 | </foreignObject> |
| 192 | <text x="545" y="538" fill="light-dark(#000000, #ffffff)" font-family=""Helvetica"" font-size="12px" text-anchor="middle" font-weight="bold"> | 192 | <text x="545" y="538" fill="light-dark(#000000, #ffffff)" font-family=""Helvetica"" font-size="12px" text-anchor="middle" font-weight="bold"> |
| 193 | SIMD 方案 | 193 | SIMD 方案 |
| 194 | </text> | 194 | </text> |
| 195 | </switch> | 195 | </switch> |
| 196 | </g> | 196 | </g> |
| 197 | </g> | 197 | </g> |
| 198 | <g> | 198 | <g> |
| 199 | <path d="M 0 491.2 L 1107.78 490" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/> | 199 | <path d="M 0 491.2 L 1107.78 490" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/> |
| 200 | </g> | 200 | </g> |
| 201 | <g> | 201 | <g> |
| 202 | <rect x="30" y="534" width="180" height="30" fill="none" stroke="none" pointer-events="all"/> | 202 | <rect x="30" y="534" width="180" height="30" fill="none" stroke="none" pointer-events="all"/> |
| 203 | </g> | 203 | </g> |
| 204 | <g> | 204 | <g> |
| 205 | <g transform="translate(-0.5 -0.5)"> | 205 | <g transform="translate(-0.5 -0.5)"> |
| 206 | <switch> | 206 | <switch> |
| 207 | <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> | 207 | <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> |
| 208 | <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 549px; margin-left: 120px;"> | 208 | <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 549px; margin-left: 120px;"> |
| 209 | <div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; "> | 209 | <div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; "> |
| 210 | <div style="display: inline-block; font-size: 14px; font-family: "Helvetica"; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: nowrap; "> | 210 | <div style="display: inline-block; font-size: 14px; font-family: "Helvetica"; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: nowrap; "> |
| 211 | Compiler(TA&NPU-IR) | 211 | Compiler(TA&NPU-IR) |
| 212 | </div> | 212 | </div> |
| 213 | </div> | 213 | </div> |
| 214 | </div> | 214 | </div> |
| 215 | </foreignObject> | 215 | </foreignObject> |
| 216 | <text x="120" y="553" fill="light-dark(#000000, #ffffff)" font-family=""Helvetica"" font-size="14px" text-anchor="middle" font-weight="bold"> | 216 | <text x="120" y="553" fill="light-dark(#000000, #ffffff)" font-family=""Helvetica"" font-size="14px" text-anchor="middle" font-weight="bold"> |
| 217 | Compiler(TA&NPU-IR) | 217 | Compiler(TA&NPU-IR) |
| 218 | </text> | 218 | </text> |
| 219 | </switch> | 219 | </switch> |
| 220 | </g> | 220 | </g> |
| 221 | </g> | 221 | </g> |
| 222 | <g> | 222 | <g> |
| 223 | <rect x="50" y="263.5" width="70" height="30" fill="none" stroke="none" pointer-events="all"/> | 223 | <rect x="50" y="263.5" width="70" height="30" fill="none" stroke="none" pointer-events="all"/> |
| 224 | </g> | 224 | </g> |
| 225 | <g> | 225 | <g> |
| 226 | <g transform="translate(-0.5 -0.5)"> | 226 | <g transform="translate(-0.5 -0.5)"> |
| 227 | <switch> | 227 | <switch> |
| 228 | <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> | 228 | <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> |
| 229 | <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 279px; margin-left: 85px;"> | 229 | <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 279px; margin-left: 85px;"> |
| 230 | <div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; "> | 230 | <div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; "> |
| 231 | <div style="display: inline-block; font-size: 14px; font-family: "Helvetica"; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: nowrap; "> | 231 | <div style="display: inline-block; font-size: 14px; font-family: "Helvetica"; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: nowrap; "> |
| 232 | Inductor | 232 | Inductor |
| 233 | </div> | 233 | </div> |
| 234 | </div> | 234 | </div> |
| 235 | </div> | 235 | </div> |
| 236 | </foreignObject> | 236 | </foreignObject> |
| 237 | <text x="85" y="283" fill="light-dark(#000000, #ffffff)" font-family=""Helvetica"" font-size="14px" text-anchor="middle" font-weight="bold"> | 237 | <text x="85" y="283" fill="light-dark(#000000, #ffffff)" font-family=""Helvetica"" font-size="14px" text-anchor="middle" font-weight="bold"> |
| 238 | Inductor | 238 | Inductor |
| 239 | </text> | 239 | </text> |
| 240 | </switch> | 240 | </switch> |
| 241 | </g> | 241 | </g> |
| 242 | </g> | 242 | </g> |
| 243 | <g> | 243 | <g> |
| 244 | <rect x="600" y="404.5" width="80" height="30" fill="none" stroke="none" pointer-events="all"/> | 244 | <rect x="600" y="404.5" width="80" height="30" fill="none" stroke="none" pointer-events="all"/> |
| 245 | </g> | 245 | </g> |
| 246 | <g> | 246 | <g> |
| 247 | <g transform="translate(-0.5 -0.5)"> | 247 | <g transform="translate(-0.5 -0.5)"> |
| 248 | <switch> | 248 | <switch> |
| 249 | <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> | 249 | <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> |
| 250 | <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 420px; margin-left: 640px;"> | 250 | <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 420px; margin-left: 640px;"> |
| 251 | <div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; "> | 251 | <div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; "> |
| 252 | <div style="display: inline-block; font-size: 14px; font-family: "Helvetica"; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: nowrap; "> | 252 | <div style="display: inline-block; font-size: 14px; font-family: "Helvetica"; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: nowrap; "> |
| 253 | Autotune | 253 | Autotune |
| 254 | </div> | 254 | </div> |
| 255 | </div> | 255 | </div> |
| 256 | </div> | 256 | </div> |
| 257 | </foreignObject> | 257 | </foreignObject> |
| 258 | <text x="640" y="424" fill="light-dark(#000000, #ffffff)" font-family=""Helvetica"" font-size="14px" text-anchor="middle" font-weight="bold"> | 258 | <text x="640" y="424" fill="light-dark(#000000, #ffffff)" font-family=""Helvetica"" font-size="14px" text-anchor="middle" font-weight="bold"> |
| 259 | Autotune | 259 | Autotune |
| 260 | </text> | 260 | </text> |
| 261 | </switch> | 261 | </switch> |
| 262 | </g> | 262 | </g> |
| 263 | </g> | 263 | </g> |
| 264 | <g> | 264 | <g> |
| 265 | <rect x="750" y="514.5" width="170" height="40" fill="#f8cecc" stroke="#b85450" pointer-events="all" style="fill: light-dark(rgb(248, 206, 204), rgb(81, 45, 43)); stroke: light-dark(rgb(184, 84, 80), rgb(215, 129, 126));"/> | 265 | <rect x="750" y="514.5" width="170" height="40" fill="#f8cecc" stroke="#b85450" pointer-events="all" style="fill: light-dark(rgb(248, 206, 204), rgb(81, 45, 43)); stroke: light-dark(rgb(184, 84, 80), rgb(215, 129, 126));"/> |
| 266 | </g> | 266 | </g> |
| 267 | <g> | 267 | <g> |
| 268 | <g transform="translate(-0.5 -0.5)"> | 268 | <g transform="translate(-0.5 -0.5)"> |
| 269 | <switch> | 269 | <switch> |
| 270 | <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> | 270 | <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> |
| 271 | <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 168px; height: 1px; padding-top: 535px; margin-left: 751px;"> | 271 | <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 168px; height: 1px; padding-top: 535px; margin-left: 751px;"> |
| 272 | <div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; "> | 272 | <div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; "> |
| 273 | <div style="display: inline-block; font-size: 12px; font-family: "Helvetica"; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: normal; word-wrap: normal; "> | 273 | <div style="display: inline-block; font-size: 12px; font-family: "Helvetica"; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: normal; word-wrap: normal; "> |
| 274 | <span> | 274 | <span> |
| 275 | <span style="text-wrap: nowrap;"> | 275 | <span style="text-wrap: nowrap;"> |
| 276 | indirect_load/indirect_store | 276 | indirect_load/indirect_store |
| 277 | </span> | 277 | </span> |
| 278 | <span style="text-wrap: nowrap;"> | 278 | <span style="text-wrap: nowrap;"> |
| 279 | <font style="font-size: 14px;"> | 279 | <font style="font-size: 14px;"> |
| 280 | </font> | 280 | </font> |
| 281 | </span> | 281 | </span> |
| 282 | </span> | 282 | </span> |
| 283 | <div> | 283 | <div> |
| 284 | <span style="text-wrap: nowrap;"> | 284 | <span style="text-wrap: nowrap;"> |
| 285 | <font style="font-size: 14px;"> | 285 | <font style="font-size: 14px;"> |
| 286 | SIMT 模板方案 | 286 | SIMT 模板方案 |
| 287 | </font> | 287 | </font> |
| 288 | </span> | 288 | </span> |
| 289 | </div> | 289 | </div> |
| 290 | </div> | 290 | </div> |
| 291 | </div> | 291 | </div> |
| 292 | </div> | 292 | </div> |
| 293 | </foreignObject> | 293 | </foreignObject> |
| 294 | <text x="835" y="538" fill="light-dark(#000000, #ffffff)" font-family=""Helvetica"" font-size="12px" text-anchor="middle" font-weight="bold"> | 294 | <text x="835" y="538" fill="light-dark(#000000, #ffffff)" font-family=""Helvetica"" font-size="12px" text-anchor="middle" font-weight="bold"> |
| 295 | indirect_load/indirect_store... | 295 | indirect_load/indirect_store... |
| 296 | </text> | 296 | </text> |
| 297 | </switch> | 297 | </switch> |
| 298 | </g> | 298 | </g> |
| 299 | </g> | 299 | </g> |
| 300 | <g> | 300 | <g> |
| 301 | <rect x="640" y="514.5" width="100" height="40" fill="#f8cecc" stroke="#b85450" pointer-events="all" style="fill: light-dark(rgb(248, 206, 204), rgb(81, 45, 43)); stroke: light-dark(rgb(184, 84, 80), rgb(215, 129, 126));"/> | 301 | <rect x="640" y="514.5" width="100" height="40" fill="#f8cecc" stroke="#b85450" pointer-events="all" style="fill: light-dark(rgb(248, 206, 204), rgb(81, 45, 43)); stroke: light-dark(rgb(184, 84, 80), rgb(215, 129, 126));"/> |
| 302 | </g> | 302 | </g> |
| 303 | <g> | 303 | <g> |
| 304 | <g transform="translate(-0.5 -0.5)"> | 304 | <g transform="translate(-0.5 -0.5)"> |
| 305 | <switch> | 305 | <switch> |
| 306 | <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> | 306 | <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> |
| 307 | <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 98px; height: 1px; padding-top: 535px; margin-left: 641px;"> | 307 | <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 98px; height: 1px; padding-top: 535px; margin-left: 641px;"> |
| 308 | <div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; "> | 308 | <div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; "> |
| 309 | <div style="display: inline-block; font-size: 12px; font-family: "Helvetica"; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: normal; word-wrap: normal; "> | 309 | <div style="display: inline-block; font-size: 12px; font-family: "Helvetica"; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: normal; word-wrap: normal; "> |
| 310 | <span style="font-size: 14px; text-wrap: nowrap;"> | 310 | <span style="font-size: 14px; text-wrap: nowrap;"> |
| 311 | 纯SIMT | 311 | 纯SIMT |
| 312 | </span> | 312 | </span> |
| 313 | </div> | 313 | </div> |
| 314 | </div> | 314 | </div> |
| 315 | </div> | 315 | </div> |
| 316 | </foreignObject> | 316 | </foreignObject> |
| 317 | <text x="690" y="538" fill="light-dark(#000000, #ffffff)" font-family=""Helvetica"" font-size="12px" text-anchor="middle" font-weight="bold"> | 317 | <text x="690" y="538" fill="light-dark(#000000, #ffffff)" font-family=""Helvetica"" font-size="12px" text-anchor="middle" font-weight="bold"> |
| 318 | 纯SIMT | 318 | 纯SIMT |
| 319 | </text> | 319 | </text> |
| 320 | </switch> | 320 | </switch> |
| 321 | </g> | 321 | </g> |
| 322 | </g> | 322 | </g> |
| 323 | <g> | 323 | <g> |
| 324 | <rect x="170" y="180" width="100" height="30" fill="none" stroke="none" pointer-events="all"/> | 324 | <rect x="170" y="180" width="100" height="30" fill="none" stroke="none" pointer-events="all"/> |
| 325 | </g> | 325 | </g> |
| 326 | <g> | 326 | <g> |
| 327 | <g transform="translate(-0.5 -0.5)"> | 327 | <g transform="translate(-0.5 -0.5)"> |
| 328 | <switch> | 328 | <switch> |
| 329 | <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> | 329 | <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> |
| 330 | <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 195px; margin-left: 220px;"> | 330 | <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 195px; margin-left: 220px;"> |
| 331 | <div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; "> | 331 | <div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; "> |
| 332 | <div style="display: inline-block; font-size: 12px; font-family: "Helvetica"; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; white-space: nowrap; "> | 332 | <div style="display: inline-block; font-size: 12px; font-family: "Helvetica"; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; white-space: nowrap; "> |
| 333 | <span style="font-size: 14px; text-align: left;"> | 333 | <span style="font-size: 14px; text-align: left;"> |
| 334 | index=x+8* | 334 | index=x+8* |
| 335 | </span> | 335 | </span> |
| 336 | <span style="font-size: 14px; text-align: left;"> | 336 | <span style="font-size: 14px; text-align: left;"> |
| 337 | y | 337 | y |
| 338 | </span> | 338 | </span> |
| 339 | </div> | 339 | </div> |
| 340 | </div> | 340 | </div> |
| 341 | </div> | 341 | </div> |
| 342 | </foreignObject> | 342 | </foreignObject> |
| 343 | <text x="220" y="199" fill="light-dark(#000000, #ffffff)" font-family=""Helvetica"" font-size="12px" text-anchor="middle"> | 343 | <text x="220" y="199" fill="light-dark(#000000, #ffffff)" font-family=""Helvetica"" font-size="12px" text-anchor="middle"> |
| 344 | index=x+8*y | 344 | index=x+8*y |
| 345 | </text> | 345 | </text> |
| 346 | </switch> | 346 | </switch> |
| 347 | </g> | 347 | </g> |
| 348 | </g> | 348 | </g> |
| 349 | <g> | 349 | <g> |
| 350 | <rect x="130" y="240" width="90" height="30" fill="none" stroke="none" pointer-events="all"/> | 350 | <rect x="130" y="240" width="90" height="30" fill="none" stroke="none" pointer-events="all"/> |
| 351 | </g> | 351 | </g> |
| 352 | <g> | 352 | <g> |
| 353 | <g transform="translate(-0.5 -0.5)"> | 353 | <g transform="translate(-0.5 -0.5)"> |
| 354 | <switch> | 354 | <switch> |
| 355 | <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> | 355 | <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> |
| 356 | <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 255px; margin-left: 175px;"> | 356 | <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 255px; margin-left: 175px;"> |
| 357 | <div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; "> | 357 | <div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; "> |
| 358 | <div style="display: inline-block; font-size: 12px; font-family: "Helvetica"; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: nowrap; "> | 358 | <div style="display: inline-block; font-size: 12px; font-family: "Helvetica"; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: nowrap; "> |
| 359 | tl.load/tl.store | 359 | tl.load/tl.store |
| 360 | </div> | 360 | </div> |
| 361 | </div> | 361 | </div> |
| 362 | </div> | 362 | </div> |
| 363 | </foreignObject> | 363 | </foreignObject> |
| 364 | <text x="175" y="259" fill="light-dark(#000000, #ffffff)" font-family=""Helvetica"" font-size="12px" text-anchor="middle" font-weight="bold"> | 364 | <text x="175" y="259" fill="light-dark(#000000, #ffffff)" font-family=""Helvetica"" font-size="12px" text-anchor="middle" font-weight="bold"> |
| 365 | tl.load/tl.store | 365 | tl.load/tl.store |
| 366 | </text> | 366 | </text> |
| 367 | </switch> | 367 | </switch> |
| 368 | </g> | 368 | </g> |
| 369 | </g> | 369 | </g> |
| 370 | <g> | 370 | <g> |
| 371 | <rect x="620" y="434.5" width="170" height="40" fill="none" stroke="none" pointer-events="all"/> | 371 | <rect x="620" y="434.5" width="170" height="40" fill="none" stroke="none" pointer-events="all"/> |
| 372 | </g> | 372 | </g> |
| 373 | <g> | 373 | <g> |
| 374 | <g transform="translate(-0.5 -0.5)"> | 374 | <g transform="translate(-0.5 -0.5)"> |
| 375 | <switch> | 375 | <switch> |
| 376 | <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> | 376 | <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> |
| 377 | <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 1px; height: 1px; padding-top: 455px; margin-left: 622px;"> | 377 | <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 1px; height: 1px; padding-top: 455px; margin-left: 622px;"> |
| 378 | <div style="box-sizing: border-box; font-size: 0; text-align: left; color: #000000; "> | 378 | <div style="box-sizing: border-box; font-size: 0; text-align: left; color: #000000; "> |
| 379 | <div style="display: inline-block; font-size: 12px; font-family: "Helvetica"; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: nowrap; "> | 379 | <div style="display: inline-block; font-size: 12px; font-family: "Helvetica"; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: nowrap; "> |
| 380 | compile_mode="simt_only" | 380 | compile_mode="simt_only" |
| 381 | <div> | 381 | <div> |
| 382 | simt only tiling config | 382 | simt only tiling config |
| 383 | </div> | 383 | </div> |
| 384 | </div> | 384 | </div> |
| 385 | </div> | 385 | </div> |
| 386 | </div> | 386 | </div> |
| 387 | </foreignObject> | 387 | </foreignObject> |
| 388 | <text x="622" y="458" fill="light-dark(#000000, #ffffff)" font-family=""Helvetica"" font-size="12px" font-weight="bold"> | 388 | <text x="622" y="458" fill="light-dark(#000000, #ffffff)" font-family=""Helvetica"" font-size="12px" font-weight="bold"> |
| 389 | compile_mode="simt_only"... | 389 | compile_mode="simt_only"... |
| 390 | </text> | 390 | </text> |
| 391 | </switch> | 391 | </switch> |
| 392 | </g> | 392 | </g> |
| 393 | </g> | 393 | </g> |
| 394 | <g> | 394 | <g> |
| 395 | <rect x="840" y="434.5" width="190" height="40" fill="none" stroke="none" pointer-events="all"/> | 395 | <rect x="840" y="434.5" width="190" height="40" fill="none" stroke="none" pointer-events="all"/> |
| 396 | </g> | 396 | </g> |
| 397 | <g> | 397 | <g> |
| 398 | <g transform="translate(-0.5 -0.5)"> | 398 | <g transform="translate(-0.5 -0.5)"> |
| 399 | <switch> | 399 | <switch> |
| 400 | <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> | 400 | <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> |
| 401 | <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 1px; height: 1px; padding-top: 455px; margin-left: 842px;"> | 401 | <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 1px; height: 1px; padding-top: 455px; margin-left: 842px;"> |
| 402 | <div style="box-sizing: border-box; font-size: 0; text-align: left; color: #000000; "> | 402 | <div style="box-sizing: border-box; font-size: 0; text-align: left; color: #000000; "> |
| 403 | <div style="display: inline-block; font-size: 12px; font-family: "Helvetica"; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: nowrap; "> | 403 | <div style="display: inline-block; font-size: 12px; font-family: "Helvetica"; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: nowrap; "> |
| 404 | compile_mode="simt_template" | 404 | compile_mode="simt_template" |
| 405 | <div> | 405 | <div> |
| 406 | simt template config | 406 | simt template config |
| 407 | </div> | 407 | </div> |
| 408 | </div> | 408 | </div> |
| 409 | </div> | 409 | </div> |
| 410 | </div> | 410 | </div> |
| 411 | </foreignObject> | 411 | </foreignObject> |
| 412 | <text x="842" y="458" fill="light-dark(#000000, #ffffff)" font-family=""Helvetica"" font-size="12px" font-weight="bold"> | 412 | <text x="842" y="458" fill="light-dark(#000000, #ffffff)" font-family=""Helvetica"" font-size="12px" font-weight="bold"> |
| 413 | compile_mode="simt_template"... | 413 | compile_mode="simt_template"... |
| 414 | </text> | 414 | </text> |
| 415 | </switch> | 415 | </switch> |
| 416 | </g> | 416 | </g> |
| 417 | </g> | 417 | </g> |
| 418 | <g> | 418 | <g> |
| 419 | <rect x="400" y="434.5" width="140" height="40" fill="none" stroke="none" pointer-events="all"/> | 419 | <rect x="400" y="434.5" width="140" height="40" fill="none" stroke="none" pointer-events="all"/> |
| 420 | </g> | 420 | </g> |
| 421 | <g> | 421 | <g> |
| 422 | <g transform="translate(-0.5 -0.5)"> | 422 | <g transform="translate(-0.5 -0.5)"> |
| 423 | <switch> | 423 | <switch> |
| 424 | <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> | 424 | <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> |
| 425 | <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 1px; height: 1px; padding-top: 455px; margin-left: 402px;"> | 425 | <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 1px; height: 1px; padding-top: 455px; margin-left: 402px;"> |
| 426 | <div style="box-sizing: border-box; font-size: 0; text-align: left; color: #000000; "> | 426 | <div style="box-sizing: border-box; font-size: 0; text-align: left; color: #000000; "> |
| 427 | <div style="display: inline-block; font-size: 12px; font-family: "Helvetica"; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: nowrap; "> | 427 | <div style="display: inline-block; font-size: 12px; font-family: "Helvetica"; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: nowrap; "> |
| 428 | compile_mode="simd" | 428 | compile_mode="simd" |
| 429 | <div> | 429 | <div> |
| 430 | simd tiling config | 430 | simd tiling config |
| 431 | </div> | 431 | </div> |
| 432 | </div> | 432 | </div> |
| 433 | </div> | 433 | </div> |
| 434 | </div> | 434 | </div> |
| 435 | </foreignObject> | 435 | </foreignObject> |
| 436 | <text x="402" y="458" fill="light-dark(#000000, #ffffff)" font-family=""Helvetica"" font-size="12px" font-weight="bold"> | 436 | <text x="402" y="458" fill="light-dark(#000000, #ffffff)" font-family=""Helvetica"" font-size="12px" font-weight="bold"> |
| 437 | compile_mode="simd"... | 437 | compile_mode="simd"... |
| 438 | </text> | 438 | </text> |
| 439 | </switch> | 439 | </switch> |
| 440 | </g> | 440 | </g> |
| 441 | </g> | 441 | </g> |
| 442 | <g> | 442 | <g> |
| 443 | <rect x="490" y="40" width="150" height="30" fill="none" stroke="none" pointer-events="all"/> | 443 | <rect x="490" y="40" width="150" height="30" fill="none" stroke="none" pointer-events="all"/> |
| 444 | </g> | 444 | </g> |
| 445 | <g> | 445 | <g> |
| 446 | <g transform="translate(-0.5 -0.5)"> | 446 | <g transform="translate(-0.5 -0.5)"> |
| 447 | <switch> | 447 | <switch> |
| 448 | <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> | 448 | <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> |
| 449 | <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 55px; margin-left: 565px;"> | 449 | <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 55px; margin-left: 565px;"> |
| 450 | <div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; "> | 450 | <div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; "> |
| 451 | <div style="display: inline-block; font-size: 16px; font-family: "Helvetica"; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: nowrap; "> | 451 | <div style="display: inline-block; font-size: 16px; font-family: "Helvetica"; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: nowrap; "> |
| 452 | Inductor 离散访存 | 452 | Inductor 离散访存 |
| 453 | </div> | 453 | </div> |
| 454 | </div> | 454 | </div> |
| 455 | </div> | 455 | </div> |
| 456 | </foreignObject> | 456 | </foreignObject> |
| 457 | <text x="565" y="60" fill="light-dark(#000000, #ffffff)" font-family=""Helvetica"" font-size="16px" text-anchor="middle" font-weight="bold"> | 457 | <text x="565" y="60" fill="light-dark(#000000, #ffffff)" font-family=""Helvetica"" font-size="16px" text-anchor="middle" font-weight="bold"> |
| 458 | Inductor 离散访存 | 458 | Inductor 离散访存 |
| 459 | </text> | 459 | </text> |
| 460 | </switch> | 460 | </switch> |
| 461 | </g> | 461 | </g> |
| 462 | </g> | 462 | </g> |
| 463 | <g> | 463 | <g> |
| 464 | <rect x="510" y="564" width="70" height="30" fill="none" stroke="none" pointer-events="all"/> | 464 | <rect x="510" y="564" width="70" height="30" fill="none" stroke="none" pointer-events="all"/> |
| 465 | </g> | 465 | </g> |
| 466 | <g> | 466 | <g> |
| 467 | <g transform="translate(-0.5 -0.5)"> | 467 | <g transform="translate(-0.5 -0.5)"> |
| 468 | <switch> | 468 | <switch> |
| 469 | <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> | 469 | <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> |
| 470 | <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 579px; margin-left: 545px;"> | 470 | <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 579px; margin-left: 545px;"> |
| 471 | <div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; "> | 471 | <div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; "> |
| 472 | <div style="display: inline-block; font-size: 12px; font-family: "Helvetica"; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: nowrap; "> | 472 | <div style="display: inline-block; font-size: 12px; font-family: "Helvetica"; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: nowrap; "> |
| 473 | A2/A3/A5 | 473 | A2/A3/A5 |
| 474 | </div> | 474 | </div> |
| 475 | </div> | 475 | </div> |
| 476 | </div> | 476 | </div> |
| 477 | </foreignObject> | 477 | </foreignObject> |
| 478 | <text x="545" y="583" fill="light-dark(#000000, #ffffff)" font-family=""Helvetica"" font-size="12px" text-anchor="middle" font-weight="bold"> | 478 | <text x="545" y="583" fill="light-dark(#000000, #ffffff)" font-family=""Helvetica"" font-size="12px" text-anchor="middle" font-weight="bold"> |
| 479 | A2/A3/A5 | 479 | A2/A3/A5 |
| 480 | </text> | 480 | </text> |
| 481 | </switch> | 481 | </switch> |
| 482 | </g> | 482 | </g> |
| 483 | </g> | 483 | </g> |
| 484 | <g> | 484 | <g> |
| 485 | <rect x="665" y="564.5" width="40" height="30" fill="none" stroke="none" pointer-events="all"/> | 485 | <rect x="665" y="564.5" width="40" height="30" fill="none" stroke="none" pointer-events="all"/> |
| 486 | </g> | 486 | </g> |
| 487 | <g> | 487 | <g> |
| 488 | <g transform="translate(-0.5 -0.5)"> | 488 | <g transform="translate(-0.5 -0.5)"> |
| 489 | <switch> | 489 | <switch> |
| 490 | <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> | 490 | <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> |
| 491 | <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 580px; margin-left: 685px;"> | 491 | <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 580px; margin-left: 685px;"> |
| 492 | <div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; "> | 492 | <div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; "> |
| 493 | <div style="display: inline-block; font-size: 12px; font-family: "Helvetica"; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: nowrap; "> | 493 | <div style="display: inline-block; font-size: 12px; font-family: "Helvetica"; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: nowrap; "> |
| 494 | A5 | 494 | A5 |
| 495 | </div> | 495 | </div> |
| 496 | </div> | 496 | </div> |
| 497 | </div> | 497 | </div> |
| 498 | </foreignObject> | 498 | </foreignObject> |
| 499 | <text x="685" y="583" fill="light-dark(#000000, #ffffff)" font-family=""Helvetica"" font-size="12px" text-anchor="middle" font-weight="bold"> | 499 | <text x="685" y="583" fill="light-dark(#000000, #ffffff)" font-family=""Helvetica"" font-size="12px" text-anchor="middle" font-weight="bold"> |
| 500 | A5 | 500 | A5 |
| 501 | </text> | 501 | </text> |
| 502 | </switch> | 502 | </switch> |
| 503 | </g> | 503 | </g> |
| 504 | </g> | 504 | </g> |
| 505 | <g> | 505 | <g> |
| 506 | <rect x="815" y="564.5" width="40" height="30" fill="none" stroke="none" pointer-events="all"/> | 506 | <rect x="815" y="564.5" width="40" height="30" fill="none" stroke="none" pointer-events="all"/> |
| 507 | </g> | 507 | </g> |
| 508 | <g> | 508 | <g> |
| 509 | <g transform="translate(-0.5 -0.5)"> | 509 | <g transform="translate(-0.5 -0.5)"> |
| 510 | <switch> | 510 | <switch> |
| 511 | <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> | 511 | <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> |
| 512 | <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 580px; margin-left: 835px;"> | 512 | <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 580px; margin-left: 835px;"> |
| 513 | <div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; "> | 513 | <div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; "> |
| 514 | <div style="display: inline-block; font-size: 12px; font-family: "Helvetica"; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: nowrap; "> | 514 | <div style="display: inline-block; font-size: 12px; font-family: "Helvetica"; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: nowrap; "> |
| 515 | A5 | 515 | A5 |
| 516 | </div> | 516 | </div> |
| 517 | </div> | 517 | </div> |
| 518 | </div> | 518 | </div> |
| 519 | </foreignObject> | 519 | </foreignObject> |
| 520 | <text x="835" y="583" fill="light-dark(#000000, #ffffff)" font-family=""Helvetica"" font-size="12px" text-anchor="middle" font-weight="bold"> | 520 | <text x="835" y="583" fill="light-dark(#000000, #ffffff)" font-family=""Helvetica"" font-size="12px" text-anchor="middle" font-weight="bold"> |
| 521 | A5 | 521 | A5 |
| 522 | </text> | 522 | </text> |
| 523 | </switch> | 523 | </switch> |
| 524 | </g> | 524 | </g> |
| 525 | </g> | 525 | </g> |
| 526 | <g> | 526 | <g> |
| 527 | <path d="M 690 310 L 690 348.13" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/> | 527 | <path d="M 690 310 L 690 348.13" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/> |
| 528 | <path d="M 690 353.38 L 686.5 346.38 L 690 348.13 L 693.5 346.38 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/> | 528 | <path d="M 690 353.38 L 686.5 346.38 L 690 348.13 L 693.5 346.38 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/> |
| 529 | </g> | 529 | </g> |
| 530 | <g> | 530 | <g> |
| 531 | <rect x="700" y="324.5" width="340" height="30" fill="none" stroke="none" pointer-events="all"/> | 531 | <rect x="700" y="324.5" width="340" height="30" fill="none" stroke="none" pointer-events="all"/> |
| 532 | </g> | 532 | </g> |
| 533 | <g> | 533 | <g> |
| 534 | <g transform="translate(-0.5 -0.5)"> | 534 | <g transform="translate(-0.5 -0.5)"> |
| 535 | <switch> | 535 | <switch> |
| 536 | <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> | 536 | <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> |
| 537 | <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 340px; margin-left: 870px;"> | 537 | <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 340px; margin-left: 870px;"> |
| 538 | <div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; "> | 538 | <div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; "> |
| 539 | <div style="display: inline-block; font-size: 12px; font-family: "Helvetica"; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: nowrap; "> | 539 | <div style="display: inline-block; font-size: 12px; font-family: "Helvetica"; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: nowrap; "> |
| 540 | INDUCTOR_INDIRECT_MEMORY_MODE='simd_simt_mix' | 540 | INDUCTOR_INDIRECT_MEMORY_MODE='simd_simt_mix' |
| 541 | </div> | 541 | </div> |
| 542 | </div> | 542 | </div> |
| 543 | </div> | 543 | </div> |
| 544 | </foreignObject> | 544 | </foreignObject> |
| 545 | <text x="870" y="343" fill="light-dark(#000000, #ffffff)" font-family=""Helvetica"" font-size="12px" text-anchor="middle" font-weight="bold"> | 545 | <text x="870" y="343" fill="light-dark(#000000, #ffffff)" font-family=""Helvetica"" font-size="12px" text-anchor="middle" font-weight="bold"> |
| 546 | INDUCTOR_INDIRECT_MEMORY_MODE='simd_simt_mix' | 546 | INDUCTOR_INDIRECT_MEMORY_MODE='simd_simt_mix' |
| 547 | </text> | 547 | </text> |
| 548 | </switch> | 548 | </switch> |
| 549 | </g> | 549 | </g> |
| 550 | </g> | 550 | </g> |
| 551 | <g> | 551 | <g> |
| 552 | <path d="M 440 140 L 440 183.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/> | 552 | <path d="M 440 140 L 440 183.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/> |
| 553 | <path d="M 440 188.88 L 436.5 181.88 L 440 183.63 L 443.5 181.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/> | 553 | <path d="M 440 188.88 L 436.5 181.88 L 440 183.63 L 443.5 181.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/> |
| 554 | </g> | 554 | </g> |
| 555 | <g> | 555 | <g> |
| 556 | <rect x="240" y="100" width="400" height="40" fill="none" stroke="none" pointer-events="all"/> | 556 | <rect x="240" y="100" width="400" height="40" fill="none" stroke="none" pointer-events="all"/> |
| 557 | </g> | 557 | </g> |
| 558 | <g> | 558 | <g> |
| 559 | <g transform="translate(-0.5 -0.5)"> | 559 | <g transform="translate(-0.5 -0.5)"> |
| 560 | <switch> | 560 | <switch> |
| 561 | <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> | 561 | <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> |
| 562 | <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 120px; margin-left: 440px;"> | 562 | <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 120px; margin-left: 440px;"> |
| 563 | <div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; "> | 563 | <div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; "> |
| 564 | <div style="display: inline-block; font-size: 12px; font-family: "Helvetica"; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; white-space: nowrap; "> | 564 | <div style="display: inline-block; font-size: 12px; font-family: "Helvetica"; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; white-space: nowrap; "> |
| 565 | <div> | 565 | <div> |
| 566 | <font color="#000000" style="color: light-dark(rgb(0, 0, 0), rgb(237, 237, 237));"> | 566 | <font color="#000000" style="color: light-dark(rgb(0, 0, 0), rgb(237, 237, 237));"> |
| 567 | <span style="font-size: 12px;"> | 567 | <span style="font-size: 12px;"> |
| 568 | load类:aten.embedding、aten.index、aten.gather、aten.index_select | 568 | load类:aten.embedding、aten.index、aten.gather、aten.index_select |
| 569 | </span> | 569 | </span> |
| 570 | </font> | 570 | </font> |
| 571 | </div> | 571 | </div> |
| 572 | <div> | 572 | <div> |
| 573 | <font color="#000000" style="color: light-dark(rgb(0, 0, 0), rgb(237, 237, 237));"> | 573 | <font color="#000000" style="color: light-dark(rgb(0, 0, 0), rgb(237, 237, 237));"> |
| 574 | <span style="font-size: 12px;"> | 574 | <span style="font-size: 12px;"> |
| 575 | store类:aten.index_put、aten.scatter | 575 | store类:aten.index_put、aten.scatter |
| 576 | </span> | 576 | </span> |
| 577 | </font> | 577 | </font> |
| 578 | </div> | 578 | </div> |
| 579 | </div> | 579 | </div> |
| 580 | </div> | 580 | </div> |
| 581 | </div> | 581 | </div> |
| 582 | </foreignObject> | 582 | </foreignObject> |
| 583 | <text x="440" y="124" fill="light-dark(#000000, #ffffff)" font-family=""Helvetica"" font-size="12px" text-anchor="middle"> | 583 | <text x="440" y="124" fill="light-dark(#000000, #ffffff)" font-family=""Helvetica"" font-size="12px" text-anchor="middle"> |
| 584 | load类:aten.embedding、aten.index、aten.gather、aten.index_select... | 584 | load类:aten.embedding、aten.index、aten.gather、aten.index_select... |
| 585 | </text> | 585 | </text> |
| 586 | </switch> | 586 | </switch> |
| 587 | </g> | 587 | </g> |
| 588 | </g> | 588 | </g> |
| 589 | </g> | 589 | </g> |
| 590 | <switch> | 590 | <switch> |
| 591 | <g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/> | 591 | <g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/> |
| 592 | <a transform="translate(0,-5)" xlink:href="https://www.drawio.com/doc/faq/svg-export-text-problems" target="_blank"> | 592 | <a transform="translate(0,-5)" xlink:href="https://www.drawio.com/doc/faq/svg-export-text-problems" target="_blank"> |
| 593 | <text text-anchor="middle" font-size="10px" x="50%" y="100%"> | 593 | <text text-anchor="middle" font-size="10px" x="50%" y="100%"> |
| 594 | Text is not SVG - cannot display | 594 | Text is not SVG - cannot display |
| 595 | </text> | 595 | </text> |
| 596 | </a> | 596 | </a> |
| 597 | </switch> | 597 | </switch> |
| 598 | </svg> | 598 | </svg> |
此文件变更行数或变更字符数较多,你可以直接 查看源码
| @@ -1,3 +1,3 @@ | |||
| 1 | from .npu_fusion_attention_graph import npu_fusion_attention_graph | 1 | from .npu_fusion_attention_graph import npu_fusion_attention_graph |
| 2 | 2 | ||
| 3 | __all__ = ["npu_fusion_attention_graph"] | 3 | __all__ = ["npu_fusion_attention_graph"] |
| @@ -254,4 +254,3 @@ def register_fa_pass(): | |||
| 254 | register_replacement( | 254 | register_replacement( |
| 255 | **register_replacement_kwargs, | 255 | **register_replacement_kwargs, |
| 256 | ) | 256 | ) |
| 257 | |||
| @@ -1,1105 +1,1105 @@ | |||
| 1 | """ | 1 | """ |
| 2 | This file holds FALLBACK_LIST in which aten/primes ops will be fallbacked to AclNN op and | 2 | This file holds FALLBACK_LIST in which aten/primes ops will be fallbacked to AclNN op and |
| 3 | will not be allowed to lowering and fusion. | 3 | will not be allowed to lowering and fusion. |
| 4 | 4 | ||
| 5 | After fixed and verified, it can be removed from FALLBACK_LIST. | 5 | After fixed and verified, it can be removed from FALLBACK_LIST. |
| 6 | """ | 6 | """ |
| 7 | 7 | ||
| 8 | import torch | 8 | import torch |
| 9 | 9 | ||
| 10 | from torch.ops import ( | 10 | from torch.ops import ( |
| 11 | quantized_decomposed, | 11 | quantized_decomposed, |
| 12 | rngprims, | 12 | rngprims, |
| 13 | _inductor_test, | 13 | _inductor_test, |
| 14 | inductor, | 14 | inductor, |
| 15 | fsdp, | 15 | fsdp, |
| 16 | _c10d_functional, | 16 | _c10d_functional, |
| 17 | _dtensor, | 17 | _dtensor, |
| 18 | _quantized, | 18 | _quantized, |
| 19 | quantized, | 19 | quantized, |
| 20 | ) | 20 | ) |
| 21 | 21 | ||
| 22 | from torch._prims.rng_prims import ( | 22 | from torch._prims.rng_prims import ( |
| 23 | graphsafe_run_with_rng_state, | 23 | graphsafe_run_with_rng_state, |
| 24 | run_and_save_rng_state, | 24 | run_and_save_rng_state, |
| 25 | run_with_rng_state, | 25 | run_with_rng_state, |
| 26 | ) | 26 | ) |
| 27 | from torch._higher_order_ops.auto_functionalize import auto_functionalized | 27 | from torch._higher_order_ops.auto_functionalize import auto_functionalized |
| 28 | from torch._higher_order_ops.cond import cond | 28 | from torch._higher_order_ops.cond import cond |
| 29 | from torch._higher_order_ops.while_loop import while_loop | 29 | from torch._higher_order_ops.while_loop import while_loop |
| 30 | from torch._higher_order_ops._invoke_quant import invoke_quant | 30 | from torch._higher_order_ops._invoke_quant import invoke_quant |
| 31 | from torch._higher_order_ops.associative_scan import associative_scan | 31 | from torch._higher_order_ops.associative_scan import associative_scan |
| 32 | from torch._higher_order_ops.effects import with_effects | 32 | from torch._higher_order_ops.effects import with_effects |
| 33 | 33 | ||
| 34 | from .config import inductor_indirect_memory_mode, inductor_ascend_linear_mode | 34 | from .config import inductor_indirect_memory_mode, inductor_ascend_linear_mode |
| 35 | 35 | ||
| 36 | aten = torch.ops.aten | 36 | aten = torch.ops.aten |
| 37 | prims = torch.ops.prims | 37 | prims = torch.ops.prims |
| 38 | 38 | ||
| 39 | 39 | ||
| 40 | NPU_EXTRA_FALLBACK_LIST = [ | 40 | NPU_EXTRA_FALLBACK_LIST = [ |
| 41 | _c10d_functional.all_gather_into_tensor, | 41 | _c10d_functional.all_gather_into_tensor, |
| 42 | _c10d_functional.all_gather_into_tensor.default, | 42 | _c10d_functional.all_gather_into_tensor.default, |
| 43 | _c10d_functional.all_gather_into_tensor_coalesced, | 43 | _c10d_functional.all_gather_into_tensor_coalesced, |
| 44 | _c10d_functional.all_gather_into_tensor_coalesced.default, | 44 | _c10d_functional.all_gather_into_tensor_coalesced.default, |
| 45 | _c10d_functional.all_gather_into_tensor_out, | 45 | _c10d_functional.all_gather_into_tensor_out, |
| 46 | _c10d_functional.all_gather_into_tensor_out.default, | 46 | _c10d_functional.all_gather_into_tensor_out.default, |
| 47 | _c10d_functional.all_reduce, | 47 | _c10d_functional.all_reduce, |
| 48 | _c10d_functional.all_reduce.default, | 48 | _c10d_functional.all_reduce.default, |
| 49 | _c10d_functional.all_reduce_, | 49 | _c10d_functional.all_reduce_, |
| 50 | _c10d_functional.all_reduce_.default, | 50 | _c10d_functional.all_reduce_.default, |
| 51 | _c10d_functional.all_reduce_coalesced, | 51 | _c10d_functional.all_reduce_coalesced, |
| 52 | _c10d_functional.all_reduce_coalesced.default, | 52 | _c10d_functional.all_reduce_coalesced.default, |
| 53 | _c10d_functional.all_reduce_coalesced_, | 53 | _c10d_functional.all_reduce_coalesced_, |
| 54 | _c10d_functional.all_reduce_coalesced_.default, | 54 | _c10d_functional.all_reduce_coalesced_.default, |
| 55 | _c10d_functional.all_to_all_single, | 55 | _c10d_functional.all_to_all_single, |
| 56 | _c10d_functional.all_to_all_single.default, | 56 | _c10d_functional.all_to_all_single.default, |
| 57 | _c10d_functional.broadcast, | 57 | _c10d_functional.broadcast, |
| 58 | _c10d_functional.broadcast.default, | 58 | _c10d_functional.broadcast.default, |
| 59 | _c10d_functional.broadcast_, | 59 | _c10d_functional.broadcast_, |
| 60 | _c10d_functional.broadcast_.default, | 60 | _c10d_functional.broadcast_.default, |
| 61 | _c10d_functional.reduce_scatter_tensor, | 61 | _c10d_functional.reduce_scatter_tensor, |
| 62 | _c10d_functional.reduce_scatter_tensor.default, | 62 | _c10d_functional.reduce_scatter_tensor.default, |
| 63 | _c10d_functional.reduce_scatter_tensor_coalesced, | 63 | _c10d_functional.reduce_scatter_tensor_coalesced, |
| 64 | _c10d_functional.reduce_scatter_tensor_coalesced.default, | 64 | _c10d_functional.reduce_scatter_tensor_coalesced.default, |
| 65 | _c10d_functional.wait_tensor, | 65 | _c10d_functional.wait_tensor, |
| 66 | _c10d_functional.wait_tensor.default, | 66 | _c10d_functional.wait_tensor.default, |
| 67 | _dtensor.shard_dim_alltoall, | 67 | _dtensor.shard_dim_alltoall, |
| 68 | _dtensor.shard_dim_alltoall.default, | 68 | _dtensor.shard_dim_alltoall.default, |
| 69 | _inductor_test.realize, | 69 | _inductor_test.realize, |
| 70 | _inductor_test.realize.default, | 70 | _inductor_test.realize.default, |
| 71 | associative_scan, | 71 | associative_scan, |
| 72 | aten.__and__, | 72 | aten.__and__, |
| 73 | aten.__and__.bool, | 73 | aten.__and__.bool, |
| 74 | aten.__and__.int, | 74 | aten.__and__.int, |
| 75 | aten.__iand__, | 75 | aten.__iand__, |
| 76 | aten.__iand__.Scalar, | 76 | aten.__iand__.Scalar, |
| 77 | aten.__iand__.Tensor, | 77 | aten.__iand__.Tensor, |
| 78 | aten.__ilshift__, | 78 | aten.__ilshift__, |
| 79 | aten.__ilshift__.Scalar, | 79 | aten.__ilshift__.Scalar, |
| 80 | aten.__ilshift__.Tensor, | 80 | aten.__ilshift__.Tensor, |
| 81 | aten.__ior__.Scalar, | 81 | aten.__ior__.Scalar, |
| 82 | aten.__irshift__.Scalar, | 82 | aten.__irshift__.Scalar, |
| 83 | aten.__ixor__, | 83 | aten.__ixor__, |
| 84 | aten.__ixor__.Scalar, | 84 | aten.__ixor__.Scalar, |
| 85 | aten.__ixor__.Tensor, | 85 | aten.__ixor__.Tensor, |
| 86 | aten.__lshift__, | 86 | aten.__lshift__, |
| 87 | aten.__lshift__.Scalar, | 87 | aten.__lshift__.Scalar, |
| 88 | aten.__lshift__.Scalar_out, | 88 | aten.__lshift__.Scalar_out, |
| 89 | aten.__lshift__.Tensor, | 89 | aten.__lshift__.Tensor, |
| 90 | aten.__lshift__.Tensor_out, | 90 | aten.__lshift__.Tensor_out, |
| 91 | aten.__lshift__.int, | 91 | aten.__lshift__.int, |
| 92 | aten.__or__, | 92 | aten.__or__, |
| 93 | aten.__or__.bool, | 93 | aten.__or__.bool, |
| 94 | aten.__or__.int, | 94 | aten.__or__.int, |
| 95 | aten.__rshift__, | 95 | aten.__rshift__, |
| 96 | aten.__rshift__.Scalar, | 96 | aten.__rshift__.Scalar, |
| 97 | aten.__rshift__.Scalar_out, | 97 | aten.__rshift__.Scalar_out, |
| 98 | aten.__rshift__.Tensor, | 98 | aten.__rshift__.Tensor, |
| 99 | aten.__rshift__.Tensor_out, | 99 | aten.__rshift__.Tensor_out, |
| 100 | aten.__rshift__.int, | 100 | aten.__rshift__.int, |
| 101 | aten.__xor__, | 101 | aten.__xor__, |
| 102 | aten.__xor__.bool, | 102 | aten.__xor__.bool, |
| 103 | aten.__xor__.int, | 103 | aten.__xor__.int, |
| 104 | aten._adaptive_avg_pool2d, | 104 | aten._adaptive_avg_pool2d, |
| 105 | aten._adaptive_avg_pool2d.default, | 105 | aten._adaptive_avg_pool2d.default, |
| 106 | aten._adaptive_avg_pool2d.out, | 106 | aten._adaptive_avg_pool2d.out, |
| 107 | aten._assert_tensor_metadata, | 107 | aten._assert_tensor_metadata, |
| 108 | aten._assert_tensor_metadata.default, | 108 | aten._assert_tensor_metadata.default, |
| 109 | aten._convolution, | 109 | aten._convolution, |
| 110 | aten._convolution.default, | 110 | aten._convolution.default, |
| 111 | aten._convolution.out, | 111 | aten._convolution.out, |
| 112 | aten._foobar, | 112 | aten._foobar, |
| 113 | aten._foobar.default, | 113 | aten._foobar.default, |
| 114 | aten._foobar.out, | 114 | aten._foobar.out, |
| 115 | aten._int_mm, | 115 | aten._int_mm, |
| 116 | aten._int_mm.default, | 116 | aten._int_mm.default, |
| 117 | aten._int_mm.out, | 117 | aten._int_mm.out, |
| 118 | aten._jagged_to_padded_dense_forward.default, | 118 | aten._jagged_to_padded_dense_forward.default, |
| 119 | aten._local_scalar_dense, | 119 | aten._local_scalar_dense, |
| 120 | aten._local_scalar_dense.default, | 120 | aten._local_scalar_dense.default, |
| 121 | aten._neg_view, | 121 | aten._neg_view, |
| 122 | aten._neg_view.default, | 122 | aten._neg_view.default, |
| 123 | aten._padded_dense_to_jagged_forward, | 123 | aten._padded_dense_to_jagged_forward, |
| 124 | aten._padded_dense_to_jagged_forward.default, | 124 | aten._padded_dense_to_jagged_forward.default, |
| 125 | aten._scaled_mm.default, | 125 | aten._scaled_mm.default, |
| 126 | aten._sparse_semi_structured_mm, | 126 | aten._sparse_semi_structured_mm, |
| 127 | aten._sparse_semi_structured_mm.default, | 127 | aten._sparse_semi_structured_mm.default, |
| 128 | aten._unsafe_index_put.hacked_twin, | 128 | aten._unsafe_index_put.hacked_twin, |
| 129 | aten._unsafe_masked_index, | 129 | aten._unsafe_masked_index, |
| 130 | aten._unsafe_masked_index.default, | 130 | aten._unsafe_masked_index.default, |
| 131 | aten._unsafe_masked_index_put_accumulate, | 131 | aten._unsafe_masked_index_put_accumulate, |
| 132 | aten._unsafe_view, | 132 | aten._unsafe_view, |
| 133 | aten._weight_int4pack_mm_for_cpu, | 133 | aten._weight_int4pack_mm_for_cpu, |
| 134 | aten._weight_int4pack_mm_for_cpu.default, | 134 | aten._weight_int4pack_mm_for_cpu.default, |
| 135 | aten._weight_int8pack_mm, | 135 | aten._weight_int8pack_mm, |
| 136 | aten._weight_int8pack_mm.default, | 136 | aten._weight_int8pack_mm.default, |
| 137 | aten.acos, | 137 | aten.acos, |
| 138 | aten.acos.Scalar, | 138 | aten.acos.Scalar, |
| 139 | aten.acos.complex, | 139 | aten.acos.complex, |
| 140 | aten.acos.default, | 140 | aten.acos.default, |
| 141 | aten.acos.float, | 141 | aten.acos.float, |
| 142 | aten.acos.int, | 142 | aten.acos.int, |
| 143 | aten.acos.out, | 143 | aten.acos.out, |
| 144 | aten.acosh, | 144 | aten.acosh, |
| 145 | aten.acosh.Scalar, | 145 | aten.acosh.Scalar, |
| 146 | aten.acosh.complex, | 146 | aten.acosh.complex, |
| 147 | aten.acosh.default, | 147 | aten.acosh.default, |
| 148 | aten.acosh.float, | 148 | aten.acosh.float, |
| 149 | aten.acosh.int, | 149 | aten.acosh.int, |
| 150 | aten.acosh.out, | 150 | aten.acosh.out, |
| 151 | aten.adaptive_max_pool2d, | 151 | aten.adaptive_max_pool2d, |
| 152 | aten.add_, | 152 | aten.add_, |
| 153 | aten.add_.Scalar, | 153 | aten.add_.Scalar, |
| 154 | aten.add_.Tensor, | 154 | aten.add_.Tensor, |
| 155 | aten.add_.t, | 155 | aten.add_.t, |
| 156 | aten.alias, | 156 | aten.alias, |
| 157 | aten.alias.default, | 157 | aten.alias.default, |
| 158 | aten.any, | 158 | aten.any, |
| 159 | aten.any.all_out, | 159 | aten.any.all_out, |
| 160 | aten.any.bool, | 160 | aten.any.bool, |
| 161 | aten.any.default, | 161 | aten.any.default, |
| 162 | aten.any.dim, | 162 | aten.any.dim, |
| 163 | aten.any.dimname_out, | 163 | aten.any.dimname_out, |
| 164 | aten.any.dims, | 164 | aten.any.dims, |
| 165 | aten.any.dims_out, | 165 | aten.any.dims_out, |
| 166 | aten.any.float, | 166 | aten.any.float, |
| 167 | aten.any.int, | 167 | aten.any.int, |
| 168 | aten.any.out, | 168 | aten.any.out, |
| 169 | aten.any.str, | 169 | aten.any.str, |
| 170 | aten.as_strided, | 170 | aten.as_strided, |
| 171 | aten.as_strided.default, | 171 | aten.as_strided.default, |
| 172 | aten.as_strided_, | 172 | aten.as_strided_, |
| 173 | aten.as_strided_.default, | 173 | aten.as_strided_.default, |
| 174 | aten.as_strided_copy, | 174 | aten.as_strided_copy, |
| 175 | aten.as_strided_copy.default, | 175 | aten.as_strided_copy.default, |
| 176 | aten.as_strided_copy.out, | 176 | aten.as_strided_copy.out, |
| 177 | aten.as_strided_scatter, | 177 | aten.as_strided_scatter, |
| 178 | aten.as_strided_scatter.default, | 178 | aten.as_strided_scatter.default, |
| 179 | aten.as_strided_scatter.out, | 179 | aten.as_strided_scatter.out, |
| 180 | aten.asin, | 180 | aten.asin, |
| 181 | aten.asin.Scalar, | 181 | aten.asin.Scalar, |
| 182 | aten.asin.complex, | 182 | aten.asin.complex, |
| 183 | aten.asin.default, | 183 | aten.asin.default, |
| 184 | aten.asin.float, | 184 | aten.asin.float, |
| 185 | aten.asin.int, | 185 | aten.asin.int, |
| 186 | aten.asin.out, | 186 | aten.asin.out, |
| 187 | aten.asinh, | 187 | aten.asinh, |
| 188 | aten.asinh.Scalar, | 188 | aten.asinh.Scalar, |
| 189 | aten.asinh.complex, | 189 | aten.asinh.complex, |
| 190 | aten.asinh.default, | 190 | aten.asinh.default, |
| 191 | aten.asinh.float, | 191 | aten.asinh.float, |
| 192 | aten.asinh.int, | 192 | aten.asinh.int, |
| 193 | aten.asinh.out, | 193 | aten.asinh.out, |
| 194 | aten.atan, | 194 | aten.atan, |
| 195 | aten.atan.Scalar, | 195 | aten.atan.Scalar, |
| 196 | aten.atan.complex, | 196 | aten.atan.complex, |
| 197 | aten.atan.default, | 197 | aten.atan.default, |
| 198 | aten.atan.float, | 198 | aten.atan.float, |
| 199 | aten.atan.int, | 199 | aten.atan.int, |
| 200 | aten.atan.out, | 200 | aten.atan.out, |
| 201 | aten.atan2, | 201 | aten.atan2, |
| 202 | aten.atan2.Scalar_Scalar, | 202 | aten.atan2.Scalar_Scalar, |
| 203 | aten.atan2.default, | 203 | aten.atan2.default, |
| 204 | aten.atan2.float, | 204 | aten.atan2.float, |
| 205 | aten.atan2.float_int, | 205 | aten.atan2.float_int, |
| 206 | aten.atan2.int, | 206 | aten.atan2.int, |
| 207 | aten.atan2.int_float, | 207 | aten.atan2.int_float, |
| 208 | aten.atan2.out, | 208 | aten.atan2.out, |
| 209 | aten.atanh, | 209 | aten.atanh, |
| 210 | aten.atanh.Scalar, | 210 | aten.atanh.Scalar, |
| 211 | aten.atanh.complex, | 211 | aten.atanh.complex, |
| 212 | aten.atanh.default, | 212 | aten.atanh.default, |
| 213 | aten.atanh.float, | 213 | aten.atanh.float, |
| 214 | aten.atanh.int, | 214 | aten.atanh.int, |
| 215 | aten.atanh.out, | 215 | aten.atanh.out, |
| 216 | aten.avg_pool2d, | 216 | aten.avg_pool2d, |
| 217 | aten.avg_pool2d.default, | 217 | aten.avg_pool2d.default, |
| 218 | aten.avg_pool2d.out, | 218 | aten.avg_pool2d.out, |
| 219 | aten.avg_pool2d_backward, | 219 | aten.avg_pool2d_backward, |
| 220 | aten.avg_pool2d_backward.default, | 220 | aten.avg_pool2d_backward.default, |
| 221 | aten.avg_pool2d_backward.grad_input, | 221 | aten.avg_pool2d_backward.grad_input, |
| 222 | aten.avg_pool3d, | 222 | aten.avg_pool3d, |
| 223 | aten.avg_pool3d.default, | 223 | aten.avg_pool3d.default, |
| 224 | aten.avg_pool3d.out, | 224 | aten.avg_pool3d.out, |
| 225 | aten.avg_pool3d_backward, | 225 | aten.avg_pool3d_backward, |
| 226 | aten.avg_pool3d_backward.default, | 226 | aten.avg_pool3d_backward.default, |
| 227 | aten.avg_pool3d_backward.grad_input, | 227 | aten.avg_pool3d_backward.grad_input, |
| 228 | aten.baddbmm, | 228 | aten.baddbmm, |
| 229 | aten.baddbmm.default, | 229 | aten.baddbmm.default, |
| 230 | aten.baddbmm.out, | 230 | aten.baddbmm.out, |
| 231 | aten.bernoulli.p, | 231 | aten.bernoulli.p, |
| 232 | aten.bernoulli_, | 232 | aten.bernoulli_, |
| 233 | aten.bernoulli_.Tensor, | 233 | aten.bernoulli_.Tensor, |
| 234 | aten.bernoulli_.float, | 234 | aten.bernoulli_.float, |
| 235 | aten.bitwise_and_, | 235 | aten.bitwise_and_, |
| 236 | aten.bitwise_left_shift, | 236 | aten.bitwise_left_shift, |
| 237 | aten.bitwise_left_shift.Scalar_Tensor, | 237 | aten.bitwise_left_shift.Scalar_Tensor, |
| 238 | aten.bitwise_left_shift.Scalar_Tensor_out, | 238 | aten.bitwise_left_shift.Scalar_Tensor_out, |
| 239 | aten.bitwise_left_shift.Tensor, | 239 | aten.bitwise_left_shift.Tensor, |
| 240 | aten.bitwise_left_shift.Tensor_Scalar, | 240 | aten.bitwise_left_shift.Tensor_Scalar, |
| 241 | aten.bitwise_left_shift.Tensor_Scalar_out, | 241 | aten.bitwise_left_shift.Tensor_Scalar_out, |
| 242 | aten.bitwise_left_shift.Tensor_out, | 242 | aten.bitwise_left_shift.Tensor_out, |
| 243 | aten.bitwise_left_shift_, | 243 | aten.bitwise_left_shift_, |
| 244 | aten.bitwise_left_shift_.Tensor, | 244 | aten.bitwise_left_shift_.Tensor, |
| 245 | aten.bitwise_left_shift_.Tensor_Scalar, | 245 | aten.bitwise_left_shift_.Tensor_Scalar, |
| 246 | aten.bitwise_not, | 246 | aten.bitwise_not, |
| 247 | aten.bitwise_not.default, | 247 | aten.bitwise_not.default, |
| 248 | aten.bitwise_not.out, | 248 | aten.bitwise_not.out, |
| 249 | aten.bitwise_not_, | 249 | aten.bitwise_not_, |
| 250 | aten.bitwise_not_.default, | 250 | aten.bitwise_not_.default, |
| 251 | aten.bitwise_or, | 251 | aten.bitwise_or, |
| 252 | aten.bitwise_or.Scalar, | 252 | aten.bitwise_or.Scalar, |
| 253 | aten.bitwise_or.Tensor, | 253 | aten.bitwise_or.Tensor, |
| 254 | aten.bitwise_or_, | 254 | aten.bitwise_or_, |
| 255 | aten.bitwise_right_shift, | 255 | aten.bitwise_right_shift, |
| 256 | aten.bitwise_right_shift.Scalar_Tensor, | 256 | aten.bitwise_right_shift.Scalar_Tensor, |
| 257 | aten.bitwise_right_shift.Scalar_Tensor_out, | 257 | aten.bitwise_right_shift.Scalar_Tensor_out, |
| 258 | aten.bitwise_right_shift.Tensor, | 258 | aten.bitwise_right_shift.Tensor, |
| 259 | aten.bitwise_right_shift.Tensor_Scalar, | 259 | aten.bitwise_right_shift.Tensor_Scalar, |
| 260 | aten.bitwise_right_shift.Tensor_Scalar_out, | 260 | aten.bitwise_right_shift.Tensor_Scalar_out, |
| 261 | aten.bitwise_right_shift.Tensor_out, | 261 | aten.bitwise_right_shift.Tensor_out, |
| 262 | aten.bitwise_right_shift_, | 262 | aten.bitwise_right_shift_, |
| 263 | aten.bitwise_right_shift_.Tensor, | 263 | aten.bitwise_right_shift_.Tensor, |
| 264 | aten.bitwise_right_shift_.Tensor_Scalar, | 264 | aten.bitwise_right_shift_.Tensor_Scalar, |
| 265 | aten.bitwise_xor_, | 265 | aten.bitwise_xor_, |
| 266 | aten.bitwise_xor_.Scalar, | 266 | aten.bitwise_xor_.Scalar, |
| 267 | aten.bitwise_xor_.Tensor, | 267 | aten.bitwise_xor_.Tensor, |
| 268 | aten.broadcast_tensors, | 268 | aten.broadcast_tensors, |
| 269 | aten.bucketize, | 269 | aten.bucketize, |
| 270 | aten.bucketize.Scalar, | 270 | aten.bucketize.Scalar, |
| 271 | aten.bucketize.Scalar_out, | 271 | aten.bucketize.Scalar_out, |
| 272 | aten.bucketize.Tensor, | 272 | aten.bucketize.Tensor, |
| 273 | aten.bucketize.Tensor_out, | 273 | aten.bucketize.Tensor_out, |
| 274 | aten.cat.names_out, | 274 | aten.cat.names_out, |
| 275 | aten.cat.out, | 275 | aten.cat.out, |
| 276 | aten.constant_pad_nd, | 276 | aten.constant_pad_nd, |
| 277 | aten.constant_pad_nd.default, | 277 | aten.constant_pad_nd.default, |
| 278 | aten.constant_pad_nd.out, | 278 | aten.constant_pad_nd.out, |
| 279 | aten.convolution, | 279 | aten.convolution, |
| 280 | aten.convolution.default, | 280 | aten.convolution.default, |
| 281 | aten.convolution.out, | 281 | aten.convolution.out, |
| 282 | aten.copysign, | 282 | aten.copysign, |
| 283 | aten.copysign.Scalar, | 283 | aten.copysign.Scalar, |
| 284 | aten.copysign.Scalar_out, | 284 | aten.copysign.Scalar_out, |
| 285 | aten.copysign.Tensor, | 285 | aten.copysign.Tensor, |
| 286 | aten.copysign.default, | 286 | aten.copysign.default, |
| 287 | aten.copysign.float, | 287 | aten.copysign.float, |
| 288 | aten.copysign.float_int, | 288 | aten.copysign.float_int, |
| 289 | aten.copysign.int, | 289 | aten.copysign.int, |
| 290 | aten.copysign.int_float, | 290 | aten.copysign.int_float, |
| 291 | aten.copysign.out, | 291 | aten.copysign.out, |
| 292 | aten.cosh, | 292 | aten.cosh, |
| 293 | aten.cosh.Scalar, | 293 | aten.cosh.Scalar, |
| 294 | aten.cosh.complex, | 294 | aten.cosh.complex, |
| 295 | aten.cosh.default, | 295 | aten.cosh.default, |
| 296 | aten.cosh.float, | 296 | aten.cosh.float, |
| 297 | aten.cosh.int, | 297 | aten.cosh.int, |
| 298 | aten.cosh.out, | 298 | aten.cosh.out, |
| 299 | aten.cummax, | 299 | aten.cummax, |
| 300 | aten.cummax.dimname_out, | 300 | aten.cummax.dimname_out, |
| 301 | aten.cummax.out, | 301 | aten.cummax.out, |
| 302 | aten.cummin, | 302 | aten.cummin, |
| 303 | aten.cummin.dimname_out, | 303 | aten.cummin.dimname_out, |
| 304 | aten.cummin.out, | 304 | aten.cummin.out, |
| 305 | aten.cumprod, | 305 | aten.cumprod, |
| 306 | aten.cumprod.dimname_out, | 306 | aten.cumprod.dimname_out, |
| 307 | aten.cumprod.out, | 307 | aten.cumprod.out, |
| 308 | aten.cumsum.dimname_out, | 308 | aten.cumsum.dimname_out, |
| 309 | aten.cumsum.out, | 309 | aten.cumsum.out, |
| 310 | aten.detach, | 310 | aten.detach, |
| 311 | aten.detach_, | 311 | aten.detach_, |
| 312 | aten.diagonal, | 312 | aten.diagonal, |
| 313 | aten.diagonal.Dimname, | 313 | aten.diagonal.Dimname, |
| 314 | aten.diagonal.default, | 314 | aten.diagonal.default, |
| 315 | aten.diagonal_copy, | 315 | aten.diagonal_copy, |
| 316 | aten.diagonal_scatter, | 316 | aten.diagonal_scatter, |
| 317 | aten.diagonal_scatter.default, | 317 | aten.diagonal_scatter.default, |
| 318 | aten.diagonal_scatter.out, | 318 | aten.diagonal_scatter.out, |
| 319 | aten.div_.Tensor, | 319 | aten.div_.Tensor, |
| 320 | aten.div_.Tensor_mode, | 320 | aten.div_.Tensor_mode, |
| 321 | aten.empty, | 321 | aten.empty, |
| 322 | aten.empty.memory_format, | 322 | aten.empty.memory_format, |
| 323 | aten.empty.names, | 323 | aten.empty.names, |
| 324 | aten.empty.names_out, | 324 | aten.empty.names_out, |
| 325 | aten.empty.out, | 325 | aten.empty.out, |
| 326 | aten.empty_like, | 326 | aten.empty_like, |
| 327 | aten.empty_strided, | 327 | aten.empty_strided, |
| 328 | aten.empty_strided.default, | 328 | aten.empty_strided.default, |
| 329 | aten.empty_strided.out, | 329 | aten.empty_strided.out, |
| 330 | aten.erfc, | 330 | aten.erfc, |
| 331 | aten.erfc.Scalar, | 331 | aten.erfc.Scalar, |
| 332 | aten.erfc.default, | 332 | aten.erfc.default, |
| 333 | aten.erfc.float, | 333 | aten.erfc.float, |
| 334 | aten.erfc.int, | 334 | aten.erfc.int, |
| 335 | aten.erfc.out, | 335 | aten.erfc.out, |
| 336 | aten.erfinv, | 336 | aten.erfinv, |
| 337 | aten.erfinv.default, | 337 | aten.erfinv.default, |
| 338 | aten.erfinv.out, | 338 | aten.erfinv.out, |
| 339 | aten.exp2.out, | 339 | aten.exp2.out, |
| 340 | aten.expand_as, | 340 | aten.expand_as, |
| 341 | aten.expand_as.default, | 341 | aten.expand_as.default, |
| 342 | aten.expm1, | 342 | aten.expm1, |
| 343 | aten.expm1.Scalar, | 343 | aten.expm1.Scalar, |
| 344 | aten.expm1.default, | 344 | aten.expm1.default, |
| 345 | aten.expm1.float, | 345 | aten.expm1.float, |
| 346 | aten.expm1.int, | 346 | aten.expm1.int, |
| 347 | aten.expm1.out, | 347 | aten.expm1.out, |
| 348 | aten.fill_, | 348 | aten.fill_, |
| 349 | aten.fmod, | 349 | aten.fmod, |
| 350 | aten.fmod.Scalar, | 350 | aten.fmod.Scalar, |
| 351 | aten.fmod.Scalar_out, | 351 | aten.fmod.Scalar_out, |
| 352 | aten.fmod.Tensor, | 352 | aten.fmod.Tensor, |
| 353 | aten.fmod.Tensor_out, | 353 | aten.fmod.Tensor_out, |
| 354 | aten.fmod.default, | 354 | aten.fmod.default, |
| 355 | aten.fmod.float, | 355 | aten.fmod.float, |
| 356 | aten.fmod.float_int, | 356 | aten.fmod.float_int, |
| 357 | aten.fmod.int, | 357 | aten.fmod.int, |
| 358 | aten.fmod.int_float, | 358 | aten.fmod.int_float, |
| 359 | aten.fractional_max_pool2d, | 359 | aten.fractional_max_pool2d, |
| 360 | aten.fractional_max_pool2d.default, | 360 | aten.fractional_max_pool2d.default, |
| 361 | aten.fractional_max_pool2d.output, | 361 | aten.fractional_max_pool2d.output, |
| 362 | aten.frexp, | 362 | aten.frexp, |
| 363 | aten.frexp.Tensor, | 363 | aten.frexp.Tensor, |
| 364 | aten.frexp.Tensor_out, | 364 | aten.frexp.Tensor_out, |
| 365 | aten.frexp.default, | 365 | aten.frexp.default, |
| 366 | aten.full_like, | 366 | aten.full_like, |
| 367 | aten.glu, | 367 | aten.glu, |
| 368 | aten.glu.default, | 368 | aten.glu.default, |
| 369 | aten.hypot, | 369 | aten.hypot, |
| 370 | aten.hypot.default, | 370 | aten.hypot.default, |
| 371 | aten.hypot.out, | 371 | aten.hypot.out, |
| 372 | aten.i0.default, | 372 | aten.i0.default, |
| 373 | aten.i0.out, | 373 | aten.i0.out, |
| 374 | aten.index.Tensor_out, | 374 | aten.index.Tensor_out, |
| 375 | aten.index.list_Tensor, | 375 | aten.index.list_Tensor, |
| 376 | aten.index.list_bool, | 376 | aten.index.list_bool, |
| 377 | aten.index.list_float, | 377 | aten.index.list_float, |
| 378 | aten.index.list_int, | 378 | aten.index.list_int, |
| 379 | aten.index.list_str, | 379 | aten.index.list_str, |
| 380 | aten.index.str, | 380 | aten.index.str, |
| 381 | aten.index_put.hacked_twin, | 381 | aten.index_put.hacked_twin, |
| 382 | aten.index_put_.hacked_twin, | 382 | aten.index_put_.hacked_twin, |
| 383 | aten.isinf, | 383 | aten.isinf, |
| 384 | aten.isinf.complex, | 384 | aten.isinf.complex, |
| 385 | aten.isinf.default, | 385 | aten.isinf.default, |
| 386 | aten.isinf.float, | 386 | aten.isinf.float, |
| 387 | aten.isinf.out, | 387 | aten.isinf.out, |
| 388 | aten.lgamma, | 388 | aten.lgamma, |
| 389 | aten.lgamma.Scalar, | 389 | aten.lgamma.Scalar, |
| 390 | aten.lgamma.default, | 390 | aten.lgamma.default, |
| 391 | aten.lgamma.float, | 391 | aten.lgamma.float, |
| 392 | aten.lgamma.int, | 392 | aten.lgamma.int, |
| 393 | aten.lgamma.out, | 393 | aten.lgamma.out, |
| 394 | aten.lift, | 394 | aten.lift, |
| 395 | aten.lift_fresh, | 395 | aten.lift_fresh, |
| 396 | aten.lift_fresh.default, | 396 | aten.lift_fresh.default, |
| 397 | aten.lift_fresh_copy, | 397 | aten.lift_fresh_copy, |
| 398 | aten.lift_fresh_copy.default, | 398 | aten.lift_fresh_copy.default, |
| 399 | aten.lift_fresh_copy.out, | 399 | aten.lift_fresh_copy.out, |
| 400 | aten.log10, | 400 | aten.log10, |
| 401 | aten.log10.Scalar, | 401 | aten.log10.Scalar, |
| 402 | aten.log10.complex, | 402 | aten.log10.complex, |
| 403 | aten.log10.default, | 403 | aten.log10.default, |
| 404 | aten.log10.float, | 404 | aten.log10.float, |
| 405 | aten.log10.int, | 405 | aten.log10.int, |
| 406 | aten.log10.out, | 406 | aten.log10.out, |
| 407 | aten.logcumsumexp, | 407 | aten.logcumsumexp, |
| 408 | aten.logcumsumexp.dimname_out, | 408 | aten.logcumsumexp.dimname_out, |
| 409 | aten.logcumsumexp.out, | 409 | aten.logcumsumexp.out, |
| 410 | aten.logical_and_, | 410 | aten.logical_and_, |
| 411 | aten.logical_and_.default, | 411 | aten.logical_and_.default, |
| 412 | aten.logical_not_, | 412 | aten.logical_not_, |
| 413 | aten.logical_not_.default, | 413 | aten.logical_not_.default, |
| 414 | aten.logical_or_, | 414 | aten.logical_or_, |
| 415 | aten.logical_or_.default, | 415 | aten.logical_or_.default, |
| 416 | aten.logical_xor, | 416 | aten.logical_xor, |
| 417 | aten.logical_xor.default, | 417 | aten.logical_xor.default, |
| 418 | aten.logical_xor.out, | 418 | aten.logical_xor.out, |
| 419 | aten.logical_xor_, | 419 | aten.logical_xor_, |
| 420 | aten.logical_xor_.default, | 420 | aten.logical_xor_.default, |
| 421 | aten.max_pool2d_with_indices, | 421 | aten.max_pool2d_with_indices, |
| 422 | aten.max_pool2d_with_indices_backward, | 422 | aten.max_pool2d_with_indices_backward, |
| 423 | aten.max_pool2d_with_indices_backward.default, | 423 | aten.max_pool2d_with_indices_backward.default, |
| 424 | aten.max_pool2d_with_indices_backward.grad_input, | 424 | aten.max_pool2d_with_indices_backward.grad_input, |
| 425 | aten.mul_, | 425 | aten.mul_, |
| 426 | aten.mul_.Scalar, | 426 | aten.mul_.Scalar, |
| 427 | aten.mul_.Tensor, | 427 | aten.mul_.Tensor, |
| 428 | aten.mul_.t, | 428 | aten.mul_.t, |
| 429 | aten.native_dropout, | 429 | aten.native_dropout, |
| 430 | aten.native_dropout.default, | 430 | aten.native_dropout.default, |
| 431 | aten.native_dropout.out, | 431 | aten.native_dropout.out, |
| 432 | aten.new_empty, | 432 | aten.new_empty, |
| 433 | aten.new_empty_strided, | 433 | aten.new_empty_strided, |
| 434 | aten.new_empty_strided.default, | 434 | aten.new_empty_strided.default, |
| 435 | aten.new_empty_strided.out, | 435 | aten.new_empty_strided.out, |
| 436 | aten.nextafter, | 436 | aten.nextafter, |
| 437 | aten.nextafter.default, | 437 | aten.nextafter.default, |
| 438 | aten.nextafter.out, | 438 | aten.nextafter.out, |
| 439 | aten.prod, | 439 | aten.prod, |
| 440 | aten.prod.Dimname_out, | 440 | aten.prod.Dimname_out, |
| 441 | aten.prod.default, | 441 | aten.prod.default, |
| 442 | aten.prod.dim_int, | 442 | aten.prod.dim_int, |
| 443 | aten.prod.int_out, | 443 | aten.prod.int_out, |
| 444 | aten.prod.out, | 444 | aten.prod.out, |
| 445 | aten.rand, | 445 | aten.rand, |
| 446 | aten.rand.generator_with_names, | 446 | aten.rand.generator_with_names, |
| 447 | aten.rand.generator_with_names_out, | 447 | aten.rand.generator_with_names_out, |
| 448 | aten.rand.names, | 448 | aten.rand.names, |
| 449 | aten.rand.names_out, | 449 | aten.rand.names_out, |
| 450 | aten.rand.out, | 450 | aten.rand.out, |
| 451 | aten.randn, | 451 | aten.randn, |
| 452 | aten.randn.generator_with_names, | 452 | aten.randn.generator_with_names, |
| 453 | aten.randn.generator_with_names_out, | 453 | aten.randn.generator_with_names_out, |
| 454 | aten.randn.names, | 454 | aten.randn.names, |
| 455 | aten.randn.names_out, | 455 | aten.randn.names_out, |
| 456 | aten.randn.out, | 456 | aten.randn.out, |
| 457 | aten.relu_, | 457 | aten.relu_, |
| 458 | aten.relu_.default, | 458 | aten.relu_.default, |
| 459 | aten.remainder, | 459 | aten.remainder, |
| 460 | aten.remainder.Scalar, | 460 | aten.remainder.Scalar, |
| 461 | aten.remainder.Scalar_Tensor_out, | 461 | aten.remainder.Scalar_Tensor_out, |
| 462 | aten.remainder.Scalar_out, | 462 | aten.remainder.Scalar_out, |
| 463 | aten.remainder.Tensor, | 463 | aten.remainder.Tensor, |
| 464 | aten.remainder.Tensor_out, | 464 | aten.remainder.Tensor_out, |
| 465 | aten.remainder.default, | 465 | aten.remainder.default, |
| 466 | aten.remainder.float, | 466 | aten.remainder.float, |
| 467 | aten.remainder.float_int, | 467 | aten.remainder.float_int, |
| 468 | aten.remainder.int, | 468 | aten.remainder.int, |
| 469 | aten.remainder.int_float, | 469 | aten.remainder.int_float, |
| 470 | aten.resize, | 470 | aten.resize, |
| 471 | aten.resize.default, | 471 | aten.resize.default, |
| 472 | aten.resize.out, | 472 | aten.resize.out, |
| 473 | aten.round.default, | 473 | aten.round.default, |
| 474 | aten.scatter.reduce, | 474 | aten.scatter.reduce, |
| 475 | aten.scatter.reduce_out, | 475 | aten.scatter.reduce_out, |
| 476 | aten.scatter.src_out, | 476 | aten.scatter.src_out, |
| 477 | aten.scatter.value, | 477 | aten.scatter.value, |
| 478 | aten.scatter.value_out, | 478 | aten.scatter.value_out, |
| 479 | aten.scatter.value_reduce, | 479 | aten.scatter.value_reduce, |
| 480 | aten.scatter.value_reduce_out, | 480 | aten.scatter.value_reduce_out, |
| 481 | aten.scatter_.reduce, | 481 | aten.scatter_.reduce, |
| 482 | aten.scatter_.value, | 482 | aten.scatter_.value, |
| 483 | aten.scatter_.value_reduce, | 483 | aten.scatter_.value_reduce, |
| 484 | aten.scatter_add, | 484 | aten.scatter_add, |
| 485 | aten.scatter_add.default, | 485 | aten.scatter_add.default, |
| 486 | aten.scatter_add.out, | 486 | aten.scatter_add.out, |
| 487 | aten.scatter_add_, | 487 | aten.scatter_add_, |
| 488 | aten.scatter_add_.default, | 488 | aten.scatter_add_.default, |
| 489 | aten.scatter_reduce.two, | 489 | aten.scatter_reduce.two, |
| 490 | aten.scatter_reduce.two_out, | 490 | aten.scatter_reduce.two_out, |
| 491 | aten.scatter_reduce_.two, | 491 | aten.scatter_reduce_.two, |
| 492 | aten.searchsorted.Tensor, | 492 | aten.searchsorted.Tensor, |
| 493 | aten.set_.source_Tensor, | 493 | aten.set_.source_Tensor, |
| 494 | aten.sigmoid_, | 494 | aten.sigmoid_, |
| 495 | aten.sigmoid_.default, | 495 | aten.sigmoid_.default, |
| 496 | aten.signbit, | 496 | aten.signbit, |
| 497 | aten.signbit.default, | 497 | aten.signbit.default, |
| 498 | aten.signbit.out, | 498 | aten.signbit.out, |
| 499 | aten.sinh, | 499 | aten.sinh, |
| 500 | aten.sinh.Scalar, | 500 | aten.sinh.Scalar, |
| 501 | aten.sinh.complex, | 501 | aten.sinh.complex, |
| 502 | aten.sinh.default, | 502 | aten.sinh.default, |
| 503 | aten.sinh.float, | 503 | aten.sinh.float, |
| 504 | aten.sinh.int, | 504 | aten.sinh.int, |
| 505 | aten.sinh.out, | 505 | aten.sinh.out, |
| 506 | aten.special_bessel_j0.default, | 506 | aten.special_bessel_j0.default, |
| 507 | aten.special_bessel_j0.out, | 507 | aten.special_bessel_j0.out, |
| 508 | aten.special_bessel_j1.default, | 508 | aten.special_bessel_j1.default, |
| 509 | aten.special_bessel_j1.out, | 509 | aten.special_bessel_j1.out, |
| 510 | aten.special_bessel_y0.default, | 510 | aten.special_bessel_y0.default, |
| 511 | aten.special_bessel_y0.out, | 511 | aten.special_bessel_y0.out, |
| 512 | aten.special_bessel_y1.default, | 512 | aten.special_bessel_y1.default, |
| 513 | aten.special_bessel_y1.out, | 513 | aten.special_bessel_y1.out, |
| 514 | aten.special_erf, | 514 | aten.special_erf, |
| 515 | aten.special_erf.out, | 515 | aten.special_erf.out, |
| 516 | aten.special_erfcx.default, | 516 | aten.special_erfcx.default, |
| 517 | aten.special_erfcx.out, | 517 | aten.special_erfcx.out, |
| 518 | aten.special_i1.default, | 518 | aten.special_i1.default, |
| 519 | aten.special_i1.out, | 519 | aten.special_i1.out, |
| 520 | aten.special_modified_bessel_i0.default, | 520 | aten.special_modified_bessel_i0.default, |
| 521 | aten.special_modified_bessel_i0.out, | 521 | aten.special_modified_bessel_i0.out, |
| 522 | aten.special_modified_bessel_i1.default, | 522 | aten.special_modified_bessel_i1.default, |
| 523 | aten.special_modified_bessel_i1.out, | 523 | aten.special_modified_bessel_i1.out, |
| 524 | aten.split.str, | 524 | aten.split.str, |
| 525 | aten.square, | 525 | aten.square, |
| 526 | aten.square.out, | 526 | aten.square.out, |
| 527 | aten.squeeze_, | 527 | aten.squeeze_, |
| 528 | aten.squeeze_.default, | 528 | aten.squeeze_.default, |
| 529 | aten.squeeze_.dim, | 529 | aten.squeeze_.dim, |
| 530 | aten.squeeze_.dimname, | 530 | aten.squeeze_.dimname, |
| 531 | aten.squeeze_.dims, | 531 | aten.squeeze_.dims, |
| 532 | aten.squeeze_copy, | 532 | aten.squeeze_copy, |
| 533 | aten.sub_, | 533 | aten.sub_, |
| 534 | aten.sub_.Scalar, | 534 | aten.sub_.Scalar, |
| 535 | aten.sub_.Tensor, | 535 | aten.sub_.Tensor, |
| 536 | aten.sym_constrain_range, | 536 | aten.sym_constrain_range, |
| 537 | aten.sym_constrain_range.default, | 537 | aten.sym_constrain_range.default, |
| 538 | aten.sym_numel, | 538 | aten.sym_numel, |
| 539 | aten.sym_numel.default, | 539 | aten.sym_numel.default, |
| 540 | aten.tan, | 540 | aten.tan, |
| 541 | aten.tan.Scalar, | 541 | aten.tan.Scalar, |
| 542 | aten.tan.complex, | 542 | aten.tan.complex, |
| 543 | aten.tan.default, | 543 | aten.tan.default, |
| 544 | aten.tan.float, | 544 | aten.tan.float, |
| 545 | aten.tan.int, | 545 | aten.tan.int, |
| 546 | aten.tan.out, | 546 | aten.tan.out, |
| 547 | aten.true_divide, | 547 | aten.true_divide, |
| 548 | aten.true_divide.out, | 548 | aten.true_divide.out, |
| 549 | aten.trunc, | 549 | aten.trunc, |
| 550 | aten.trunc.default, | 550 | aten.trunc.default, |
| 551 | aten.trunc.out, | 551 | aten.trunc.out, |
| 552 | aten.unbind, | 552 | aten.unbind, |
| 553 | aten.unbind.Dimname, | 553 | aten.unbind.Dimname, |
| 554 | aten.unbind.int, | 554 | aten.unbind.int, |
| 555 | aten.unfold, | 555 | aten.unfold, |
| 556 | aten.unfold.default, | 556 | aten.unfold.default, |
| 557 | aten.unsqueeze_, | 557 | aten.unsqueeze_, |
| 558 | aten.unsqueeze_.default, | 558 | aten.unsqueeze_.default, |
| 559 | aten.upsample_nearest2d_backward.default, | 559 | aten.upsample_nearest2d_backward.default, |
| 560 | aten.var.correction, | 560 | aten.var.correction, |
| 561 | aten.var.correction_names_out, | 561 | aten.var.correction_names_out, |
| 562 | aten.var.correction_out, | 562 | aten.var.correction_out, |
| 563 | aten.var.names_out, | 563 | aten.var.names_out, |
| 564 | aten.var.out, | 564 | aten.var.out, |
| 565 | aten.var_mean.correction, | 565 | aten.var_mean.correction, |
| 566 | aten.var_mean.correction_out, | 566 | aten.var_mean.correction_out, |
| 567 | aten.view, | 567 | aten.view, |
| 568 | aten.view.default, | 568 | aten.view.default, |
| 569 | aten.view.dtype, | 569 | aten.view.dtype, |
| 570 | cond, | 570 | cond, |
| 571 | fsdp.copy_.default, | 571 | fsdp.copy_.default, |
| 572 | inductor.resize_storage_bytes_, | 572 | inductor.resize_storage_bytes_, |
| 573 | inductor.resize_storage_bytes_.default, | 573 | inductor.resize_storage_bytes_.default, |
| 574 | invoke_quant, | 574 | invoke_quant, |
| 575 | prims._low_memory_max_pool2d_offsets_to_indices, | 575 | prims._low_memory_max_pool2d_offsets_to_indices, |
| 576 | prims._low_memory_max_pool2d_offsets_to_indices.default, | 576 | prims._low_memory_max_pool2d_offsets_to_indices.default, |
| 577 | prims._low_memory_max_pool2d_with_offsets, | 577 | prims._low_memory_max_pool2d_with_offsets, |
| 578 | prims._low_memory_max_pool2d_with_offsets.default, | 578 | prims._low_memory_max_pool2d_with_offsets.default, |
| 579 | prims._sink_tokens.default, | 579 | prims._sink_tokens.default, |
| 580 | prims._unsafe_index_put_.default, | 580 | prims._unsafe_index_put_.default, |
| 581 | prims.abs, | 581 | prims.abs, |
| 582 | prims.abs.default, | 582 | prims.abs.default, |
| 583 | prims.acos, | 583 | prims.acos, |
| 584 | prims.acos.default, | 584 | prims.acos.default, |
| 585 | prims.acosh, | 585 | prims.acosh, |
| 586 | prims.acosh.default, | 586 | prims.acosh.default, |
| 587 | prims.add, | 587 | prims.add, |
| 588 | prims.add.default, | 588 | prims.add.default, |
| 589 | prims.asin, | 589 | prims.asin, |
| 590 | prims.asin.default, | 590 | prims.asin.default, |
| 591 | prims.asinh, | 591 | prims.asinh, |
| 592 | prims.asinh.default, | 592 | prims.asinh.default, |
| 593 | prims.atan, | 593 | prims.atan, |
| 594 | prims.atan.default, | 594 | prims.atan.default, |
| 595 | prims.atan2, | 595 | prims.atan2, |
| 596 | prims.atan2.default, | 596 | prims.atan2.default, |
| 597 | prims.atanh, | 597 | prims.atanh, |
| 598 | prims.atanh.default, | 598 | prims.atanh.default, |
| 599 | prims.bessel_j0, | 599 | prims.bessel_j0, |
| 600 | prims.bessel_j0.default, | 600 | prims.bessel_j0.default, |
| 601 | prims.bessel_j1, | 601 | prims.bessel_j1, |
| 602 | prims.bessel_j1.default, | 602 | prims.bessel_j1.default, |
| 603 | prims.bitwise_and, | 603 | prims.bitwise_and, |
| 604 | prims.bitwise_and.default, | 604 | prims.bitwise_and.default, |
| 605 | prims.bitwise_not, | 605 | prims.bitwise_not, |
| 606 | prims.bitwise_not.default, | 606 | prims.bitwise_not.default, |
| 607 | prims.bitwise_or, | 607 | prims.bitwise_or, |
| 608 | prims.bitwise_or.default, | 608 | prims.bitwise_or.default, |
| 609 | prims.bitwise_xor, | 609 | prims.bitwise_xor, |
| 610 | prims.bitwise_xor.default, | 610 | prims.bitwise_xor.default, |
| 611 | prims.ceil, | 611 | prims.ceil, |
| 612 | prims.ceil.default, | 612 | prims.ceil.default, |
| 613 | prims.cos, | 613 | prims.cos, |
| 614 | prims.cos.default, | 614 | prims.cos.default, |
| 615 | prims.cosh, | 615 | prims.cosh, |
| 616 | prims.cosh.default, | 616 | prims.cosh.default, |
| 617 | prims.digamma, | 617 | prims.digamma, |
| 618 | prims.div, | 618 | prims.div, |
| 619 | prims.div.default, | 619 | prims.div.default, |
| 620 | prims.eq, | 620 | prims.eq, |
| 621 | prims.eq.default, | 621 | prims.eq.default, |
| 622 | prims.erf, | 622 | prims.erf, |
| 623 | prims.erf.default, | 623 | prims.erf.default, |
| 624 | prims.erfc, | 624 | prims.erfc, |
| 625 | prims.erfc.default, | 625 | prims.erfc.default, |
| 626 | prims.erfcx, | 626 | prims.erfcx, |
| 627 | prims.erfcx.default, | 627 | prims.erfcx.default, |
| 628 | prims.exp, | 628 | prims.exp, |
| 629 | prims.exp.default, | 629 | prims.exp.default, |
| 630 | prims.expm1, | 630 | prims.expm1, |
| 631 | prims.expm1.default, | 631 | prims.expm1.default, |
| 632 | prims.fma, | 632 | prims.fma, |
| 633 | prims.fma.default, | 633 | prims.fma.default, |
| 634 | prims.fmod, | 634 | prims.fmod, |
| 635 | prims.fmod.default, | 635 | prims.fmod.default, |
| 636 | prims.frexp, | 636 | prims.frexp, |
| 637 | prims.frexp.default, | 637 | prims.frexp.default, |
| 638 | prims.ge, | 638 | prims.ge, |
| 639 | prims.ge.default, | 639 | prims.ge.default, |
| 640 | prims.gt, | 640 | prims.gt, |
| 641 | prims.gt.default, | 641 | prims.gt.default, |
| 642 | prims.hypot, | 642 | prims.hypot, |
| 643 | prims.hypot.default, | 643 | prims.hypot.default, |
| 644 | prims.igamma, | 644 | prims.igamma, |
| 645 | prims.igammac, | 645 | prims.igammac, |
| 646 | prims.inductor_force_stride_order.default, | 646 | prims.inductor_force_stride_order.default, |
| 647 | prims.inductor_lookup_seed.default, | 647 | prims.inductor_lookup_seed.default, |
| 648 | prims.inductor_randint.default, | 648 | prims.inductor_randint.default, |
| 649 | prims.inductor_random.default, | 649 | prims.inductor_random.default, |
| 650 | prims.inductor_seed.default, | 650 | prims.inductor_seed.default, |
| 651 | prims.inductor_seeds.default, | 651 | prims.inductor_seeds.default, |
| 652 | prims.le, | 652 | prims.le, |
| 653 | prims.le.default, | 653 | prims.le.default, |
| 654 | prims.lgamma, | 654 | prims.lgamma, |
| 655 | prims.lgamma.default, | 655 | prims.lgamma.default, |
| 656 | prims.log, | 656 | prims.log, |
| 657 | prims.log.default, | 657 | prims.log.default, |
| 658 | prims.log10, | 658 | prims.log10, |
| 659 | prims.log10.default, | 659 | prims.log10.default, |
| 660 | prims.log1p, | 660 | prims.log1p, |
| 661 | prims.log1p.default, | 661 | prims.log1p.default, |
| 662 | prims.lt, | 662 | prims.lt, |
| 663 | prims.lt.default, | 663 | prims.lt.default, |
| 664 | prims.minimum, | 664 | prims.minimum, |
| 665 | prims.minimum.default, | 665 | prims.minimum.default, |
| 666 | prims.ndtri, | 666 | prims.ndtri, |
| 667 | prims.ndtri.default, | 667 | prims.ndtri.default, |
| 668 | prims.ne, | 668 | prims.ne, |
| 669 | prims.ne.default, | 669 | prims.ne.default, |
| 670 | prims.neg, | 670 | prims.neg, |
| 671 | prims.neg.default, | 671 | prims.neg.default, |
| 672 | prims.nextafter, | 672 | prims.nextafter, |
| 673 | prims.nextafter.default, | 673 | prims.nextafter.default, |
| 674 | prims.prepare_softmax_online.default, | 674 | prims.prepare_softmax_online.default, |
| 675 | prims.reciprocal, | 675 | prims.reciprocal, |
| 676 | prims.reciprocal.default, | 676 | prims.reciprocal.default, |
| 677 | prims.remainder, | 677 | prims.remainder, |
| 678 | prims.remainder.default, | 678 | prims.remainder.default, |
| 679 | prims.rev.default, | 679 | prims.rev.default, |
| 680 | prims.rsqrt.default, | 680 | prims.rsqrt.default, |
| 681 | prims.sign, | 681 | prims.sign, |
| 682 | prims.sign.default, | 682 | prims.sign.default, |
| 683 | prims.signbit, | 683 | prims.signbit, |
| 684 | prims.signbit.default, | 684 | prims.signbit.default, |
| 685 | prims.sin, | 685 | prims.sin, |
| 686 | prims.sin.default, | 686 | prims.sin.default, |
| 687 | prims.sinh, | 687 | prims.sinh, |
| 688 | prims.sinh.default, | 688 | prims.sinh.default, |
| 689 | prims.spherical_bessel_j0, | 689 | prims.spherical_bessel_j0, |
| 690 | prims.spherical_bessel_j0.default, | 690 | prims.spherical_bessel_j0.default, |
| 691 | prims.sqrt, | 691 | prims.sqrt, |
| 692 | prims.sqrt.default, | 692 | prims.sqrt.default, |
| 693 | prims.sub, | 693 | prims.sub, |
| 694 | prims.sub.default, | 694 | prims.sub.default, |
| 695 | prims.sum, | 695 | prims.sum, |
| 696 | prims.sum.default, | 696 | prims.sum.default, |
| 697 | prims.tan, | 697 | prims.tan, |
| 698 | prims.tan.default, | 698 | prims.tan.default, |
| 699 | prims.var, | 699 | prims.var, |
| 700 | prims.var.default, | 700 | prims.var.default, |
| 701 | prims.view_of, | 701 | prims.view_of, |
| 702 | prims.view_of.default, | 702 | prims.view_of.default, |
| 703 | prims.xor_sum, | 703 | prims.xor_sum, |
| 704 | prims.xor_sum.default, | 704 | prims.xor_sum.default, |
| 705 | prims.zeta, | 705 | prims.zeta, |
| 706 | prims.zeta.default, | 706 | prims.zeta.default, |
| 707 | quantized_decomposed.dequantize_per_channel, | 707 | quantized_decomposed.dequantize_per_channel, |
| 708 | quantized_decomposed.dequantize_per_channel.default, | 708 | quantized_decomposed.dequantize_per_channel.default, |
| 709 | quantized_decomposed.dequantize_per_tensor.default, | 709 | quantized_decomposed.dequantize_per_tensor.default, |
| 710 | quantized_decomposed.dequantize_per_tensor.tensor, | 710 | quantized_decomposed.dequantize_per_tensor.tensor, |
| 711 | quantized_decomposed.quantize_per_channel, | 711 | quantized_decomposed.quantize_per_channel, |
| 712 | quantized_decomposed.quantize_per_channel.default, | 712 | quantized_decomposed.quantize_per_channel.default, |
| 713 | quantized_decomposed.quantize_per_tensor.default, | 713 | quantized_decomposed.quantize_per_tensor.default, |
| 714 | quantized_decomposed.quantize_per_tensor.tensor, | 714 | quantized_decomposed.quantize_per_tensor.tensor, |
| 715 | rngprims.philox_rand, | 715 | rngprims.philox_rand, |
| 716 | rngprims.philox_rand.default, | 716 | rngprims.philox_rand.default, |
| 717 | while_loop, | 717 | while_loop, |
| 718 | with_effects, | 718 | with_effects, |
| 719 | ] | 719 | ] |
| 720 | TORCH_NATIVE_FALLBACK_LIST = [ | 720 | TORCH_NATIVE_FALLBACK_LIST = [ |
| 721 | _quantized.wrapped_fbgemm_linear_fp16_weight.default, | 721 | _quantized.wrapped_fbgemm_linear_fp16_weight.default, |
| 722 | _quantized.wrapped_fbgemm_pack_gemm_matrix_fp16.default, | 722 | _quantized.wrapped_fbgemm_pack_gemm_matrix_fp16.default, |
| 723 | aten._adaptive_avg_pool2d_backward.default, | 723 | aten._adaptive_avg_pool2d_backward.default, |
| 724 | aten._adaptive_avg_pool2d_backward.out, | 724 | aten._adaptive_avg_pool2d_backward.out, |
| 725 | aten._adaptive_avg_pool3d.default, | 725 | aten._adaptive_avg_pool3d.default, |
| 726 | aten._adaptive_avg_pool3d.out, | 726 | aten._adaptive_avg_pool3d.out, |
| 727 | aten._adaptive_avg_pool3d_backward.default, | 727 | aten._adaptive_avg_pool3d_backward.default, |
| 728 | aten._adaptive_avg_pool3d_backward.out, | 728 | aten._adaptive_avg_pool3d_backward.out, |
| 729 | aten._addmm_activation.default, | 729 | aten._addmm_activation.default, |
| 730 | aten._addmm_activation.out, | 730 | aten._addmm_activation.out, |
| 731 | aten._cdist_backward.default, | 731 | aten._cdist_backward.default, |
| 732 | aten._cdist_backward.out, | 732 | aten._cdist_backward.out, |
| 733 | aten._cdist_forward.default, | 733 | aten._cdist_forward.default, |
| 734 | aten._cdist_forward.out, | 734 | aten._cdist_forward.out, |
| 735 | aten._cudnn_rnn.default, | 735 | aten._cudnn_rnn.default, |
| 736 | aten._cudnn_rnn.out, | 736 | aten._cudnn_rnn.out, |
| 737 | aten._cudnn_rnn_backward.default, | 737 | aten._cudnn_rnn_backward.default, |
| 738 | aten._cudnn_rnn_backward.out, | 738 | aten._cudnn_rnn_backward.out, |
| 739 | aten._dyn_quant_matmul_4bit.default, | 739 | aten._dyn_quant_matmul_4bit.default, |
| 740 | aten._dyn_quant_pack_4bit_weight.default, | 740 | aten._dyn_quant_pack_4bit_weight.default, |
| 741 | aten._efficient_attention_backward.default, | 741 | aten._efficient_attention_backward.default, |
| 742 | aten._efficient_attention_forward.default, | 742 | aten._efficient_attention_forward.default, |
| 743 | aten._efficientzerotensor.default, | 743 | aten._efficientzerotensor.default, |
| 744 | aten._efficientzerotensor.out, | 744 | aten._efficientzerotensor.out, |
| 745 | aten._embedding_bag.default, | 745 | aten._embedding_bag.default, |
| 746 | aten._embedding_bag.out, | 746 | aten._embedding_bag.out, |
| 747 | aten._embedding_bag_backward.default, | 747 | aten._embedding_bag_backward.default, |
| 748 | aten._embedding_bag_forward_only.default, | 748 | aten._embedding_bag_forward_only.default, |
| 749 | aten._embedding_bag_forward_only.out, | 749 | aten._embedding_bag_forward_only.out, |
| 750 | aten._embedding_bag_per_sample_weights_backward.default, | 750 | aten._embedding_bag_per_sample_weights_backward.default, |
| 751 | aten._embedding_bag_per_sample_weights_backward.out, | 751 | aten._embedding_bag_per_sample_weights_backward.out, |
| 752 | aten._fft_r2c.default, | 752 | aten._fft_r2c.default, |
| 753 | aten._fft_r2c.out, | 753 | aten._fft_r2c.out, |
| 754 | aten._flash_attention_backward.default, | 754 | aten._flash_attention_backward.default, |
| 755 | aten._flash_attention_forward.default, | 755 | aten._flash_attention_forward.default, |
| 756 | aten._fused_moving_avg_obs_fq_helper.default, | 756 | aten._fused_moving_avg_obs_fq_helper.default, |
| 757 | aten._fused_moving_avg_obs_fq_helper.out, | 757 | aten._fused_moving_avg_obs_fq_helper.out, |
| 758 | aten._fused_moving_avg_obs_fq_helper_functional.default, | 758 | aten._fused_moving_avg_obs_fq_helper_functional.default, |
| 759 | aten._histogramdd_bin_edges.default, | 759 | aten._histogramdd_bin_edges.default, |
| 760 | aten._histogramdd_from_bin_cts.default, | 760 | aten._histogramdd_from_bin_cts.default, |
| 761 | aten._linalg_check_errors.default, | 761 | aten._linalg_check_errors.default, |
| 762 | aten._linalg_det.default, | 762 | aten._linalg_det.default, |
| 763 | aten._linalg_det.result, | 763 | aten._linalg_det.result, |
| 764 | aten._linalg_eigh.default, | 764 | aten._linalg_eigh.default, |
| 765 | aten._linalg_eigh.eigenvalues, | 765 | aten._linalg_eigh.eigenvalues, |
| 766 | aten._linalg_slogdet.default, | 766 | aten._linalg_slogdet.default, |
| 767 | aten._linalg_slogdet.sign, | 767 | aten._linalg_slogdet.sign, |
| 768 | aten._linalg_solve_ex.default, | 768 | aten._linalg_solve_ex.default, |
| 769 | aten._linalg_solve_ex.result, | 769 | aten._linalg_solve_ex.result, |
| 770 | aten._linalg_svd.U, | 770 | aten._linalg_svd.U, |
| 771 | aten._linalg_svd.default, | 771 | aten._linalg_svd.default, |
| 772 | aten._pdist_backward.default, | 772 | aten._pdist_backward.default, |
| 773 | aten._pdist_backward.out, | 773 | aten._pdist_backward.out, |
| 774 | aten._pdist_forward.default, | 774 | aten._pdist_forward.default, |
| 775 | aten._pdist_forward.out, | 775 | aten._pdist_forward.out, |
| 776 | aten._scaled_dot_product_cudnn_attention.default, | 776 | aten._scaled_dot_product_cudnn_attention.default, |
| 777 | aten._scaled_dot_product_cudnn_attention_backward.default, | 777 | aten._scaled_dot_product_cudnn_attention_backward.default, |
| 778 | aten._scaled_dot_product_efficient_attention.default, | 778 | aten._scaled_dot_product_efficient_attention.default, |
| 779 | aten._scaled_dot_product_efficient_attention_backward.default, | 779 | aten._scaled_dot_product_efficient_attention_backward.default, |
| 780 | aten._scaled_dot_product_flash_attention.default, | 780 | aten._scaled_dot_product_flash_attention.default, |
| 781 | aten._scaled_dot_product_flash_attention_backward.default, | 781 | aten._scaled_dot_product_flash_attention_backward.default, |
| 782 | aten._scaled_dot_product_flash_attention_for_cpu.default, | 782 | aten._scaled_dot_product_flash_attention_for_cpu.default, |
| 783 | aten._scaled_dot_product_flash_attention_for_cpu_backward.default, | 783 | aten._scaled_dot_product_flash_attention_for_cpu_backward.default, |
| 784 | aten._scaled_dot_product_fused_attention_overrideable.default, | 784 | aten._scaled_dot_product_fused_attention_overrideable.default, |
| 785 | aten._scaled_dot_product_fused_attention_overrideable_backward.default, | 785 | aten._scaled_dot_product_fused_attention_overrideable_backward.default, |
| 786 | aten._segment_reduce_backward.default, | 786 | aten._segment_reduce_backward.default, |
| 787 | aten._sparse_coo_tensor_with_dims_and_tensors.default, | 787 | aten._sparse_coo_tensor_with_dims_and_tensors.default, |
| 788 | aten._sparse_coo_tensor_with_dims_and_tensors.out, | 788 | aten._sparse_coo_tensor_with_dims_and_tensors.out, |
| 789 | aten._thnn_fused_lstm_cell.default, | 789 | aten._thnn_fused_lstm_cell.default, |
| 790 | aten._thnn_fused_lstm_cell.out, | 790 | aten._thnn_fused_lstm_cell.out, |
| 791 | aten._to_sparse.default, | 791 | aten._to_sparse.default, |
| 792 | aten._to_sparse.out, | 792 | aten._to_sparse.out, |
| 793 | aten._to_sparse.sparse_dim, | 793 | aten._to_sparse.sparse_dim, |
| 794 | aten._to_sparse.sparse_dim_out, | 794 | aten._to_sparse.sparse_dim_out, |
| 795 | aten._trilinear.default, | 795 | aten._trilinear.default, |
| 796 | aten._trilinear.out, | 796 | aten._trilinear.out, |
| 797 | aten.adaptive_max_pool2d_backward.default, | 797 | aten.adaptive_max_pool2d_backward.default, |
| 798 | aten.adaptive_max_pool2d_backward.grad_input, | 798 | aten.adaptive_max_pool2d_backward.grad_input, |
| 799 | aten.adaptive_max_pool3d.default, | 799 | aten.adaptive_max_pool3d.default, |
| 800 | aten.adaptive_max_pool3d.out, | 800 | aten.adaptive_max_pool3d.out, |
| 801 | aten.adaptive_max_pool3d_backward.default, | 801 | aten.adaptive_max_pool3d_backward.default, |
| 802 | aten.adaptive_max_pool3d_backward.grad_input, | 802 | aten.adaptive_max_pool3d_backward.grad_input, |
| 803 | aten.addbmm.default, | 803 | aten.addbmm.default, |
| 804 | aten.addbmm.out, | 804 | aten.addbmm.out, |
| 805 | aten.angle.Scalar, | 805 | aten.angle.Scalar, |
| 806 | aten.angle.complex, | 806 | aten.angle.complex, |
| 807 | aten.angle.default, | 807 | aten.angle.default, |
| 808 | aten.angle.float, | 808 | aten.angle.float, |
| 809 | aten.angle.int, | 809 | aten.angle.int, |
| 810 | aten.angle.out, | 810 | aten.angle.out, |
| 811 | aten.cholesky_inverse.default, | 811 | aten.cholesky_inverse.default, |
| 812 | aten.cholesky_inverse.out, | 812 | aten.cholesky_inverse.out, |
| 813 | aten.cholesky_solve.default, | 813 | aten.cholesky_solve.default, |
| 814 | aten.cholesky_solve.out, | 814 | aten.cholesky_solve.out, |
| 815 | aten.convolution_backward.default, | 815 | aten.convolution_backward.default, |
| 816 | aten.convolution_backward.out, | 816 | aten.convolution_backward.out, |
| 817 | aten.cummax.default, | 817 | aten.cummax.default, |
| 818 | aten.cummin.default, | 818 | aten.cummin.default, |
| 819 | aten.cumprod.default, | 819 | aten.cumprod.default, |
| 820 | aten.cumsum.default, | 820 | aten.cumsum.default, |
| 821 | aten.digamma.default, | 821 | aten.digamma.default, |
| 822 | aten.digamma.out, | 822 | aten.digamma.out, |
| 823 | aten.exponential.default, | 823 | aten.exponential.default, |
| 824 | aten.fractional_max_pool2d_backward.default, | 824 | aten.fractional_max_pool2d_backward.default, |
| 825 | aten.fractional_max_pool2d_backward.grad_input, | 825 | aten.fractional_max_pool2d_backward.grad_input, |
| 826 | aten.fractional_max_pool3d.default, | 826 | aten.fractional_max_pool3d.default, |
| 827 | aten.fractional_max_pool3d.output, | 827 | aten.fractional_max_pool3d.output, |
| 828 | aten.fractional_max_pool3d_backward.default, | 828 | aten.fractional_max_pool3d_backward.default, |
| 829 | aten.fractional_max_pool3d_backward.grad_input, | 829 | aten.fractional_max_pool3d_backward.grad_input, |
| 830 | aten.gcd.default, | 830 | aten.gcd.default, |
| 831 | aten.geqrf.a, | 831 | aten.geqrf.a, |
| 832 | aten.geqrf.default, | 832 | aten.geqrf.default, |
| 833 | aten.grid_sampler_2d_backward.default, | 833 | aten.grid_sampler_2d_backward.default, |
| 834 | aten.grid_sampler_2d_backward.out, | 834 | aten.grid_sampler_2d_backward.out, |
| 835 | aten.histc.default, | 835 | aten.histc.default, |
| 836 | aten.histc.out, | 836 | aten.histc.out, |
| 837 | aten.histogram.bin_ct, | 837 | aten.histogram.bin_ct, |
| 838 | aten.igamma.default, | 838 | aten.igamma.default, |
| 839 | aten.igamma.out, | 839 | aten.igamma.out, |
| 840 | aten.igammac.default, | 840 | aten.igammac.default, |
| 841 | aten.igammac.out, | 841 | aten.igammac.out, |
| 842 | aten.index_reduce.default, | 842 | aten.index_reduce.default, |
| 843 | aten.index_reduce.out, | 843 | aten.index_reduce.out, |
| 844 | aten.kthvalue.default, | 844 | aten.kthvalue.default, |
| 845 | aten.kthvalue.dimname, | 845 | aten.kthvalue.dimname, |
| 846 | aten.kthvalue.dimname_out, | 846 | aten.kthvalue.dimname_out, |
| 847 | aten.kthvalue.values, | 847 | aten.kthvalue.values, |
| 848 | aten.linalg_cholesky_ex.L, | 848 | aten.linalg_cholesky_ex.L, |
| 849 | aten.linalg_cholesky_ex.default, | 849 | aten.linalg_cholesky_ex.default, |
| 850 | aten.linalg_householder_product.default, | 850 | aten.linalg_householder_product.default, |
| 851 | aten.linalg_householder_product.out, | 851 | aten.linalg_householder_product.out, |
| 852 | aten.linalg_inv_ex.default, | 852 | aten.linalg_inv_ex.default, |
| 853 | aten.linalg_inv_ex.inverse, | 853 | aten.linalg_inv_ex.inverse, |
| 854 | aten.linalg_ldl_factor_ex.default, | 854 | aten.linalg_ldl_factor_ex.default, |
| 855 | aten.linalg_ldl_factor_ex.out, | 855 | aten.linalg_ldl_factor_ex.out, |
| 856 | aten.linalg_ldl_solve.default, | 856 | aten.linalg_ldl_solve.default, |
| 857 | aten.linalg_ldl_solve.out, | 857 | aten.linalg_ldl_solve.out, |
| 858 | aten.linalg_lu.default, | 858 | aten.linalg_lu.default, |
| 859 | aten.linalg_lu.out, | 859 | aten.linalg_lu.out, |
| 860 | aten.linalg_lu_factor_ex.default, | 860 | aten.linalg_lu_factor_ex.default, |
| 861 | aten.linalg_lu_factor_ex.out, | 861 | aten.linalg_lu_factor_ex.out, |
| 862 | aten.linalg_lu_solve.default, | 862 | aten.linalg_lu_solve.default, |
| 863 | aten.linalg_lu_solve.out, | 863 | aten.linalg_lu_solve.out, |
| 864 | aten.linalg_matrix_exp.default, | 864 | aten.linalg_matrix_exp.default, |
| 865 | aten.linalg_matrix_exp.out, | 865 | aten.linalg_matrix_exp.out, |
| 866 | aten.linalg_pinv.atol_rtol_tensor, | 866 | aten.linalg_pinv.atol_rtol_tensor, |
| 867 | aten.linalg_qr.default, | 867 | aten.linalg_qr.default, |
| 868 | aten.linalg_qr.out, | 868 | aten.linalg_qr.out, |
| 869 | aten.linalg_solve_triangular.default, | 869 | aten.linalg_solve_triangular.default, |
| 870 | aten.linalg_solve_triangular.out, | 870 | aten.linalg_solve_triangular.out, |
| 871 | aten.logcumsumexp.default, | 871 | aten.logcumsumexp.default, |
| 872 | aten.lu_unpack.default, | 872 | aten.lu_unpack.default, |
| 873 | aten.lu_unpack.out, | 873 | aten.lu_unpack.out, |
| 874 | aten.masked_scatter.default, | 874 | aten.masked_scatter.default, |
| 875 | aten.masked_scatter.out, | 875 | aten.masked_scatter.out, |
| 876 | aten.masked_scatter_backward.default, | 876 | aten.masked_scatter_backward.default, |
| 877 | aten.max_pool3d_with_indices.default, | 877 | aten.max_pool3d_with_indices.default, |
| 878 | aten.max_pool3d_with_indices.out, | 878 | aten.max_pool3d_with_indices.out, |
| 879 | aten.max_pool3d_with_indices_backward.default, | 879 | aten.max_pool3d_with_indices_backward.default, |
| 880 | aten.max_pool3d_with_indices_backward.grad_input, | 880 | aten.max_pool3d_with_indices_backward.grad_input, |
| 881 | aten.median.default, | 881 | aten.median.default, |
| 882 | aten.median.dim, | 882 | aten.median.dim, |
| 883 | aten.median.dim_values, | 883 | aten.median.dim_values, |
| 884 | aten.median.names_dim, | 884 | aten.median.names_dim, |
| 885 | aten.median.names_dim_values, | 885 | aten.median.names_dim_values, |
| 886 | aten.median.out, | 886 | aten.median.out, |
| 887 | aten.mode.default, | 887 | aten.mode.default, |
| 888 | aten.mode.dimname, | 888 | aten.mode.dimname, |
| 889 | aten.mode.dimname_out, | 889 | aten.mode.dimname_out, |
| 890 | aten.mode.values, | 890 | aten.mode.values, |
| 891 | aten.nanmedian.default, | 891 | aten.nanmedian.default, |
| 892 | aten.nanmedian.dim, | 892 | aten.nanmedian.dim, |
| 893 | aten.nanmedian.dim_values, | 893 | aten.nanmedian.dim_values, |
| 894 | aten.nanmedian.names_dim, | 894 | aten.nanmedian.names_dim, |
| 895 | aten.nanmedian.names_dim_values, | 895 | aten.nanmedian.names_dim_values, |
| 896 | aten.nanmedian.out, | 896 | aten.nanmedian.out, |
| 897 | aten.nonzero.default, | 897 | aten.nonzero.default, |
| 898 | aten.ormqr.default, | 898 | aten.ormqr.default, |
| 899 | aten.ormqr.out, | 899 | aten.ormqr.out, |
| 900 | aten.polygamma.default, | 900 | aten.polygamma.default, |
| 901 | aten.polygamma.out, | 901 | aten.polygamma.out, |
| 902 | aten.rand.default, | 902 | aten.rand.default, |
| 903 | aten.rand.generator, | 903 | aten.rand.generator, |
| 904 | aten.randint.default, | 904 | aten.randint.default, |
| 905 | aten.randint.generator, | 905 | aten.randint.generator, |
| 906 | aten.randint.generator_out, | 906 | aten.randint.generator_out, |
| 907 | aten.randint.low, | 907 | aten.randint.low, |
| 908 | aten.randint.low_generator, | 908 | aten.randint.low_generator, |
| 909 | aten.randint.low_generator_out, | 909 | aten.randint.low_generator_out, |
| 910 | aten.randint.low_out, | 910 | aten.randint.low_out, |
| 911 | aten.randint.out, | 911 | aten.randint.out, |
| 912 | aten.randn.default, | 912 | aten.randn.default, |
| 913 | aten.randn.generator, | 913 | aten.randn.generator, |
| 914 | aten.randperm.default, | 914 | aten.randperm.default, |
| 915 | aten.randperm.generator, | 915 | aten.randperm.generator, |
| 916 | aten.randperm.generator_out, | 916 | aten.randperm.generator_out, |
| 917 | aten.randperm.out, | 917 | aten.randperm.out, |
| 918 | aten.replication_pad1d_backward.default, | 918 | aten.replication_pad1d_backward.default, |
| 919 | aten.replication_pad1d_backward.grad_input, | 919 | aten.replication_pad1d_backward.grad_input, |
| 920 | aten.replication_pad2d_backward.default, | 920 | aten.replication_pad2d_backward.default, |
| 921 | aten.replication_pad2d_backward.grad_input, | 921 | aten.replication_pad2d_backward.grad_input, |
| 922 | aten.resize_.default, | 922 | aten.resize_.default, |
| 923 | aten.resize_as_.default, | 923 | aten.resize_as_.default, |
| 924 | aten.segment_reduce.default, | 924 | aten.segment_reduce.default, |
| 925 | aten.soft_margin_loss_backward.default, | 925 | aten.soft_margin_loss_backward.default, |
| 926 | aten.soft_margin_loss_backward.grad_input, | 926 | aten.soft_margin_loss_backward.grad_input, |
| 927 | aten.sort.Tensor, | 927 | aten.sort.Tensor, |
| 928 | aten.sort.any, | 928 | aten.sort.any, |
| 929 | aten.sort.bool, | 929 | aten.sort.bool, |
| 930 | aten.sort.default, | 930 | aten.sort.default, |
| 931 | aten.sort.dimname, | 931 | aten.sort.dimname, |
| 932 | aten.sort.dimname_stable, | 932 | aten.sort.dimname_stable, |
| 933 | aten.sort.dimname_values, | 933 | aten.sort.dimname_values, |
| 934 | aten.sort.dimname_values_stable, | 934 | aten.sort.dimname_values_stable, |
| 935 | aten.sort.float, | 935 | aten.sort.float, |
| 936 | aten.sort.int, | 936 | aten.sort.int, |
| 937 | aten.sort.stable, | 937 | aten.sort.stable, |
| 938 | aten.sort.str, | 938 | aten.sort.str, |
| 939 | aten.sort.values, | 939 | aten.sort.values, |
| 940 | aten.sort.values_stable, | 940 | aten.sort.values_stable, |
| 941 | aten.special_airy_ai.default, | 941 | aten.special_airy_ai.default, |
| 942 | aten.special_airy_ai.out, | 942 | aten.special_airy_ai.out, |
| 943 | aten.special_chebyshev_polynomial_t.default, | 943 | aten.special_chebyshev_polynomial_t.default, |
| 944 | aten.special_chebyshev_polynomial_t.n_scalar, | 944 | aten.special_chebyshev_polynomial_t.n_scalar, |
| 945 | aten.special_chebyshev_polynomial_t.n_scalar_out, | 945 | aten.special_chebyshev_polynomial_t.n_scalar_out, |
| 946 | aten.special_chebyshev_polynomial_t.out, | 946 | aten.special_chebyshev_polynomial_t.out, |
| 947 | aten.special_chebyshev_polynomial_t.x_scalar, | 947 | aten.special_chebyshev_polynomial_t.x_scalar, |
| 948 | aten.special_chebyshev_polynomial_t.x_scalar_out, | 948 | aten.special_chebyshev_polynomial_t.x_scalar_out, |
| 949 | aten.special_chebyshev_polynomial_u.default, | 949 | aten.special_chebyshev_polynomial_u.default, |
| 950 | aten.special_chebyshev_polynomial_u.n_scalar, | 950 | aten.special_chebyshev_polynomial_u.n_scalar, |
| 951 | aten.special_chebyshev_polynomial_u.n_scalar_out, | 951 | aten.special_chebyshev_polynomial_u.n_scalar_out, |
| 952 | aten.special_chebyshev_polynomial_u.out, | 952 | aten.special_chebyshev_polynomial_u.out, |
| 953 | aten.special_chebyshev_polynomial_u.x_scalar, | 953 | aten.special_chebyshev_polynomial_u.x_scalar, |
| 954 | aten.special_chebyshev_polynomial_u.x_scalar_out, | 954 | aten.special_chebyshev_polynomial_u.x_scalar_out, |
| 955 | aten.special_chebyshev_polynomial_v.default, | 955 | aten.special_chebyshev_polynomial_v.default, |
| 956 | aten.special_chebyshev_polynomial_v.n_scalar, | 956 | aten.special_chebyshev_polynomial_v.n_scalar, |
| 957 | aten.special_chebyshev_polynomial_v.n_scalar_out, | 957 | aten.special_chebyshev_polynomial_v.n_scalar_out, |
| 958 | aten.special_chebyshev_polynomial_v.out, | 958 | aten.special_chebyshev_polynomial_v.out, |
| 959 | aten.special_chebyshev_polynomial_v.x_scalar, | 959 | aten.special_chebyshev_polynomial_v.x_scalar, |
| 960 | aten.special_chebyshev_polynomial_v.x_scalar_out, | 960 | aten.special_chebyshev_polynomial_v.x_scalar_out, |
| 961 | aten.special_chebyshev_polynomial_w.default, | 961 | aten.special_chebyshev_polynomial_w.default, |
| 962 | aten.special_chebyshev_polynomial_w.n_scalar, | 962 | aten.special_chebyshev_polynomial_w.n_scalar, |
| 963 | aten.special_chebyshev_polynomial_w.n_scalar_out, | 963 | aten.special_chebyshev_polynomial_w.n_scalar_out, |
| 964 | aten.special_chebyshev_polynomial_w.out, | 964 | aten.special_chebyshev_polynomial_w.out, |
| 965 | aten.special_chebyshev_polynomial_w.x_scalar, | 965 | aten.special_chebyshev_polynomial_w.x_scalar, |
| 966 | aten.special_chebyshev_polynomial_w.x_scalar_out, | 966 | aten.special_chebyshev_polynomial_w.x_scalar_out, |
| 967 | aten.special_gammainc.default, | 967 | aten.special_gammainc.default, |
| 968 | aten.special_gammainc.out, | 968 | aten.special_gammainc.out, |
| 969 | aten.special_gammaincc.default, | 969 | aten.special_gammaincc.default, |
| 970 | aten.special_gammaincc.out, | 970 | aten.special_gammaincc.out, |
| 971 | aten.special_hermite_polynomial_h.default, | 971 | aten.special_hermite_polynomial_h.default, |
| 972 | aten.special_hermite_polynomial_h.n_scalar, | 972 | aten.special_hermite_polynomial_h.n_scalar, |
| 973 | aten.special_hermite_polynomial_h.n_scalar_out, | 973 | aten.special_hermite_polynomial_h.n_scalar_out, |
| 974 | aten.special_hermite_polynomial_h.out, | 974 | aten.special_hermite_polynomial_h.out, |
| 975 | aten.special_hermite_polynomial_h.x_scalar, | 975 | aten.special_hermite_polynomial_h.x_scalar, |
| 976 | aten.special_hermite_polynomial_h.x_scalar_out, | 976 | aten.special_hermite_polynomial_h.x_scalar_out, |
| 977 | aten.special_hermite_polynomial_he.default, | 977 | aten.special_hermite_polynomial_he.default, |
| 978 | aten.special_hermite_polynomial_he.n_scalar, | 978 | aten.special_hermite_polynomial_he.n_scalar, |
| 979 | aten.special_hermite_polynomial_he.n_scalar_out, | 979 | aten.special_hermite_polynomial_he.n_scalar_out, |
| 980 | aten.special_hermite_polynomial_he.out, | 980 | aten.special_hermite_polynomial_he.out, |
| 981 | aten.special_hermite_polynomial_he.x_scalar, | 981 | aten.special_hermite_polynomial_he.x_scalar, |
| 982 | aten.special_hermite_polynomial_he.x_scalar_out, | 982 | aten.special_hermite_polynomial_he.x_scalar_out, |
| 983 | aten.special_i0e.default, | 983 | aten.special_i0e.default, |
| 984 | aten.special_i0e.out, | 984 | aten.special_i0e.out, |
| 985 | aten.special_i1e.default, | 985 | aten.special_i1e.default, |
| 986 | aten.special_i1e.out, | 986 | aten.special_i1e.out, |
| 987 | aten.special_laguerre_polynomial_l.default, | 987 | aten.special_laguerre_polynomial_l.default, |
| 988 | aten.special_laguerre_polynomial_l.n_scalar, | 988 | aten.special_laguerre_polynomial_l.n_scalar, |
| 989 | aten.special_laguerre_polynomial_l.n_scalar_out, | 989 | aten.special_laguerre_polynomial_l.n_scalar_out, |
| 990 | aten.special_laguerre_polynomial_l.out, | 990 | aten.special_laguerre_polynomial_l.out, |
| 991 | aten.special_laguerre_polynomial_l.x_scalar, | 991 | aten.special_laguerre_polynomial_l.x_scalar, |
| 992 | aten.special_laguerre_polynomial_l.x_scalar_out, | 992 | aten.special_laguerre_polynomial_l.x_scalar_out, |
| 993 | aten.special_legendre_polynomial_p.default, | 993 | aten.special_legendre_polynomial_p.default, |
| 994 | aten.special_legendre_polynomial_p.n_scalar, | 994 | aten.special_legendre_polynomial_p.n_scalar, |
| 995 | aten.special_legendre_polynomial_p.n_scalar_out, | 995 | aten.special_legendre_polynomial_p.n_scalar_out, |
| 996 | aten.special_legendre_polynomial_p.out, | 996 | aten.special_legendre_polynomial_p.out, |
| 997 | aten.special_legendre_polynomial_p.x_scalar, | 997 | aten.special_legendre_polynomial_p.x_scalar, |
| 998 | aten.special_legendre_polynomial_p.x_scalar_out, | 998 | aten.special_legendre_polynomial_p.x_scalar_out, |
| 999 | aten.special_log_ndtr.default, | 999 | aten.special_log_ndtr.default, |
| 1000 | aten.special_log_ndtr.out, | 1000 | aten.special_log_ndtr.out, |
| 1001 | aten.special_modified_bessel_k0.default, | 1001 | aten.special_modified_bessel_k0.default, |
| 1002 | aten.special_modified_bessel_k0.out, | 1002 | aten.special_modified_bessel_k0.out, |
| 1003 | aten.special_modified_bessel_k1.default, | 1003 | aten.special_modified_bessel_k1.default, |
| 1004 | aten.special_modified_bessel_k1.out, | 1004 | aten.special_modified_bessel_k1.out, |
| 1005 | aten.special_ndtr.default, | 1005 | aten.special_ndtr.default, |
| 1006 | aten.special_ndtr.out, | 1006 | aten.special_ndtr.out, |
| 1007 | aten.special_ndtri.default, | 1007 | aten.special_ndtri.default, |
| 1008 | aten.special_ndtri.out, | 1008 | aten.special_ndtri.out, |
| 1009 | aten.special_scaled_modified_bessel_k0.default, | 1009 | aten.special_scaled_modified_bessel_k0.default, |
| 1010 | aten.special_scaled_modified_bessel_k0.out, | 1010 | aten.special_scaled_modified_bessel_k0.out, |
| 1011 | aten.special_scaled_modified_bessel_k1.default, | 1011 | aten.special_scaled_modified_bessel_k1.default, |
| 1012 | aten.special_scaled_modified_bessel_k1.out, | 1012 | aten.special_scaled_modified_bessel_k1.out, |
| 1013 | aten.special_shifted_chebyshev_polynomial_t.default, | 1013 | aten.special_shifted_chebyshev_polynomial_t.default, |
| 1014 | aten.special_shifted_chebyshev_polynomial_t.n_scalar, | 1014 | aten.special_shifted_chebyshev_polynomial_t.n_scalar, |
| 1015 | aten.special_shifted_chebyshev_polynomial_t.n_scalar_out, | 1015 | aten.special_shifted_chebyshev_polynomial_t.n_scalar_out, |
| 1016 | aten.special_shifted_chebyshev_polynomial_t.out, | 1016 | aten.special_shifted_chebyshev_polynomial_t.out, |
| 1017 | aten.special_shifted_chebyshev_polynomial_t.x_scalar, | 1017 | aten.special_shifted_chebyshev_polynomial_t.x_scalar, |
| 1018 | aten.special_shifted_chebyshev_polynomial_t.x_scalar_out, | 1018 | aten.special_shifted_chebyshev_polynomial_t.x_scalar_out, |
| 1019 | aten.special_shifted_chebyshev_polynomial_u.default, | 1019 | aten.special_shifted_chebyshev_polynomial_u.default, |
| 1020 | aten.special_shifted_chebyshev_polynomial_u.n_scalar, | 1020 | aten.special_shifted_chebyshev_polynomial_u.n_scalar, |
| 1021 | aten.special_shifted_chebyshev_polynomial_u.n_scalar_out, | 1021 | aten.special_shifted_chebyshev_polynomial_u.n_scalar_out, |
| 1022 | aten.special_shifted_chebyshev_polynomial_u.out, | 1022 | aten.special_shifted_chebyshev_polynomial_u.out, |
| 1023 | aten.special_shifted_chebyshev_polynomial_u.x_scalar, | 1023 | aten.special_shifted_chebyshev_polynomial_u.x_scalar, |
| 1024 | aten.special_shifted_chebyshev_polynomial_u.x_scalar_out, | 1024 | aten.special_shifted_chebyshev_polynomial_u.x_scalar_out, |
| 1025 | aten.special_shifted_chebyshev_polynomial_v.default, | 1025 | aten.special_shifted_chebyshev_polynomial_v.default, |
| 1026 | aten.special_shifted_chebyshev_polynomial_v.n_scalar, | 1026 | aten.special_shifted_chebyshev_polynomial_v.n_scalar, |
| 1027 | aten.special_shifted_chebyshev_polynomial_v.n_scalar_out, | 1027 | aten.special_shifted_chebyshev_polynomial_v.n_scalar_out, |
| 1028 | aten.special_shifted_chebyshev_polynomial_v.out, | 1028 | aten.special_shifted_chebyshev_polynomial_v.out, |
| 1029 | aten.special_shifted_chebyshev_polynomial_v.x_scalar, | 1029 | aten.special_shifted_chebyshev_polynomial_v.x_scalar, |
| 1030 | aten.special_shifted_chebyshev_polynomial_v.x_scalar_out, | 1030 | aten.special_shifted_chebyshev_polynomial_v.x_scalar_out, |
| 1031 | aten.special_shifted_chebyshev_polynomial_w.default, | 1031 | aten.special_shifted_chebyshev_polynomial_w.default, |
| 1032 | aten.special_shifted_chebyshev_polynomial_w.n_scalar, | 1032 | aten.special_shifted_chebyshev_polynomial_w.n_scalar, |
| 1033 | aten.special_shifted_chebyshev_polynomial_w.n_scalar_out, | 1033 | aten.special_shifted_chebyshev_polynomial_w.n_scalar_out, |
| 1034 | aten.special_shifted_chebyshev_polynomial_w.out, | 1034 | aten.special_shifted_chebyshev_polynomial_w.out, |
| 1035 | aten.special_shifted_chebyshev_polynomial_w.x_scalar, | 1035 | aten.special_shifted_chebyshev_polynomial_w.x_scalar, |
| 1036 | aten.special_shifted_chebyshev_polynomial_w.x_scalar_out, | 1036 | aten.special_shifted_chebyshev_polynomial_w.x_scalar_out, |
| 1037 | aten.special_spherical_bessel_j0.default, | 1037 | aten.special_spherical_bessel_j0.default, |
| 1038 | aten.special_spherical_bessel_j0.out, | 1038 | aten.special_spherical_bessel_j0.out, |
| 1039 | aten.special_zeta.default, | 1039 | aten.special_zeta.default, |
| 1040 | aten.special_zeta.other_scalar, | 1040 | aten.special_zeta.other_scalar, |
| 1041 | aten.special_zeta.other_scalar_out, | 1041 | aten.special_zeta.other_scalar_out, |
| 1042 | aten.special_zeta.out, | 1042 | aten.special_zeta.out, |
| 1043 | aten.special_zeta.self_scalar, | 1043 | aten.special_zeta.self_scalar, |
| 1044 | aten.special_zeta.self_scalar_out, | 1044 | aten.special_zeta.self_scalar_out, |
| 1045 | aten.to_sparse.default, | 1045 | aten.to_sparse.default, |
| 1046 | aten.to_sparse.sparse_dim, | 1046 | aten.to_sparse.sparse_dim, |
| 1047 | aten.topk.default, | 1047 | aten.topk.default, |
| 1048 | aten.topk.values, | 1048 | aten.topk.values, |
| 1049 | aten.triangular_solve.X, | 1049 | aten.triangular_solve.X, |
| 1050 | aten.triangular_solve.default, | 1050 | aten.triangular_solve.default, |
| 1051 | aten.uniform.default, | 1051 | aten.uniform.default, |
| 1052 | aten.uniform.out, | 1052 | aten.uniform.out, |
| 1053 | aten.upsample_bicubic2d_backward.default, | 1053 | aten.upsample_bicubic2d_backward.default, |
| 1054 | aten.upsample_bicubic2d_backward.grad_input, | 1054 | aten.upsample_bicubic2d_backward.grad_input, |
| 1055 | aten.upsample_linear1d_backward.default, | 1055 | aten.upsample_linear1d_backward.default, |
| 1056 | aten.upsample_linear1d_backward.grad_input, | 1056 | aten.upsample_linear1d_backward.grad_input, |
| 1057 | aten.upsample_trilinear3d_backward.default, | 1057 | aten.upsample_trilinear3d_backward.default, |
| 1058 | aten.upsample_trilinear3d_backward.grad_input, | 1058 | aten.upsample_trilinear3d_backward.grad_input, |
| 1059 | aten.view_as_complex.default, | 1059 | aten.view_as_complex.default, |
| 1060 | aten.zeros.names, | 1060 | aten.zeros.names, |
| 1061 | auto_functionalized, | 1061 | auto_functionalized, |
| 1062 | graphsafe_run_with_rng_state, | 1062 | graphsafe_run_with_rng_state, |
| 1063 | prims.digamma.default, | 1063 | prims.digamma.default, |
| 1064 | prims.igamma.default, | 1064 | prims.igamma.default, |
| 1065 | prims.igammac.default, | 1065 | prims.igammac.default, |
| 1066 | quantized.max_pool2d.default, | 1066 | quantized.max_pool2d.default, |
| 1067 | run_and_save_rng_state, | 1067 | run_and_save_rng_state, |
| 1068 | run_with_rng_state, | 1068 | run_with_rng_state, |
| 1069 | ] | 1069 | ] |
| 1070 | 1070 | ||
| 1071 | FALLBACK_LIST = TORCH_NATIVE_FALLBACK_LIST + NPU_EXTRA_FALLBACK_LIST | 1071 | FALLBACK_LIST = TORCH_NATIVE_FALLBACK_LIST + NPU_EXTRA_FALLBACK_LIST |
| 1072 | 1072 | ||
| 1073 | if inductor_indirect_memory_mode != 'linear': | 1073 | if inductor_indirect_memory_mode != 'linear': |
| 1074 | FALLBACK_LIST += [ | 1074 | FALLBACK_LIST += [ |
| 1075 | aten.isnan, | 1075 | aten.isnan, |
| 1076 | ] | 1076 | ] |
| 1077 | 1077 | ||
| 1078 | INDIRECT_MEM_FALLBACK_LIST = [ | 1078 | INDIRECT_MEM_FALLBACK_LIST = [ |
| 1079 | aten.cat, | 1079 | aten.cat, |
| 1080 | aten.embedding, | 1080 | aten.embedding, |
| 1081 | aten.embedding.default, | 1081 | aten.embedding.default, |
| 1082 | aten.embedding.out, | 1082 | aten.embedding.out, |
| 1083 | aten.gather, | 1083 | aten.gather, |
| 1084 | aten.gather.default, | 1084 | aten.gather.default, |
| 1085 | aten.gather.dimname_out, | 1085 | aten.gather.dimname_out, |
| 1086 | aten.gather.out, | 1086 | aten.gather.out, |
| 1087 | aten.index, | 1087 | aten.index, |
| 1088 | aten.index.Tensor, | 1088 | aten.index.Tensor, |
| 1089 | aten._unsafe_index, | 1089 | aten._unsafe_index, |
| 1090 | aten._unsafe_index.Tensor, | 1090 | aten._unsafe_index.Tensor, |
| 1091 | aten.index_put, | 1091 | aten.index_put, |
| 1092 | aten.index_put.default, | 1092 | aten.index_put.default, |
| 1093 | aten.index_put_, | 1093 | aten.index_put_, |
| 1094 | aten.index_put_.default, | 1094 | aten.index_put_.default, |
| 1095 | aten._unsafe_index_put, | 1095 | aten._unsafe_index_put, |
| 1096 | aten.scatter, | 1096 | aten.scatter, |
| 1097 | aten.scatter.src, | 1097 | aten.scatter.src, |
| 1098 | aten.scatter_, | 1098 | aten.scatter_, |
| 1099 | aten.scatter_.src, | 1099 | aten.scatter_.src, |
| 1100 | aten.scatter_reduce, | 1100 | aten.scatter_reduce, |
| 1101 | aten.scatter_reduce_, | 1101 | aten.scatter_reduce_, |
| 1102 | ] | 1102 | ] |
| 1103 | 1103 | ||
| 1104 | if not inductor_indirect_memory_mode: | 1104 | if not inductor_indirect_memory_mode: |
| 1105 | FALLBACK_LIST += INDIRECT_MEM_FALLBACK_LIST | 1105 | FALLBACK_LIST += INDIRECT_MEM_FALLBACK_LIST |
| @@ -1,17 +1,17 @@ | |||
| 1 | """ | 1 | """ |
| 2 | this tool is to get and save FALLBACK_LIST in lowering_fallback_list.py | 2 | this tool is to get and save FALLBACK_LIST in lowering_fallback_list.py |
| 3 | """ | 3 | """ |
| 4 | 4 | ||
| 5 | from torch_npu._inductor import lowering_fallback_list | 5 | from torch_npu._inductor import lowering_fallback_list |
| 6 | from torch_npu._inductor.tools.aten_op_tool import write_to_file | 6 | from torch_npu._inductor.tools.aten_op_tool import write_to_file |
| 7 | 7 | ||
| 8 | 8 | ||
| 9 | def get_npu_fallback_list(): | 9 | def get_npu_fallback_list(): |
| 10 | npu_fallback_list = {str(op) for op in lowering_fallback_list.FALLBACK_LIST} | 10 | npu_fallback_list = {str(op) for op in lowering_fallback_list.FALLBACK_LIST} |
| 11 | print(f"len(lowering_fallback_list.FALLBACK_LIST): {len(lowering_fallback_list.FALLBACK_LIST)}") | 11 | print(f"len(lowering_fallback_list.FALLBACK_LIST): {len(lowering_fallback_list.FALLBACK_LIST)}") |
| 12 | return npu_fallback_list | 12 | return npu_fallback_list |
| 13 | 13 | ||
| 14 | 14 | ||
| 15 | if __name__ == "__main__": | 15 | if __name__ == "__main__": |
| 16 | npu_fallback_list = get_npu_fallback_list() | 16 | npu_fallback_list = get_npu_fallback_list() |
| 17 | write_to_file(sorted(npu_fallback_list), f"npu_fallback_list_{len(npu_fallback_list)}.txt") | 17 | write_to_file(sorted(npu_fallback_list), f"npu_fallback_list_{len(npu_fallback_list)}.txt") |
| @@ -1,6 +1,6 @@ | |||
| 1 | FILE(GLOB _AFD_SRCS *.cpp) | 1 | FILE(GLOB _AFD_SRCS *.cpp) |
| 2 | 2 | ||
| 3 | LIST(APPEND AFD_SRCS ${_AFD_SRCS}) | 3 | LIST(APPEND AFD_SRCS ${_AFD_SRCS}) |
| 4 | 4 | ||
| 5 | # Pass to parent | 5 | # Pass to parent |
| 6 | set(AFD_SRCS ${AFD_SRCS} PARENT_SCOPE) | 6 | set(AFD_SRCS ${AFD_SRCS} PARENT_SCOPE) |
| @@ -1,44 +1,44 @@ | |||
| 1 | #include "torch_npu/csrc/afd/Init.h" | 1 | #include "torch_npu/csrc/afd/Init.h" |
| 2 | #include <torch/csrc/python_headers.h> | 2 | #include <torch/csrc/python_headers.h> |
| 3 | #include <torch/csrc/Exceptions.h> | 3 | #include <torch/csrc/Exceptions.h> |
| 4 | #include <torch/csrc/utils/object_ptr.h> | 4 | #include <torch/csrc/utils/object_ptr.h> |
| 5 | #include <torch/csrc/utils/pybind.h> | 5 | #include <torch/csrc/utils/pybind.h> |
| 6 | #include "torch_npu/csrc/afd/ScheduleContext.h" | 6 | #include "torch_npu/csrc/afd/ScheduleContext.h" |
| 7 | 7 | ||
| 8 | namespace torch_npu { | 8 | namespace torch_npu { |
| 9 | namespace afd { | 9 | namespace afd { |
| 10 | 10 | ||
| 11 | PyObject *afd_init(PyObject * _unused, PyObject * noargs) | 11 | PyObject *afd_init(PyObject * _unused, PyObject * noargs) |
| 12 | { | 12 | { |
| 13 | auto torch_npu_C_module = THPObjectPtr(PyImport_ImportModule("torch_npu._C")); | 13 | auto torch_npu_C_module = THPObjectPtr(PyImport_ImportModule("torch_npu._C")); |
| 14 | if (!torch_npu_C_module) { | 14 | if (!torch_npu_C_module) { |
| 15 | throw python_error(); | 15 | throw python_error(); |
| 16 | } | 16 | } |
| 17 | auto torch_npu_C_m = py::handle(torch_npu_C_module).cast<py::module>(); | 17 | auto torch_npu_C_m = py::handle(torch_npu_C_module).cast<py::module>(); |
| 18 | 18 | ||
| 19 | auto m = torch_npu_C_m.def_submodule("_afd", "Attention-FFN Disaggregation"); | 19 | auto m = torch_npu_C_m.def_submodule("_afd", "Attention-FFN Disaggregation"); |
| 20 | auto module = py::handle(m).cast<py::module>(); | 20 | auto module = py::handle(m).cast<py::module>(); |
| 21 | 21 | ||
| 22 | py::class_<ScheduleContextHolder>(module, "ScheduleContextHolder") | 22 | py::class_<ScheduleContextHolder>(module, "ScheduleContextHolder") |
| 23 | .def(py::init<int32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint64_t, uint64_t, | 23 | .def(py::init<int32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint64_t, uint64_t, |
| 24 | uint64_t, uint64_t>()) | 24 | uint64_t, uint64_t>()) |
| 25 | .def("init", &ScheduleContextHolder::Init) | 25 | .def("init", &ScheduleContextHolder::Init) |
| 26 | .def("get_context_tensor", &ScheduleContextHolder::GetContextTensor) | 26 | .def("get_context_tensor", &ScheduleContextHolder::GetContextTensor) |
| 27 | .def("stop_schedule", &ScheduleContextHolder::StopSchedule) | 27 | .def("stop_schedule", &ScheduleContextHolder::StopSchedule) |
| 28 | .def("get_schedule_context_info", &ScheduleContextHolder::GetScheduleContextInfo); | 28 | .def("get_schedule_context_info", &ScheduleContextHolder::GetScheduleContextInfo); |
| 29 | Py_RETURN_TRUE; | 29 | Py_RETURN_TRUE; |
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | // methods on torch._C | 32 | // methods on torch._C |
| 33 | PyMethodDef methods[] = { | 33 | PyMethodDef methods[] = { |
| 34 | {"_afd_init", afd_init, METH_NOARGS, nullptr}, | 34 | {"_afd_init", afd_init, METH_NOARGS, nullptr}, |
| 35 | {nullptr, nullptr, 0, nullptr} | 35 | {nullptr, nullptr, 0, nullptr} |
| 36 | }; | 36 | }; |
| 37 | 37 | ||
| 38 | PyMethodDef *python_functions() | 38 | PyMethodDef *python_functions() |
| 39 | { | 39 | { |
| 40 | return methods; | 40 | return methods; |
| 41 | } | 41 | } |
| 42 | 42 | ||
| 43 | } // namespace afd | 43 | } // namespace afd |
| 44 | } // namespace torch_npu | 44 | } // namespace torch_npu |
| @@ -1,12 +1,12 @@ | |||
| 1 | #pragma once | 1 | #pragma once |
| 2 | 2 | ||
| 3 | #include <torch/csrc/python_headers.h> | 3 | #include <torch/csrc/python_headers.h> |
| 4 | #include "torch_npu/csrc/core/npu/NPUMacros.h" | 4 | #include "torch_npu/csrc/core/npu/NPUMacros.h" |
| 5 | 5 | ||
| 6 | namespace torch_npu { | 6 | namespace torch_npu { |
| 7 | namespace afd { | 7 | namespace afd { |
| 8 | 8 | ||
| 9 | TORCH_NPU_API PyMethodDef *python_functions(); | 9 | TORCH_NPU_API PyMethodDef *python_functions(); |
| 10 | 10 | ||
| 11 | } // namespace afd | 11 | } // namespace afd |
| 12 | } // namespace torch_npu | 12 | } // namespace torch_npu |
| @@ -1,21 +1,21 @@ | |||
| 1 | FILE(GLOB _ATEN_SRCS | 1 | FILE(GLOB _ATEN_SRCS |
| 2 | *.cpp | 2 | *.cpp |
| 3 | common/*.cpp | 3 | common/*.cpp |
| 4 | mirror/*.cpp | 4 | mirror/*.cpp |
| 5 | ops/*.cpp | 5 | ops/*.cpp |
| 6 | ops/op_api/*.cpp) | 6 | ops/op_api/*.cpp) |
| 7 | 7 | ||
| 8 | FILE(GLOB _EXCLUDE | 8 | FILE(GLOB _EXCLUDE |
| 9 | VariableTypeEverything.cpp | 9 | VariableTypeEverything.cpp |
| 10 | ADInplaceOrViewTypeEverything.cpp | 10 | ADInplaceOrViewTypeEverything.cpp |
| 11 | python_functionsEverything.cpp | 11 | python_functionsEverything.cpp |
| 12 | RegisterFunctionalizationEverything.cpp) | 12 | RegisterFunctionalizationEverything.cpp) |
| 13 | 13 | ||
| 14 | FOREACH(ITEM ${_EXCLUDE}) | 14 | FOREACH(ITEM ${_EXCLUDE}) |
| 15 | LIST(REMOVE_ITEM _ATEN_SRCS ${ITEM}) | 15 | LIST(REMOVE_ITEM _ATEN_SRCS ${ITEM}) |
| 16 | ENDFOREACH() | 16 | ENDFOREACH() |
| 17 | 17 | ||
| 18 | LIST(APPEND ATEN_SRCS ${_ATEN_SRCS}) | 18 | LIST(APPEND ATEN_SRCS ${_ATEN_SRCS}) |
| 19 | 19 | ||
| 20 | # Pass to parent | 20 | # Pass to parent |
| 21 | set(ATEN_SRCS ${ATEN_SRCS} PARENT_SCOPE) | 21 | set(ATEN_SRCS ${ATEN_SRCS} PARENT_SCOPE) |
| @@ -1,50 +1,50 @@ | |||
| 1 | #include <ATen/ATen.h> | 1 | #include <ATen/ATen.h> |
| 2 | #include <ATen/NativeFunctions.h> | 2 | #include <ATen/NativeFunctions.h> |
| 3 | #include <ATen/TensorUtils.h> | 3 | #include <ATen/TensorUtils.h> |
| 4 | #include <c10/core/Storage.h> | 4 | #include <c10/core/Storage.h> |
| 5 | #include "torch_npu/csrc/core/npu/NPUFunctions.h" | 5 | #include "torch_npu/csrc/core/npu/NPUFunctions.h" |
| 6 | #include "torch_npu/csrc/core/npu/NPUException.h" | 6 | #include "torch_npu/csrc/core/npu/NPUException.h" |
| 7 | #include "torch_npu/csrc/core/npu/CachingHostAllocator.h" | 7 | #include "torch_npu/csrc/core/npu/CachingHostAllocator.h" |
| 8 | 8 | ||
| 9 | #define TORCH_ASSERT_ONLY_METHOD_OPERATORS | 9 | #define TORCH_ASSERT_ONLY_METHOD_OPERATORS |
| 10 | #include <ATen/core/Tensor.h> | 10 | #include <ATen/core/Tensor.h> |
| 11 | #include <ATen/core/dispatch/DispatchKeyExtractor.h> | 11 | #include <ATen/core/dispatch/DispatchKeyExtractor.h> |
| 12 | #include <torch/library.h> | 12 | #include <torch/library.h> |
| 13 | 13 | ||
| 14 | #ifndef AT_PER_OPERATOR_HEADERS | 14 | #ifndef AT_PER_OPERATOR_HEADERS |
| 15 | #include <ATen/Operators.h> | 15 | #include <ATen/Operators.h> |
| 16 | #else | 16 | #else |
| 17 | #include <ATen/ops/is_pinned_ops.h> | 17 | #include <ATen/ops/is_pinned_ops.h> |
| 18 | #include <ATen/ops/_pin_memory_ops.h> | 18 | #include <ATen/ops/_pin_memory_ops.h> |
| 19 | 19 | ||
| 20 | ${ops_headers} | 20 | ${ops_headers} |
| 21 | #endif | 21 | #endif |
| 22 | 22 | ||
| 23 | namespace at_npu { | 23 | namespace at_npu { |
| 24 | namespace native { | 24 | namespace native { |
| 25 | 25 | ||
| 26 | bool is_pinned(const at::Tensor& self, c10::optional<at::Device> device) | 26 | bool is_pinned(const at::Tensor& self, c10::optional<at::Device> device) |
| 27 | { | 27 | { |
| 28 | // Only CPU tensors can be pinned | 28 | // Only CPU tensors can be pinned |
| 29 | if (!self.is_cpu()) { | 29 | if (!self.is_cpu()) { |
| 30 | return false; | 30 | return false; |
| 31 | } | 31 | } |
| 32 | c10::DispatchKeySet _dk = c10::DispatchKeySet(c10::computeDispatchKey(c10::nullopt, self.layout(), device.value_or(c10::DeviceType::PrivateUse1))); | 32 | c10::DispatchKeySet _dk = c10::DispatchKeySet(c10::computeDispatchKey(c10::nullopt, self.layout(), device.value_or(c10::DeviceType::PrivateUse1))); |
| 33 | return at::_ops::is_pinned::redispatch(_dk, self, device); | 33 | return at::_ops::is_pinned::redispatch(_dk, self, device); |
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | at::Tensor _pin_memory(const at::Tensor& self, c10::optional<at::Device> device) | 36 | at::Tensor _pin_memory(const at::Tensor& self, c10::optional<at::Device> device) |
| 37 | { | 37 | { |
| 38 | TORCH_CHECK(self.device().is_cpu(), "cannot pin '", self.toString(), "' only dense CPU tensors can be pinned", PTA_ERROR(ErrCode::TYPE)); | 38 | TORCH_CHECK(self.device().is_cpu(), "cannot pin '", self.toString(), "' only dense CPU tensors can be pinned", PTA_ERROR(ErrCode::TYPE)); |
| 39 | c10::DispatchKeySet _dk = c10::DispatchKeySet(c10::computeDispatchKey(c10::nullopt, self.layout(), device.value_or(c10::DeviceType::PrivateUse1))); | 39 | c10::DispatchKeySet _dk = c10::DispatchKeySet(c10::computeDispatchKey(c10::nullopt, self.layout(), device.value_or(c10::DeviceType::PrivateUse1))); |
| 40 | return at::_ops::_pin_memory::redispatch(_dk, self, device); | 40 | return at::_ops::_pin_memory::redispatch(_dk, self, device); |
| 41 | } | 41 | } |
| 42 | 42 | ||
| 43 | TORCH_LIBRARY_IMPL(aten, BackendSelect, m) | 43 | TORCH_LIBRARY_IMPL(aten, BackendSelect, m) |
| 44 | { | 44 | { |
| 45 | m.impl(TORCH_SELECTIVE_NAME("aten::is_pinned"), TORCH_FN(is_pinned)); | 45 | m.impl(TORCH_SELECTIVE_NAME("aten::is_pinned"), TORCH_FN(is_pinned)); |
| 46 | m.impl(TORCH_SELECTIVE_NAME("aten::_pin_memory"), TORCH_FN(_pin_memory)); | 46 | m.impl(TORCH_SELECTIVE_NAME("aten::_pin_memory"), TORCH_FN(_pin_memory)); |
| 47 | } | 47 | } |
| 48 | 48 | ||
| 49 | } | 49 | } |
| 50 | } | 50 | } |
| @@ -45,4 +45,3 @@ at::Tensor NPUNativeOpApiFunctions::clone(const at::Tensor &src, c10::optional<c | |||
| 45 | 45 | ||
| 46 | } // namespace native | 46 | } // namespace native |
| 47 | } // namespace at_npu | 47 | } // namespace at_npu |
| 48 | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | FILE(GLOB _CORE_SRCS *.cpp npu/*.cpp npu/*/*.cpp) | 1 | FILE(GLOB _CORE_SRCS *.cpp npu/*.cpp npu/*/*.cpp) |
| 2 | 2 | ||
| 3 | LIST(APPEND CORE_SRCS ${_CORE_SRCS}) | 3 | LIST(APPEND CORE_SRCS ${_CORE_SRCS}) |
| 4 | 4 | ||
| 5 | # Pass to parent | 5 | # Pass to parent |
| 6 | set(CORE_SRCS ${CORE_SRCS} PARENT_SCOPE) | 6 | set(CORE_SRCS ${CORE_SRCS} PARENT_SCOPE) |
| @@ -67,4 +67,3 @@ c10::intrusive_ptr<c10::StorageImpl> make_npu_storage_impl( | |||
| 67 | bool resizable); | 67 | bool resizable); |
| 68 | 68 | ||
| 69 | } // namespace torch_npu | 69 | } // namespace torch_npu |
| 70 | |||
| @@ -1,400 +1,400 @@ | |||
| 1 | #pragma once | 1 | #pragma once |
| 2 | 2 | ||
| 3 | #include <unordered_map> | 3 | #include <unordered_map> |
| 4 | 4 | ||
| 5 | namespace c10_npu::acl { | 5 | namespace c10_npu::acl { |
| 6 | 6 | ||
| 7 | class AclErrorCode { | 7 | class AclErrorCode { |
| 8 | public: | 8 | public: |
| 9 | std::unordered_map<int, std::string> error_code_map = { | 9 | std::unordered_map<int, std::string> error_code_map = { |
| 10 | {100000, "Parameter verification failed.\n\ | 10 | {100000, "Parameter verification failed.\n\ |
| 11 | Check whether the input parameter value of the interface is correct."}, | 11 | Check whether the input parameter value of the interface is correct."}, |
| 12 | {100001, "ACL uninitialized.\n\ | 12 | {100001, "ACL uninitialized.\n\ |
| 13 | (1)Check whether the acl.init interface has been invoked for initialization. Ensure that the acl.init \ | 13 | (1)Check whether the acl.init interface has been invoked for initialization. Ensure that the acl.init \ |
| 14 | interface has been invoked before other pyACL interfaces.\n\ | 14 | interface has been invoked before other pyACL interfaces.\n\ |
| 15 | (2)Check whether the initialization interface of the corresponding function has been invoked, for example, \ | 15 | (2)Check whether the initialization interface of the corresponding function has been invoked, for example, \ |
| 16 | the acl.mdl.init_dump interface for initializing Dump and the acl.prof.init interface for initializing Profiling."}, | 16 | the acl.mdl.init_dump interface for initializing Dump and the acl.prof.init interface for initializing Profiling."}, |
| 17 | {100002, "Repeated initialization or repeated loading.\n\ | 17 | {100002, "Repeated initialization or repeated loading.\n\ |
| 18 | Check whether the corresponding interface is repeatedly invoked for initialization or loading."}, | 18 | Check whether the corresponding interface is repeatedly invoked for initialization or loading."}, |
| 19 | {100003, "Invalid file.\n\ | 19 | {100003, "Invalid file.\n\ |
| 20 | Check whether the file exists and can be accessed."}, | 20 | Check whether the file exists and can be accessed."}, |
| 21 | {100004, "Failed to write the file.\n\ | 21 | {100004, "Failed to write the file.\n\ |
| 22 | Check whether the file path exists and whether the file has the write permission."}, | 22 | Check whether the file path exists and whether the file has the write permission."}, |
| 23 | {100005, "Invalid file size.\n\ | 23 | {100005, "Invalid file size.\n\ |
| 24 | Check whether the file size meets the interface requirements."}, | 24 | Check whether the file size meets the interface requirements."}, |
| 25 | {100006, "Failed to parse the file.\n\ | 25 | {100006, "Failed to parse the file.\n\ |
| 26 | Check whether the file content is valid."}, | 26 | Check whether the file content is valid."}, |
| 27 | {100007, "The file is missing parameters.\n\ | 27 | {100007, "The file is missing parameters.\n\ |
| 28 | Check whether the file content is complete."}, | 28 | Check whether the file content is complete."}, |
| 29 | {100008, "Invalid file parameter.\n\ | 29 | {100008, "Invalid file parameter.\n\ |
| 30 | Check whether the parameter values in the file are correct."}, | 30 | Check whether the parameter values in the file are correct."}, |
| 31 | {100009, "Invalid dump configuration.\n\ | 31 | {100009, "Invalid dump configuration.\n\ |
| 32 | Check whether the dump configuration in the acl.init interface configuration file is correct. For details, \ | 32 | Check whether the dump configuration in the acl.init interface configuration file is correct. For details, \ |
| 33 | see 'Preparing Comparison Data > Preparing Offline Model Dump Data Files' in the <Precision Comparison Tool \ | 33 | see 'Preparing Comparison Data > Preparing Offline Model Dump Data Files' in the <Precision Comparison Tool \ |
| 34 | User Guide>."}, | 34 | User Guide>."}, |
| 35 | {100010, "Invalid Profiling configuration.\n\ | 35 | {100010, "Invalid Profiling configuration.\n\ |
| 36 | Check whether the profiling configuration is correct."}, | 36 | Check whether the profiling configuration is correct."}, |
| 37 | {100011, "Invalid model ID.\n\ | 37 | {100011, "Invalid model ID.\n\ |
| 38 | Check whether the model ID is correct and whether the model is correctly loaded."}, | 38 | Check whether the model ID is correct and whether the model is correctly loaded."}, |
| 39 | {100012, "Failed to deserialize the model.\n\ | 39 | {100012, "Failed to deserialize the model.\n\ |
| 40 | The model may not match the current version. Convert the model again by referring to the <ATC Tool User \ | 40 | The model may not match the current version. Convert the model again by referring to the <ATC Tool User \ |
| 41 | Guide>."}, | 41 | Guide>."}, |
| 42 | {100013, "Failed to parse the model.\n\ | 42 | {100013, "Failed to parse the model.\n\ |
| 43 | The model may not match the current version. Convert the model again by referring to the <ATC Tool User \ | 43 | The model may not match the current version. Convert the model again by referring to the <ATC Tool User \ |
| 44 | Guide>."}, | 44 | Guide>."}, |
| 45 | {100014, "Failed to read the model.\n\ | 45 | {100014, "Failed to read the model.\n\ |
| 46 | Check whether the model file exists and can be accessed."}, | 46 | Check whether the model file exists and can be accessed."}, |
| 47 | {100015, "Invalid model size.\n\ | 47 | {100015, "Invalid model size.\n\ |
| 48 | The model file is invalid, Convert the model again by referring to the <ATC Tool User Guide>."}, | 48 | The model file is invalid, Convert the model again by referring to the <ATC Tool User Guide>."}, |
| 49 | {100016, "The model is missing parameters.\n\ | 49 | {100016, "The model is missing parameters.\n\ |
| 50 | The model may not match the current version. Convert the model again by referring to the <ATC Tool User \ | 50 | The model may not match the current version. Convert the model again by referring to the <ATC Tool User \ |
| 51 | Guide>."}, | 51 | Guide>."}, |
| 52 | {100017, "The input for the model does not match.\n\ | 52 | {100017, "The input for the model does not match.\n\ |
| 53 | Check whether the model input is correct."}, | 53 | Check whether the model input is correct."}, |
| 54 | {100018, "The output of the model does not match.\n\ | 54 | {100018, "The output of the model does not match.\n\ |
| 55 | Check whether the output of the model is correct."}, | 55 | Check whether the output of the model is correct."}, |
| 56 | {100019, "Model is not-dynamic.\n\ | 56 | {100019, "Model is not-dynamic.\n\ |
| 57 | Check whether the current model supports dynamic scenarios. If not, convert the model again by referring \ | 57 | Check whether the current model supports dynamic scenarios. If not, convert the model again by referring \ |
| 58 | to the <ATC Tool User Guide>."}, | 58 | to the <ATC Tool User Guide>."}, |
| 59 | {100020, "The type of a single operator does not match.\n\ | 59 | {100020, "The type of a single operator does not match.\n\ |
| 60 | Check whether the operator type is correct."}, | 60 | Check whether the operator type is correct."}, |
| 61 | {100021, "The input of a single operator does not match.\n\ | 61 | {100021, "The input of a single operator does not match.\n\ |
| 62 | Check whether the operator input is correct."}, | 62 | Check whether the operator input is correct."}, |
| 63 | {100022, "The output of a single operator does not match.\n\ | 63 | {100022, "The output of a single operator does not match.\n\ |
| 64 | Check whether the operator output is correct."}, | 64 | Check whether the operator output is correct."}, |
| 65 | {100023, "The attributes of a single operator do not match.\n\ | 65 | {100023, "The attributes of a single operator do not match.\n\ |
| 66 | Check whether the operator attributes are correct."}, | 66 | Check whether the operator attributes are correct."}, |
| 67 | {100024, "Single operator not found.\n\ | 67 | {100024, "Single operator not found.\n\ |
| 68 | Check whether the operator type is supported."}, | 68 | Check whether the operator type is supported."}, |
| 69 | {100025, "Failed to load a single operator.\n\ | 69 | {100025, "Failed to load a single operator.\n\ |
| 70 | The model may not match the current version. Convert the model again by referring to the <ATC Tool User \ | 70 | The model may not match the current version. Convert the model again by referring to the <ATC Tool User \ |
| 71 | Guide>."}, | 71 | Guide>."}, |
| 72 | {100026, "Unsupported data type.\n\ | 72 | {100026, "Unsupported data type.\n\ |
| 73 | Check whether the data type exists or is currently supported."}, | 73 | Check whether the data type exists or is currently supported."}, |
| 74 | {100027, "The format does not match.\n\ | 74 | {100027, "The format does not match.\n\ |
| 75 | Check whether the format is correct."}, | 75 | Check whether the format is correct."}, |
| 76 | {100028, "When the operator interface is compiled in binary selection mode, the operator haven't registered a \ | 76 | {100028, "When the operator interface is compiled in binary selection mode, the operator haven't registered a \ |
| 77 | selector.\n\ | 77 | selector.\n\ |
| 78 | Check whether the acl.op.register_compile_func interface is invoked to register the operator selector."}, | 78 | Check whether the acl.op.register_compile_func interface is invoked to register the operator selector."}, |
| 79 | {100029, "During operator compilation, the operator kernel haven't registered.\n\ | 79 | {100029, "During operator compilation, the operator kernel haven't registered.\n\ |
| 80 | Check whether the acl.op.create_kernel interface is invoked to register the operator kernel."}, | 80 | Check whether the acl.op.create_kernel interface is invoked to register the operator kernel."}, |
| 81 | {100030, "When the operator interface is compiled in binary selection mode, the operator is registered \ | 81 | {100030, "When the operator interface is compiled in binary selection mode, the operator is registered \ |
| 82 | repeatedly.\n\ | 82 | repeatedly.\n\ |
| 83 | Check whether the acl.op.register_compile_func interface is repeatedly invoked to register the operator \ | 83 | Check whether the acl.op.register_compile_func interface is repeatedly invoked to register the operator \ |
| 84 | selector."}, | 84 | selector."}, |
| 85 | {100031, "During operator compilation, the operator kernel is registered repeatedly.\n\ | 85 | {100031, "During operator compilation, the operator kernel is registered repeatedly.\n\ |
| 86 | Check whether the acl.op.create_kernel interface is repeatedly invoked to register the operator kernel."}, | 86 | Check whether the acl.op.create_kernel interface is repeatedly invoked to register the operator kernel."}, |
| 87 | {100032, "Invalid queue ID.\n\ | 87 | {100032, "Invalid queue ID.\n\ |
| 88 | Check whether the queue ID is correct."}, | 88 | Check whether the queue ID is correct."}, |
| 89 | {100033, "Duplicate subscription.\n\ | 89 | {100033, "Duplicate subscription.\n\ |
| 90 | Check whether the acl.rt.subscribe_report interface is invoked repeatedly for the same stream."}, | 90 | Check whether the acl.rt.subscribe_report interface is invoked repeatedly for the same stream."}, |
| 91 | {100034, "The stream is not subscribed.\n\ | 91 | {100034, "The stream is not subscribed.\n\ |
| 92 | Check whether the acl.rt.subscribe_report interface has been invoked.\n\ | 92 | Check whether the acl.rt.subscribe_report interface has been invoked.\n\ |
| 93 | [notice] This return code will be discarded in later versions. Use the 'ACL_ERROR_RT_STREAM_NO_CB_REG' \ | 93 | [notice] This return code will be discarded in later versions. Use the 'ACL_ERROR_RT_STREAM_NO_CB_REG' \ |
| 94 | return code."}, | 94 | return code."}, |
| 95 | {100035, "The thread is not subscribed.\n\ | 95 | {100035, "The thread is not subscribed.\n\ |
| 96 | Check whether the acl.rt.subscribe_report interface has been invoked.\n\ | 96 | Check whether the acl.rt.subscribe_report interface has been invoked.\n\ |
| 97 | [notice] This return code will be discarded in later versions. Use the 'ACL_ERROR_RT_THREAD_SUBSCRIBE' \ | 97 | [notice] This return code will be discarded in later versions. Use the 'ACL_ERROR_RT_THREAD_SUBSCRIBE' \ |
| 98 | return code."}, | 98 | return code."}, |
| 99 | {100036, "Waiting for callback time out.\n\ | 99 | {100036, "Waiting for callback time out.\n\ |
| 100 | (1)Check whether the acl.rt.launch_callback interface has been invoked to deliver the callback task.\n\ | 100 | (1)Check whether the acl.rt.launch_callback interface has been invoked to deliver the callback task.\n\ |
| 101 | (2)Check whether the timeout interval in the acl.rt.process_report interface is proper.\n\ | 101 | (2)Check whether the timeout interval in the acl.rt.process_report interface is proper.\n\ |
| 102 | (3)Check whether the callback task has been processed. If the callback task has been processed but the \ | 102 | (3)Check whether the callback task has been processed. If the callback task has been processed but the \ |
| 103 | acl.rt.process_report interface is still invoked, optimize the code logic.\n\ | 103 | acl.rt.process_report interface is still invoked, optimize the code logic.\n\ |
| 104 | [notice] This return code will be discarded in later versions. Use the 'ACL_ERROR_RT_REPORT_TIMEOUT' \ | 104 | [notice] This return code will be discarded in later versions. Use the 'ACL_ERROR_RT_REPORT_TIMEOUT' \ |
| 105 | return code."}, | 105 | return code."}, |
| 106 | {100037, "Repeated deinitialization.\n\ | 106 | {100037, "Repeated deinitialization.\n\ |
| 107 | Check whether the acl.finalize interface is repeatedly invoked for deinitialization."}, | 107 | Check whether the acl.finalize interface is repeatedly invoked for deinitialization."}, |
| 108 | {100038, "The static AIPP configuration information does not exist.\n\ | 108 | {100038, "The static AIPP configuration information does not exist.\n\ |
| 109 | When invoking the 'acl.mdl.get_first_aipp_info' interface, ensure that the index value is correct.\n\ | 109 | When invoking the 'acl.mdl.get_first_aipp_info' interface, ensure that the index value is correct.\n\ |
| 110 | [notice] This return code will be discarded in later versions. Use the 'ACL_ERROR_GE_AIPP_NOT_EXIST' \ | 110 | [notice] This return code will be discarded in later versions. Use the 'ACL_ERROR_GE_AIPP_NOT_EXIST' \ |
| 111 | return code."}, | 111 | return code."}, |
| 112 | {100039, "The dynamic library path configured before running the application is the path of the \ | 112 | {100039, "The dynamic library path configured before running the application is the path of the \ |
| 113 | compilation stub, not the correct dynamic library path.\n\ | 113 | compilation stub, not the correct dynamic library path.\n\ |
| 114 | Check the configuration of the dynamic library path and ensure that the dynamic library in running mode \ | 114 | Check the configuration of the dynamic library path and ensure that the dynamic library in running mode \ |
| 115 | is used."}, | 115 | is used."}, |
| 116 | {100040, "Group is not set.\n\ | 116 | {100040, "Group is not set.\n\ |
| 117 | Check if the aclrtSetGroup interface has been called."}, | 117 | Check if the aclrtSetGroup interface has been called."}, |
| 118 | {100041, "No corresponding Group is created.\n\ | 118 | {100041, "No corresponding Group is created.\n\ |
| 119 | Check whether the Group ID set during the interface invocation is within the supported range. \ | 119 | Check whether the Group ID set during the interface invocation is within the supported range. \ |
| 120 | The value range of the Group ID is [0, (number of groups -1)]. You can invoke the aclrtGetGroupCount interface to \ | 120 | The value range of the Group ID is [0, (number of groups -1)]. You can invoke the aclrtGetGroupCount interface to \ |
| 121 | obtain the number of groups."}, | 121 | obtain the number of groups."}, |
| 122 | {100042, "A profiling data collection task exists.\n\ | 122 | {100042, "A profiling data collection task exists.\n\ |
| 123 | Check whether multiple profiling configurations are delivered to the same device.\n\ | 123 | Check whether multiple profiling configurations are delivered to the same device.\n\ |
| 124 | For details, see the Profiling pyACL API. (Performance data is collected and flushed to disks through \ | 124 | For details, see the Profiling pyACL API. (Performance data is collected and flushed to disks through \ |
| 125 | the Profiling pyACL API.) The code logic is adjusted based on the interface invoking requirements and interface \ | 125 | the Profiling pyACL API.) The code logic is adjusted based on the interface invoking requirements and interface \ |
| 126 | invoking sequence in."}, | 126 | invoking sequence in."}, |
| 127 | {100043, "The acl.prof.init interface is not used for analysis initialization.\n\ | 127 | {100043, "The acl.prof.init interface is not used for analysis initialization.\n\ |
| 128 | Check the interface invoking sequence for collecting and analyzing data and analyze the pyACL API by \ | 128 | Check the interface invoking sequence for collecting and analyzing data and analyze the pyACL API by \ |
| 129 | referring to.\n\ | 129 | referring to.\n\ |
| 130 | See (Performance data collected and flushed to disks by analyzing the pyACL API) Description in."}, | 130 | See (Performance data collected and flushed to disks by analyzing the pyACL API) Description in."}, |
| 131 | {100044, "A task for obtaining dump data exists.\n\ | 131 | {100044, "A task for obtaining dump data exists.\n\ |
| 132 | Before invoking the 'acl.mdl.init_dump', 'acl.mdl.set_dump', 'acl.mdl.finalize_dump' interfaces to configure \ | 132 | Before invoking the 'acl.mdl.init_dump', 'acl.mdl.set_dump', 'acl.mdl.finalize_dump' interfaces to configure \ |
| 133 | dump information: Check whether the acl.init interface has been invoked to configure the dump information.\n\ | 133 | dump information: Check whether the acl.init interface has been invoked to configure the dump information.\n\ |
| 134 | If yes, adjust the code logic and retain one method to configure the dump information."}, | 134 | If yes, adjust the code logic and retain one method to configure the dump information."}, |
| 135 | {100045, "The acl.mdl.init_dump interface is not used to initialize the dump.\n\ | 135 | {100045, "The acl.mdl.init_dump interface is not used to initialize the dump.\n\ |
| 136 | Check the invoking sequence of the interfaces for obtaining dump data. For details, see the description \ | 136 | Check the invoking sequence of the interfaces for obtaining dump data. For details, see the description \ |
| 137 | of the acl.mdl.init_dump interface."}, | 137 | of the acl.mdl.init_dump interface."}, |
| 138 | {148046, "Subscribe to the same model repeatedly.\n\ | 138 | {148046, "Subscribe to the same model repeatedly.\n\ |
| 139 | Check the API invoking sequence. See (Performance data collected and flushed to disks by analyzing \ | 139 | Check the API invoking sequence. See (Performance data collected and flushed to disks by analyzing \ |
| 140 | the pyACL API) Description in."}, | 140 | the pyACL API) Description in."}, |
| 141 | {148047, "A conflict occurs in invoking the interface for collecting performance data.\n\ | 141 | {148047, "A conflict occurs in invoking the interface for collecting performance data.\n\ |
| 142 | The interfaces for collecting profiling performance data in the two modes cannot be invoked crossly.\n\ | 142 | The interfaces for collecting profiling performance data in the two modes cannot be invoked crossly.\n\ |
| 143 | The 'acl.prof.model_subscribe', 'acl.prof.get_op_*', 'acl.prof.model_un_subscribe' interface cannot be \ | 143 | The 'acl.prof.model_subscribe', 'acl.prof.get_op_*', 'acl.prof.model_un_subscribe' interface cannot be \ |
| 144 | invoked between the 'acl.prof.init' and 'acl.prof.finalize' interfaces.\n\ | 144 | invoked between the 'acl.prof.init' and 'acl.prof.finalize' interfaces.\n\ |
| 145 | The 'acl.prof.init', 'acl.prof.start', 'acl.prof.stop', and 'acl.prof.finalize' interfaces cannot be \ | 145 | The 'acl.prof.init', 'acl.prof.start', 'acl.prof.stop', and 'acl.prof.finalize' interfaces cannot be \ |
| 146 | invoked between the 'acl.prof.model_subscribe' and 'acl.prof.model_un_subscribe' interfaces."}, | 146 | invoked between the 'acl.prof.model_subscribe' and 'acl.prof.model_un_subscribe' interfaces."}, |
| 147 | {148048, "Invalid operator cache information aging configuration.\n\ | 147 | {148048, "Invalid operator cache information aging configuration.\n\ |
| 148 | Check the aging configuration of the operator cache information. For details, see the configuration \ | 148 | Check the aging configuration of the operator cache information. For details, see the configuration \ |
| 149 | description and example in acl.init."}, | 149 | description and example in acl.init."}, |
| 150 | {148049, "The 'ASCEND_OPP_PATH' environment variable is not set, or the value of the environment variable \ | 150 | {148049, "The 'ASCEND_OPP_PATH' environment variable is not set, or the value of the environment variable \ |
| 151 | is incorrect.\n\ | 151 | is incorrect.\n\ |
| 152 | Check whether the 'ASCEND_OPP_PATH' environment variable is set and whether the value of the environment \ | 152 | Check whether the 'ASCEND_OPP_PATH' environment variable is set and whether the value of the environment \ |
| 153 | variable is the installation path of the opp software package."}, | 153 | variable is the installation path of the opp software package."}, |
| 154 | {148050, "The operator does not support dynamic Shape.\n\ | 154 | {148050, "The operator does not support dynamic Shape.\n\ |
| 155 | Check whether the shape of the operator in the single-operator model file is dynamic. If yes, change the \ | 155 | Check whether the shape of the operator in the single-operator model file is dynamic. If yes, change the \ |
| 156 | shape to a fixed one.\n\ | 156 | shape to a fixed one.\n\ |
| 157 | Check whether the shape of aclTensorDesc is dynamic during operator compilation. If yes, create \ | 157 | Check whether the shape of aclTensorDesc is dynamic during operator compilation. If yes, create \ |
| 158 | aclTensorDesc based on the fixed shape."}, | 158 | aclTensorDesc based on the fixed shape."}, |
| 159 | {148051, "Related resources have not been released.\n\ | 159 | {148051, "Related resources have not been released.\n\ |
| 160 | This error code is returned if the related channel is not destroyed when the channel description \ | 160 | This error code is returned if the related channel is not destroyed when the channel description \ |
| 161 | information is being destroyed. Check whether the channel associated with the channel description is destroyed."}, | 161 | information is being destroyed. Check whether the channel associated with the channel description is destroyed."}, |
| 162 | {148052, "Input image encoding format (such as arithmetic encoding and progressive encoding) that is not \ | 162 | {148052, "Input image encoding format (such as arithmetic encoding and progressive encoding) that is not \ |
| 163 | supported by the JPEGD function.\n\ | 163 | supported by the JPEGD function.\n\ |
| 164 | When JPEGD image decoding is implemented, only Huffman encoding is supported.\n\ | 164 | When JPEGD image decoding is implemented, only Huffman encoding is supported.\n\ |
| 165 | The color space of the original image before compression is YUV. The ratio of pixel components is \n\ | 165 | The color space of the original image before compression is YUV. The ratio of pixel components is \n\ |
| 166 | 4:4:4, 4:2:2, 4:2:0, 4:0:0, or 4:4:0.\n\ | 166 | 4:4:4, 4:2:2, 4:2:0, 4:0:0, or 4:4:0.\n\ |
| 167 | Arithmetic coding, progressive JPEG, and JPEG2000 are not supported."}, | 167 | Arithmetic coding, progressive JPEG, and JPEG2000 are not supported."}, |
| 168 | {200000, "Failed to apply for memory.\n\ | 168 | {200000, "Failed to apply for memory.\n\ |
| 169 | Check the available memory in the hardware environment."}, | 169 | Check the available memory in the hardware environment."}, |
| 170 | {200001, "The interface does not support this function.\n\ | 170 | {200001, "The interface does not support this function.\n\ |
| 171 | Check whether the invoked interface is supported."}, | 171 | Check whether the invoked interface is supported."}, |
| 172 | {200002, "Invalid device.\n\ | 172 | {200002, "Invalid device.\n\ |
| 173 | Check whether the device exists.\n\ | 173 | Check whether the device exists.\n\ |
| 174 | [notice] This return code will be discarded in later versions. Use the 'ACL_ERROR_RT_INVALID_DEVICEID' \ | 174 | [notice] This return code will be discarded in later versions. Use the 'ACL_ERROR_RT_INVALID_DEVICEID' \ |
| 175 | return code."}, | 175 | return code."}, |
| 176 | {200003, "The memory address is not aligned.\n\ | 176 | {200003, "The memory address is not aligned.\n\ |
| 177 | Check whether the memory address meets the interface requirements."}, | 177 | Check whether the memory address meets the interface requirements."}, |
| 178 | {200004, "The resources do not match.\n\ | 178 | {200004, "The resources do not match.\n\ |
| 179 | Check whether the correct resources such as Stream and Context are transferred when the interface is \ | 179 | Check whether the correct resources such as Stream and Context are transferred when the interface is \ |
| 180 | invoked."}, | 180 | invoked."}, |
| 181 | {200005, "Invalid resource handle.\n\ | 181 | {200005, "Invalid resource handle.\n\ |
| 182 | Check whether the transferred resources such as Stream and Context are destroyed or occupied when the \ | 182 | Check whether the transferred resources such as Stream and Context are destroyed or occupied when the \ |
| 183 | interface is invoked."}, | 183 | interface is invoked."}, |
| 184 | {200006, "This feature is not supported.\n\ | 184 | {200006, "This feature is not supported.\n\ |
| 185 | Rectify the fault based on the error information in the ascend log or contact Huawei technical support. For \ | 185 | Rectify the fault based on the error information in the ascend log or contact Huawei technical support. For \ |
| 186 | details about logs, see the Log Reference."}, | 186 | details about logs, see the Log Reference."}, |
| 187 | {200007, "Unsupported profiling configurations are delivered.\n\ | 187 | {200007, "Unsupported profiling configurations are delivered.\n\ |
| 188 | Check whether the profiling configuration is correct by referring to the description in \ | 188 | Check whether the profiling configuration is correct by referring to the description in \ |
| 189 | 'acl.prof.create_config'."}, | 189 | 'acl.prof.create_config'."}, |
| 190 | {300000, "The storage limit is exceeded.\n\ | 190 | {300000, "The storage limit is exceeded.\n\ |
| 191 | Check the remaining storage space in the hardware environment."}, | 191 | Check the remaining storage space in the hardware environment."}, |
| 192 | {500000, "Unknown internal error.\n\ | 192 | {500000, "Unknown internal error.\n\ |
| 193 | Rectify the fault based on the error information in the ascend log."}, | 193 | Rectify the fault based on the error information in the ascend log."}, |
| 194 | {500001, "The internal ACL of the system is incorrect.\n\ | 194 | {500001, "The internal ACL of the system is incorrect.\n\ |
| 195 | Rectify the fault based on the error information in the ascend log."}, | 195 | Rectify the fault based on the error information in the ascend log."}, |
| 196 | {500002, "A GE error occurs in the system.\n\ | 196 | {500002, "A GE error occurs in the system.\n\ |
| 197 | Rectify the fault based on the error information in the ascend log."}, | 197 | Rectify the fault based on the error information in the ascend log."}, |
| 198 | {500003, "The internal RUNTIME of the system is incorrect.\n\ | 198 | {500003, "The internal RUNTIME of the system is incorrect.\n\ |
| 199 | Rectify the fault based on the error information in the ascend log."}, | 199 | Rectify the fault based on the error information in the ascend log."}, |
| 200 | {500004, "An internal DRV (Driver) error occurs.\n\ | 200 | {500004, "An internal DRV (Driver) error occurs.\n\ |
| 201 | Rectify the fault based on the error information in the ascend log."}, | 201 | Rectify the fault based on the error information in the ascend log."}, |
| 202 | {500005, "Profiling error.\n\ | 202 | {500005, "Profiling error.\n\ |
| 203 | Rectify the fault based on the error information in the ascend log."}, | 203 | Rectify the fault based on the error information in the ascend log."}, |
| 204 | /* following return codes is for the internal RUNTIME */ | 204 | /* following return codes is for the internal RUNTIME */ |
| 205 | {107000, "Parameter verification failed.\n\ | 205 | {107000, "Parameter verification failed.\n\ |
| 206 | Check whether the input parameters of the interface are correct."}, | 206 | Check whether the input parameters of the interface are correct."}, |
| 207 | {107001, "Invalid device ID.\n\ | 207 | {107001, "Invalid device ID.\n\ |
| 208 | Check whether the device ID is valid."}, | 208 | Check whether the device ID is valid."}, |
| 209 | /* Warning: key logs in the fault mode library!!! Don't make arbitrary modifications!!! */ | 209 | /* Warning: key logs in the fault mode library!!! Don't make arbitrary modifications!!! */ |
| 210 | {107002, "The context is empty.\n\ | 210 | {107002, "The context is empty.\n\ |
| 211 | Check whether acl.rt.set_context or acl.rt.set_device is called."}, | 211 | Check whether acl.rt.set_context or acl.rt.set_device is called."}, |
| 212 | {107003, "The stream is not in the current context.\n\ | 212 | {107003, "The stream is not in the current context.\n\ |
| 213 | Check whether the context where the stream is located is the same as the current context."}, | 213 | Check whether the context where the stream is located is the same as the current context."}, |
| 214 | {107004, "The model is not in the current context.\n\ | 214 | {107004, "The model is not in the current context.\n\ |
| 215 | Check whether the loaded model is consistent with the current context."}, | 215 | Check whether the loaded model is consistent with the current context."}, |
| 216 | {107005, "The stream is not in the current model.\n\ | 216 | {107005, "The stream is not in the current model.\n\ |
| 217 | Check whether the stream has been bound to the model."}, | 217 | Check whether the stream has been bound to the model."}, |
| 218 | {107006, "The event timestamp is invalid.\n\ | 218 | {107006, "The event timestamp is invalid.\n\ |
| 219 | Check whether the event is created."}, | 219 | Check whether the event is created."}, |
| 220 | {107007, "Invert the event timestamp.\n\ | 220 | {107007, "Invert the event timestamp.\n\ |
| 221 | Check whether the event is created."}, | 221 | Check whether the event is created."}, |
| 222 | {107008, "The memory address is not aligned.\n\ | 222 | {107008, "The memory address is not aligned.\n\ |
| 223 | Check whether the applied memory addresses are aligned. For details about the restrictions on the memory \ | 223 | Check whether the applied memory addresses are aligned. For details about the restrictions on the memory \ |
| 224 | application interface, see Memory Management."}, | 224 | application interface, see Memory Management."}, |
| 225 | {107009, "Failed to open the file.\n\ | 225 | {107009, "Failed to open the file.\n\ |
| 226 | Check whether the file exists."}, | 226 | Check whether the file exists."}, |
| 227 | {107010, "Failed to write the file.\n\ | 227 | {107010, "Failed to write the file.\n\ |
| 228 | Check whether the file exists or has the write permission."}, | 228 | Check whether the file exists or has the write permission."}, |
| 229 | {107011, "The stream is not subscribed to or subscribed to repeatedly.\n\ | 229 | {107011, "The stream is not subscribed to or subscribed to repeatedly.\n\ |
| 230 | Check whether the current stream is subscribed to or repeatedly subscribed to."}, | 230 | Check whether the current stream is subscribed to or repeatedly subscribed to."}, |
| 231 | {107012, "The thread is not subscribed or subscribed repeatedly.\n\ | 231 | {107012, "The thread is not subscribed or subscribed repeatedly.\n\ |
| 232 | Check whether the current thread subscribes to or subscribes to the thread repeatedly."}, | 232 | Check whether the current thread subscribes to or subscribes to the thread repeatedly."}, |
| 233 | {107013, "The group is not set."}, | 233 | {107013, "The group is not set."}, |
| 234 | {107014, "The corresponding group is not created.\n\ | 234 | {107014, "The corresponding group is not created.\n\ |
| 235 | Check whether the group ID set when the interface is invoked is within the supported range. The value \ | 235 | Check whether the group ID set when the interface is invoked is within the supported range. The value \ |
| 236 | range of the group ID is [0, (Number of groups - 1)]."}, | 236 | range of the group ID is [0, (Number of groups - 1)]."}, |
| 237 | {107015, "The stream corresponding to the callback is not registered with the thread.\n\ | 237 | {107015, "The stream corresponding to the callback is not registered with the thread.\n\ |
| 238 | Check whether the stream has been registered with the thread and whether the acl.rt.subscribe_report \ | 238 | Check whether the stream has been registered with the thread and whether the acl.rt.subscribe_report \ |
| 239 | interface is invoked."}, | 239 | interface is invoked."}, |
| 240 | {107016, "Invalid memory type.\n\ | 240 | {107016, "Invalid memory type.\n\ |
| 241 | Check whether the memory type is valid."}, | 241 | Check whether the memory type is valid."}, |
| 242 | {107017, "Invalid resource handle.\n\ | 242 | {107017, "Invalid resource handle.\n\ |
| 243 | Check whether the input and used parameters are correct."}, | 243 | Check whether the input and used parameters are correct."}, |
| 244 | {107018, "The memory type applied for is incorrect.\n\ | 244 | {107018, "The memory type applied for is incorrect.\n\ |
| 245 | Check whether the input and used memory types are correct."}, | 245 | Check whether the input and used memory types are correct."}, |
| 246 | {107019, "Task execution timed out.\n\ | 246 | {107019, "Task execution timed out.\n\ |
| 247 | Re-execute the interface for delivering the task."}, | 247 | Re-execute the interface for delivering the task."}, |
| 248 | {207000, "This feature is not supported.\n\ | 248 | {207000, "This feature is not supported.\n\ |
| 249 | Rectify the fault based on the error information in the ascend log."}, | 249 | Rectify the fault based on the error information in the ascend log."}, |
| 250 | {207001, "Failed to apply for memory.\n\ | 250 | {207001, "Failed to apply for memory.\n\ |
| 251 | Check the remaining storage space in the hardware environment."}, | 251 | Check the remaining storage space in the hardware environment."}, |
| 252 | {207002, "Failed to release the memory.\n\ | 252 | {207002, "Failed to release the memory.\n\ |
| 253 | Rectify the fault based on the error information in the ascend log."}, | 253 | Rectify the fault based on the error information in the ascend log."}, |
| 254 | {207003, "The operation of the aicore operator overflows.\n\ | 254 | {207003, "The operation of the aicore operator overflows.\n\ |
| 255 | Check whether the corresponding aicore operator operation overflows."}, | 255 | Check whether the corresponding aicore operator operation overflows."}, |
| 256 | {207004, "The device is unavailable.\n\ | 256 | {207004, "The device is unavailable.\n\ |
| 257 | Check whether the device is running properly."}, | 257 | Check whether the device is running properly."}, |
| 258 | {207005, "Failed to apply for memory.\n\ | 258 | {207005, "Failed to apply for memory.\n\ |
| 259 | Check the remaining storage space in the hardware environment."}, | 259 | Check the remaining storage space in the hardware environment."}, |
| 260 | {207006, "You do not have the operation permission.\n\ | 260 | {207006, "You do not have the operation permission.\n\ |
| 261 | Check whether the permission of the user who runs the application is correct."}, | 261 | Check whether the permission of the user who runs the application is correct."}, |
| 262 | {207007, "Event resources are insufficient.\n\ | 262 | {207007, "Event resources are insufficient.\n\ |
| 263 | Check whether the number of events meets the requirements by referring to the description of the \ | 263 | Check whether the number of events meets the requirements by referring to the description of the \ |
| 264 | acl.rt.create_event interface."}, | 264 | acl.rt.create_event interface."}, |
| 265 | {207008, "Stream resources are insufficient.\n\ | 265 | {207008, "Stream resources are insufficient.\n\ |
| 266 | Check whether the number of streams meets the requirements by referring to the description of the \ | 266 | Check whether the number of streams meets the requirements by referring to the description of the \ |
| 267 | acl.rt.create_stream interface."}, | 267 | acl.rt.create_stream interface."}, |
| 268 | {207009, "Notify resources in the system are insufficient.\n\ | 268 | {207009, "Notify resources in the system are insufficient.\n\ |
| 269 | There are too many concurrent data preprocessing tasks or model inference consumes too many resources. \ | 269 | There are too many concurrent data preprocessing tasks or model inference consumes too many resources. \ |
| 270 | You are advised to reduce the number of concurrent tasks or uninstall some models."}, | 270 | You are advised to reduce the number of concurrent tasks or uninstall some models."}, |
| 271 | {207010, "Insufficient model resources.\n\ | 271 | {207010, "Insufficient model resources.\n\ |
| 272 | You are advised to uninstall some models."}, | 272 | You are advised to uninstall some models."}, |
| 273 | {207011, "Runtime internal resources are insufficient.\n\ | 273 | {207011, "Runtime internal resources are insufficient.\n\ |
| 274 | Rectify the fault based on the error information in the ascend log."}, | 274 | Rectify the fault based on the error information in the ascend log."}, |
| 275 | {207012, "The number of queues exceeds the upper limit.\n\ | 275 | {207012, "The number of queues exceeds the upper limit.\n\ |
| 276 | Destroy unnecessary queues before creating new queues."}, | 276 | Destroy unnecessary queues before creating new queues."}, |
| 277 | {207013, "The queue is empty.\n\ | 277 | {207013, "The queue is empty.\n\ |
| 278 | Cannot obtain data from an empty queue. Add data to the queue and then obtain data."}, | 278 | Cannot obtain data from an empty queue. Add data to the queue and then obtain data."}, |
| 279 | {207014, "The queue is full. \n\ | 279 | {207014, "The queue is full. \n\ |
| 280 | You cannot add data to a queue that is full. Obtain data from the queue and then add data."}, | 280 | You cannot add data to a queue that is full. Obtain data from the queue and then add data."}, |
| 281 | {207015, "The queue is initialized repeatedly. \n\ | 281 | {207015, "The queue is initialized repeatedly. \n\ |
| 282 | You are advised to initialize the queue only once."}, | 282 | You are advised to initialize the queue only once."}, |
| 283 | {207018, "The memory on the device is exhausted. \n\ | 283 | {207018, "The memory on the device is exhausted. \n\ |
| 284 | Check the memory usage on the device and properly plan the memory usage based on the memory specifications \ | 284 | Check the memory usage on the device and properly plan the memory usage based on the memory specifications \ |
| 285 | on the device."}, | 285 | on the device."}, |
| 286 | {507000, "An internal error occurs in the runtime module on the host. \n\ | 286 | {507000, "An internal error occurs in the runtime module on the host. \n\ |
| 287 | Rectify the fault based on the error information in the ascend log."}, | 287 | Rectify the fault based on the error information in the ascend log."}, |
| 288 | {507001, "An internal error occurs in the task scheduler module on the device. \n\ | 288 | {507001, "An internal error occurs in the task scheduler module on the device. \n\ |
| 289 | Rectify the fault based on the error information in the ascend log."}, | 289 | Rectify the fault based on the error information in the ascend log."}, |
| 290 | {507002, "The number of tasks on the stream reaches the maximum. \n\ | 290 | {507002, "The number of tasks on the stream reaches the maximum. \n\ |
| 291 | Rectify the fault based on the error information in the ascend log."}, | 291 | Rectify the fault based on the error information in the ascend log."}, |
| 292 | {507003, "The number of tasks on the stream is empty. \n\ | 292 | {507003, "The number of tasks on the stream is empty. \n\ |
| 293 | Rectify the fault based on the error information in the ascend log."}, | 293 | Rectify the fault based on the error information in the ascend log."}, |
| 294 | {507004, "Not all tasks on the stream are executed. \n\ | 294 | {507004, "Not all tasks on the stream are executed. \n\ |
| 295 | Rectify the fault based on the error information in the ascend log."}, | 295 | Rectify the fault based on the error information in the ascend log."}, |
| 296 | {507005, "Task execution on the AI CPU is complete. \n\ | 296 | {507005, "Task execution on the AI CPU is complete. \n\ |
| 297 | Rectify the fault based on the error information in the ascend log."}, | 297 | Rectify the fault based on the error information in the ascend log."}, |
| 298 | {507006, "The event is not complete. \n\ | 298 | {507006, "The event is not complete. \n\ |
| 299 | Rectify the fault based on the error information in the ascend log."}, | 299 | Rectify the fault based on the error information in the ascend log."}, |
| 300 | {507007, "Failed to release the context. \n\ | 300 | {507007, "Failed to release the context. \n\ |
| 301 | Rectify the fault based on the error information in the ascend log."}, | 301 | Rectify the fault based on the error information in the ascend log."}, |
| 302 | {507008, "Failed to obtain the SOC version. \n\ | 302 | {507008, "Failed to obtain the SOC version. \n\ |
| 303 | Rectify the fault based on the error information in the ascend log."}, | 303 | Rectify the fault based on the error information in the ascend log."}, |
| 304 | {507009, "The task type is not supported. \n\ | 304 | {507009, "The task type is not supported. \n\ |
| 305 | Rectify the fault based on the error information in the ascend log."}, | 305 | Rectify the fault based on the error information in the ascend log."}, |
| 306 | {507010, "The task scheduler loses the heartbeat. \n\ | 306 | {507010, "The task scheduler loses the heartbeat. \n\ |
| 307 | Rectify the fault based on the error information in the ascend log."}, | 307 | Rectify the fault based on the error information in the ascend log."}, |
| 308 | {507011, "Model execution failed. \n\ | 308 | {507011, "Model execution failed. \n\ |
| 309 | Rectify the fault based on the error information in the ascend log."}, | 309 | Rectify the fault based on the error information in the ascend log."}, |
| 310 | {507012, "Failed to obtain the task scheduler message. \n\ | 310 | {507012, "Failed to obtain the task scheduler message. \n\ |
| 311 | Rectify the fault based on the error information in the ascend log."}, | 311 | Rectify the fault based on the error information in the ascend log."}, |
| 312 | {507013, "System Direct Memory Access (DMA) hardware execution error. \n\ | 312 | {507013, "System Direct Memory Access (DMA) hardware execution error. \n\ |
| 313 | Rectify the fault based on the error information in the ascend log."}, | 313 | Rectify the fault based on the error information in the ascend log."}, |
| 314 | {507014, "The aicore execution times out. \n\ | 314 | {507014, "The aicore execution times out. \n\ |
| 315 | Rectify the fault based on the error information in the ascend log."}, | 315 | Rectify the fault based on the error information in the ascend log."}, |
| 316 | {507015, "The aicore execution is abnormal. \n\ | 316 | {507015, "The aicore execution is abnormal. \n\ |
| 317 | Rectify the fault based on the error information in the ascend log."}, | 317 | Rectify the fault based on the error information in the ascend log."}, |
| 318 | {507016, "An exception occurs when the aicore trap is executed. \n\ | 318 | {507016, "An exception occurs when the aicore trap is executed. \n\ |
| 319 | Rectify the fault based on the error information in the ascend log."}, | 319 | Rectify the fault based on the error information in the ascend log."}, |
| 320 | {507017, "The aicpu execution times out. \n\ | 320 | {507017, "The aicpu execution times out. \n\ |
| 321 | Rectify the fault based on the error information in the ascend log."}, | 321 | Rectify the fault based on the error information in the ascend log."}, |
| 322 | {507018, "The aicpu execution is abnormal. \n\ | 322 | {507018, "The aicpu execution is abnormal. \n\ |
| 323 | Rectify the fault based on the error information in the ascend log."}, | 323 | Rectify the fault based on the error information in the ascend log."}, |
| 324 | {507019, "The AICPU does not send a response to the task scheduler after data dump. \n\ | 324 | {507019, "The AICPU does not send a response to the task scheduler after data dump. \n\ |
| 325 | Rectify the fault based on the error information in the ascend log."}, | 325 | Rectify the fault based on the error information in the ascend log."}, |
| 326 | {507020, "The AIPPU does not send a response to the task scheduler after executing the model. \n\ | 326 | {507020, "The AIPPU does not send a response to the task scheduler after executing the model. \n\ |
| 327 | Rectify the fault based on the error information in the ascend log."}, | 327 | Rectify the fault based on the error information in the ascend log."}, |
| 328 | {507021, "The profiling function is abnormal. \n\ | 328 | {507021, "The profiling function is abnormal. \n\ |
| 329 | Rectify the fault based on the error information in the ascend log."}, | 329 | Rectify the fault based on the error information in the ascend log."}, |
| 330 | {507022, "The communication between processes is abnormal. \n\ | 330 | {507022, "The communication between processes is abnormal. \n\ |
| 331 | Rectify the fault based on the error information in the ascend log."}, | 331 | Rectify the fault based on the error information in the ascend log."}, |
| 332 | {507023, "The model exits. \n\ | 332 | {507023, "The model exits. \n\ |
| 333 | Rectify the fault based on the error information in the ascend log."}, | 333 | Rectify the fault based on the error information in the ascend log."}, |
| 334 | {507024, "The operator is being deregistered. \n\ | 334 | {507024, "The operator is being deregistered. \n\ |
| 335 | Rectify the fault based on the error information in the ascend log."}, | 335 | Rectify the fault based on the error information in the ascend log."}, |
| 336 | {507025, "The ring buffer function is not initialized. \n\ | 336 | {507025, "The ring buffer function is not initialized. \n\ |
| 337 | Rectify the fault based on the error information in the ascend log."}, | 337 | Rectify the fault based on the error information in the ascend log."}, |
| 338 | {507026, "The ring buffer has no data. \n\ | 338 | {507026, "The ring buffer has no data. \n\ |
| 339 | Rectify the fault based on the error information in the ascend log."}, | 339 | Rectify the fault based on the error information in the ascend log."}, |
| 340 | {507027, "The kernel in RUNTIME is not registered. \n\ | 340 | {507027, "The kernel in RUNTIME is not registered. \n\ |
| 341 | Rectify the fault based on the error information in the ascend log."}, | 341 | Rectify the fault based on the error information in the ascend log."}, |
| 342 | {507028, "Repeatedly register the kernel inside the RUNTIME. \n\ | 342 | {507028, "Repeatedly register the kernel inside the RUNTIME. \n\ |
| 343 | Rectify the fault based on the error information in the ascend log."}, | 343 | Rectify the fault based on the error information in the ascend log."}, |
| 344 | {507029, "The debug function failed to be registered. \n\ | 344 | {507029, "The debug function failed to be registered. \n\ |
| 345 | Rectify the fault based on the error information in the ascend log."}, | 345 | Rectify the fault based on the error information in the ascend log."}, |
| 346 | {507030, "Deregistration of the debugging function fails. \n\ | 346 | {507030, "Deregistration of the debugging function fails. \n\ |
| 347 | Rectify the fault based on the error information in the ascend log."}, | 347 | Rectify the fault based on the error information in the ascend log."}, |
| 348 | {507031, "The tag is not in the current context. \n\ | 348 | {507031, "The tag is not in the current context. \n\ |
| 349 | Rectify the fault based on the error information in the ascend log."}, | 349 | Rectify the fault based on the error information in the ascend log."}, |
| 350 | {507032, "The number of registered programs exceeds the upper limit. \n\ | 350 | {507032, "The number of registered programs exceeds the upper limit. \n\ |
| 351 | Rectify the fault based on the error information in the ascend log."}, | 351 | Rectify the fault based on the error information in the ascend log."}, |
| 352 | {507033, "Failed to start the device. \n\ | 352 | {507033, "Failed to start the device. \n\ |
| 353 | Rectify the fault based on the error information in the ascend log."}, | 353 | Rectify the fault based on the error information in the ascend log."}, |
| 354 | {507034, "Vector core execution timed out. \n\ | 354 | {507034, "Vector core execution timed out. \n\ |
| 355 | Rectify the fault based on the error information in the ascend log."}, | 355 | Rectify the fault based on the error information in the ascend log."}, |
| 356 | {507035, "The vector core execution is abnormal. \n\ | 356 | {507035, "The vector core execution is abnormal. \n\ |
| 357 | Rectify the fault based on the error information in the ascend log."}, | 357 | Rectify the fault based on the error information in the ascend log."}, |
| 358 | {507036, "An exception occurs when vector core traps are executed. \n\ | 358 | {507036, "An exception occurs when vector core traps are executed. \n\ |
| 359 | Rectify the fault based on the error information in the ascend log."}, | 359 | Rectify the fault based on the error information in the ascend log."}, |
| 360 | {507037, "An exception occurred when applying for internal resources of the Runtime. \n\ | 360 | {507037, "An exception occurred when applying for internal resources of the Runtime. \n\ |
| 361 | Rectify the fault based on the error information in the ascend log."}, | 361 | Rectify the fault based on the error information in the ascend log."}, |
| 362 | {507038, "An error occurred when modifying the die mode, can not change the die mode. \n\ | 362 | {507038, "An error occurred when modifying the die mode, can not change the die mode. \n\ |
| 363 | Rectify the fault based on the error information in the ascend log."}, | 363 | Rectify the fault based on the error information in the ascend log."}, |
| 364 | {507039, "The die cannot be specified in single-die mode. \n\ | 364 | {507039, "The die cannot be specified in single-die mode. \n\ |
| 365 | Rectify the fault based on the error information in the ascend log."}, | 365 | Rectify the fault based on the error information in the ascend log."}, |
| 366 | {507040, "The specified die ID is incorrect. \n\ | 366 | {507040, "The specified die ID is incorrect. \n\ |
| 367 | Rectify the fault based on the error information in the ascend log."}, | 367 | Rectify the fault based on the error information in the ascend log."}, |
| 368 | {507041, "The die mode is not set. \n\ | 368 | {507041, "The die mode is not set. \n\ |
| 369 | Rectify the fault based on the error information in the ascend log."}, | 369 | Rectify the fault based on the error information in the ascend log."}, |
| 370 | {507042, "The aicore trap read out-of-bounds exception. \n\ | 370 | {507042, "The aicore trap read out-of-bounds exception. \n\ |
| 371 | Rectify the fault based on the error information in the ascend log."}, | 371 | Rectify the fault based on the error information in the ascend log."}, |
| 372 | {507043, "The aicore trap write out-of-bounds exception. \n\ | 372 | {507043, "The aicore trap write out-of-bounds exception. \n\ |
| 373 | Rectify the fault based on the error information in the ascend log."}, | 373 | Rectify the fault based on the error information in the ascend log."}, |
| 374 | {507044, "Vector core trap read out-of-bounds exception. \n\ | 374 | {507044, "Vector core trap read out-of-bounds exception. \n\ |
| 375 | Rectify the fault based on the error information in the ascend log."}, | 375 | Rectify the fault based on the error information in the ascend log."}, |
| 376 | {507045, "Vector core trap write out-of-bounds exception. \n\ | 376 | {507045, "Vector core trap write out-of-bounds exception. \n\ |
| 377 | Rectify the fault based on the error information in the ascend log."}, | 377 | Rectify the fault based on the error information in the ascend log."}, |
| 378 | {507046, "In the specified timeout waiting event, all tasks in the specified stream are not completed. \n\ | 378 | {507046, "In the specified timeout waiting event, all tasks in the specified stream are not completed. \n\ |
| 379 | Rectify the fault based on the error information in the ascend log."}, | 379 | Rectify the fault based on the error information in the ascend log."}, |
| 380 | {507047, "During the specified event synchronization waiting time, the event is not executed completely. \n\ | 380 | {507047, "During the specified event synchronization waiting time, the event is not executed completely. \n\ |
| 381 | Rectify the fault based on the error information in the ascend log."}, | 381 | Rectify the fault based on the error information in the ascend log."}, |
| 382 | {507048, "The execution of the internal task times out. \n\ | 382 | {507048, "The execution of the internal task times out. \n\ |
| 383 | Rectify the fault based on the error information in the ascend log."}, | 383 | Rectify the fault based on the error information in the ascend log."}, |
| 384 | {507049, "An exception occurs during the execution of an internal task. \n\ | 384 | {507049, "An exception occurs during the execution of an internal task. \n\ |
| 385 | Rectify the fault based on the error information in the ascend log."}, | 385 | Rectify the fault based on the error information in the ascend log."}, |
| 386 | {507050, "The trap of the internal task is abnormal. \n\ | 386 | {507050, "The trap of the internal task is abnormal. \n\ |
| 387 | Rectify the fault based on the error information in the ascend log."}, | 387 | Rectify the fault based on the error information in the ascend log."}, |
| 388 | {507051, "Messages fail to be sent during data enqueuing. \n\ | 388 | {507051, "Messages fail to be sent during data enqueuing. \n\ |
| 389 | Rectify the fault based on the error information in the ascend log."}, | 389 | Rectify the fault based on the error information in the ascend log."}, |
| 390 | {507052, "Memory copy fails during data enqueuing. \n\ | 390 | {507052, "Memory copy fails during data enqueuing. \n\ |
| 391 | Rectify the fault based on the error information in the ascend log."}, | 391 | Rectify the fault based on the error information in the ascend log."}, |
| 392 | {507899, "An internal error occurs in the Driver module. \n\ | 392 | {507899, "An internal error occurs in the Driver module. \n\ |
| 393 | Rectify the fault based on the error information in the ascend log."}, | 393 | Rectify the fault based on the error information in the ascend log."}, |
| 394 | {507900, "An internal error occurs on the AI CPU module. \n\ | 394 | {507900, "An internal error occurs on the AI CPU module. \n\ |
| 395 | Rectify the fault based on the error information in the ascend log."}, | 395 | Rectify the fault based on the error information in the ascend log."}, |
| 396 | {507901, "The internal host device communication (HDC) session is disconnected. \n\ | 396 | {507901, "The internal host device communication (HDC) session is disconnected. \n\ |
| 397 | Rectify the fault based on the error information in the ascend log."}, | 397 | Rectify the fault based on the error information in the ascend log."}, |
| 398 | }; /* aclError code */ | 398 | }; /* aclError code */ |
| 399 | }; | 399 | }; |
| 400 | } /* c10_npu::acl */ | 400 | } /* c10_npu::acl */ |
| @@ -115,4 +115,3 @@ bool IsAclnnOnly() | |||
| 115 | return GetSocVersion() >= SocVersion::Ascend950; | 115 | return GetSocVersion() >= SocVersion::Ascend950; |
| 116 | } | 116 | } |
| 117 | } // namespace c10_npu | 117 | } // namespace c10_npu |
| 118 | |||
| @@ -45,4 +45,3 @@ bool IsBF16Supported(); | |||
| 45 | bool IsAclnnOnly(); | 45 | bool IsAclnnOnly(); |
| 46 | } // namespace c10_npu | 46 | } // namespace c10_npu |
| 47 | 47 | ||
| 48 | |||
| @@ -1,12 +1,12 @@ | |||
| 1 | if (DEFINED BUILD_LIBTORCH) | 1 | if (DEFINED BUILD_LIBTORCH) |
| 2 | FILE(GLOB _DIST_SRCS *.cpp) | 2 | FILE(GLOB _DIST_SRCS *.cpp) |
| 3 | # Exclude Python binding files when building libtorch | 3 | # Exclude Python binding files when building libtorch |
| 4 | list(REMOVE_ITEM _DIST_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/Init.cpp") | 4 | list(REMOVE_ITEM _DIST_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/Init.cpp") |
| 5 | else() | 5 | else() |
| 6 | FILE(GLOB _DIST_SRCS *.cpp rpc/*.cpp symm_mem/*.cpp) | 6 | FILE(GLOB _DIST_SRCS *.cpp rpc/*.cpp symm_mem/*.cpp) |
| 7 | endif() | 7 | endif() |
| 8 | 8 | ||
| 9 | LIST(APPEND DIST_SRCS ${_DIST_SRCS}) | 9 | LIST(APPEND DIST_SRCS ${_DIST_SRCS}) |
| 10 | 10 | ||
| 11 | # Pass to parent | 11 | # Pass to parent |
| 12 | set(DIST_SRCS ${DIST_SRCS} PARENT_SCOPE) | 12 | set(DIST_SRCS ${DIST_SRCS} PARENT_SCOPE) |
| @@ -1,311 +1,311 @@ | |||
| 1 | #include <filesystem> | 1 | #include <filesystem> |
| 2 | #include <fstream> | 2 | #include <fstream> |
| 3 | #include <string> | 3 | #include <string> |
| 4 | 4 | ||
| 5 | #include <torch/csrc/distributed/c10d/Utils.hpp> | 5 | #include <torch/csrc/distributed/c10d/Utils.hpp> |
| 6 | 6 | ||
| 7 | #include "torch_npu/csrc/core/npu/interface/HcclInterface.h" | 7 | #include "torch_npu/csrc/core/npu/interface/HcclInterface.h" |
| 8 | #include "torch_npu/csrc/distributed/HCCLUtils.hpp" | 8 | #include "torch_npu/csrc/distributed/HCCLUtils.hpp" |
| 9 | 9 | ||
| 10 | 10 | ||
| 11 | namespace c10d_npu { | 11 | namespace c10d_npu { |
| 12 | bool isFileExists(const std::string& path) | 12 | bool isFileExists(const std::string& path) |
| 13 | { | 13 | { |
| 14 | std::filesystem::path filePath(path); | 14 | std::filesystem::path filePath(path); |
| 15 | 15 | ||
| 16 | if (!filePath.is_absolute()) { | 16 | if (!filePath.is_absolute()) { |
| 17 | TORCH_CHECK(false, "Path is not absolute.", DIST_ERROR(ErrCode::UNAVAIL)) | 17 | TORCH_CHECK(false, "Path is not absolute.", DIST_ERROR(ErrCode::UNAVAIL)) |
| 18 | return false; | 18 | return false; |
| 19 | } | 19 | } |
| 20 | 20 | ||
| 21 | if (std::filesystem::exists(filePath) && std::filesystem::is_regular_file(filePath)) { | 21 | if (std::filesystem::exists(filePath) && std::filesystem::is_regular_file(filePath)) { |
| 22 | return true; | 22 | return true; |
| 23 | } else { | 23 | } else { |
| 24 | return false; | 24 | return false; |
| 25 | } | 25 | } |
| 26 | } | 26 | } |
| 27 | 27 | ||
| 28 | bool checkFilePathReadable(const std::string& file) | 28 | bool checkFilePathReadable(const std::string& file) |
| 29 | { | 29 | { |
| 30 | std::filesystem::path filePath(file); | 30 | std::filesystem::path filePath(file); |
| 31 | 31 | ||
| 32 | if (!std::filesystem::exists(filePath)) { | 32 | if (!std::filesystem::exists(filePath)) { |
| 33 | return false; | 33 | return false; |
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | if (std::filesystem::is_symlink(filePath)) { | 36 | if (std::filesystem::is_symlink(filePath)) { |
| 37 | return false; | 37 | return false; |
| 38 | } | 38 | } |
| 39 | 39 | ||
| 40 | if (!std::filesystem::is_regular_file(filePath)) { | 40 | if (!std::filesystem::is_regular_file(filePath)) { |
| 41 | return false; | 41 | return false; |
| 42 | } | 42 | } |
| 43 | 43 | ||
| 44 | std::filesystem::perms perms = std::filesystem::status(filePath).permissions(); | 44 | std::filesystem::perms perms = std::filesystem::status(filePath).permissions(); |
| 45 | if ((perms & std::filesystem::perms::owner_read) == std::filesystem::perms::owner_read) { | 45 | if ((perms & std::filesystem::perms::owner_read) == std::filesystem::perms::owner_read) { |
| 46 | return true; | 46 | return true; |
| 47 | } | 47 | } |
| 48 | return false; | 48 | return false; |
| 49 | } | 49 | } |
| 50 | 50 | ||
| 51 | // HCCL DataType mapping | 51 | // HCCL DataType mapping |
| 52 | std::map<at::ScalarType, HcclDataType> kScalarTypeToHcclDataType = { | 52 | std::map<at::ScalarType, HcclDataType> kScalarTypeToHcclDataType = { |
| 53 | {at::kByte, HCCL_DATA_TYPE_UINT8}, | 53 | {at::kByte, HCCL_DATA_TYPE_UINT8}, |
| 54 | {at::kChar, HCCL_DATA_TYPE_INT8}, | 54 | {at::kChar, HCCL_DATA_TYPE_INT8}, |
| 55 | {at::kShort, HCCL_DATA_TYPE_INT16}, | 55 | {at::kShort, HCCL_DATA_TYPE_INT16}, |
| 56 | {at::kInt, HCCL_DATA_TYPE_INT32}, | 56 | {at::kInt, HCCL_DATA_TYPE_INT32}, |
| 57 | {at::kLong, HCCL_DATA_TYPE_INT64}, | 57 | {at::kLong, HCCL_DATA_TYPE_INT64}, |
| 58 | {at::kHalf, HCCL_DATA_TYPE_FP16}, | 58 | {at::kHalf, HCCL_DATA_TYPE_FP16}, |
| 59 | {at::kFloat, HCCL_DATA_TYPE_FP32}, | 59 | {at::kFloat, HCCL_DATA_TYPE_FP32}, |
| 60 | {at::ScalarType::UInt16, HCCL_DATA_TYPE_UINT16}, | 60 | {at::ScalarType::UInt16, HCCL_DATA_TYPE_UINT16}, |
| 61 | {at::ScalarType::UInt32, HCCL_DATA_TYPE_UINT32}, | 61 | {at::ScalarType::UInt32, HCCL_DATA_TYPE_UINT32}, |
| 62 | {at::ScalarType::UInt64, HCCL_DATA_TYPE_UINT64}, | 62 | {at::ScalarType::UInt64, HCCL_DATA_TYPE_UINT64}, |
| 63 | {at::kDouble, HCCL_DATA_TYPE_FP64}, | 63 | {at::kDouble, HCCL_DATA_TYPE_FP64}, |
| 64 | {at::kBool, HCCL_DATA_TYPE_UINT8}, | 64 | {at::kBool, HCCL_DATA_TYPE_UINT8}, |
| 65 | {at::kBFloat16, HCCL_DATA_TYPE_BFP16}, | 65 | {at::kBFloat16, HCCL_DATA_TYPE_BFP16}, |
| 66 | {at::ScalarType::Float8_e4m3fn, HCCL_DATA_TYPE_FP8E4M3}, | 66 | {at::ScalarType::Float8_e4m3fn, HCCL_DATA_TYPE_FP8E4M3}, |
| 67 | {at::ScalarType::Float8_e5m2, HCCL_DATA_TYPE_FP8E5M2}, | 67 | {at::ScalarType::Float8_e5m2, HCCL_DATA_TYPE_FP8E5M2}, |
| 68 | }; | 68 | }; |
| 69 | 69 | ||
| 70 | std::map<HcclDataType, std::string> kHcclDataTypeToStringMap = { | 70 | std::map<HcclDataType, std::string> kHcclDataTypeToStringMap = { |
| 71 | {HCCL_DATA_TYPE_UINT8, "at::kByte/at::kBool"}, | 71 | {HCCL_DATA_TYPE_UINT8, "at::kByte/at::kBool"}, |
| 72 | {HCCL_DATA_TYPE_INT8, "at::kChar"}, | 72 | {HCCL_DATA_TYPE_INT8, "at::kChar"}, |
| 73 | {HCCL_DATA_TYPE_INT16, "at::kShort"}, | 73 | {HCCL_DATA_TYPE_INT16, "at::kShort"}, |
| 74 | {HCCL_DATA_TYPE_INT32, "at::kInt"}, | 74 | {HCCL_DATA_TYPE_INT32, "at::kInt"}, |
| 75 | {HCCL_DATA_TYPE_UINT16, "at::ScalarType::UInt16"}, | 75 | {HCCL_DATA_TYPE_UINT16, "at::ScalarType::UInt16"}, |
| 76 | {HCCL_DATA_TYPE_UINT32, "at::ScalarType::UInt32"}, | 76 | {HCCL_DATA_TYPE_UINT32, "at::ScalarType::UInt32"}, |
| 77 | {HCCL_DATA_TYPE_UINT64, "at::ScalarType::UInt64"}, | 77 | {HCCL_DATA_TYPE_UINT64, "at::ScalarType::UInt64"}, |
| 78 | {HCCL_DATA_TYPE_INT64, "at::kLong"}, | 78 | {HCCL_DATA_TYPE_INT64, "at::kLong"}, |
| 79 | {HCCL_DATA_TYPE_FP16, "at::kHalf"}, | 79 | {HCCL_DATA_TYPE_FP16, "at::kHalf"}, |
| 80 | {HCCL_DATA_TYPE_FP32, "at::kFloat"}, | 80 | {HCCL_DATA_TYPE_FP32, "at::kFloat"}, |
| 81 | {HCCL_DATA_TYPE_FP64, "at::kDouble"}, | 81 | {HCCL_DATA_TYPE_FP64, "at::kDouble"}, |
| 82 | {HCCL_DATA_TYPE_BFP16, "at::kBFloat16"}, | 82 | {HCCL_DATA_TYPE_BFP16, "at::kBFloat16"}, |
| 83 | {HCCL_DATA_TYPE_FP8E4M3, "at::ScalarType::Float8_e4m3fn"}, | 83 | {HCCL_DATA_TYPE_FP8E4M3, "at::ScalarType::Float8_e4m3fn"}, |
| 84 | {HCCL_DATA_TYPE_FP8E5M2, "at::ScalarType::Float8_e5m2"}, | 84 | {HCCL_DATA_TYPE_FP8E5M2, "at::ScalarType::Float8_e5m2"}, |
| 85 | }; | 85 | }; |
| 86 | 86 | ||
| 87 | // Helper function that gets the data type and issues error if not supported | 87 | // Helper function that gets the data type and issues error if not supported |
| 88 | HcclDataType getHcclDataType(at::ScalarType type) | 88 | HcclDataType getHcclDataType(at::ScalarType type) |
| 89 | { | 89 | { |
| 90 | try { | 90 | try { |
| 91 | return kScalarTypeToHcclDataType.at(type); | 91 | return kScalarTypeToHcclDataType.at(type); |
| 92 | } catch (std::out_of_range& e) { | 92 | } catch (std::out_of_range& e) { |
| 93 | throw std::runtime_error("Unsupported data type for HCCL process group" + DIST_ERROR(ErrCode::NOT_SUPPORT)); | 93 | throw std::runtime_error("Unsupported data type for HCCL process group" + DIST_ERROR(ErrCode::NOT_SUPPORT)); |
| 94 | } | 94 | } |
| 95 | } | 95 | } |
| 96 | 96 | ||
| 97 | std::string getHcclDataTypeSerialString(HcclDataType type) | 97 | std::string getHcclDataTypeSerialString(HcclDataType type) |
| 98 | { | 98 | { |
| 99 | const auto& iter = kHcclDataTypeToStringMap.find(type); | 99 | const auto& iter = kHcclDataTypeToStringMap.find(type); |
| 100 | if (iter != kHcclDataTypeToStringMap.cend()) { | 100 | if (iter != kHcclDataTypeToStringMap.cend()) { |
| 101 | return iter->second; | 101 | return iter->second; |
| 102 | } else { | 102 | } else { |
| 103 | TORCH_NPU_WARN_ONCE("Can not serialize undefined hccl data type."); | 103 | TORCH_NPU_WARN_ONCE("Can not serialize undefined hccl data type."); |
| 104 | return ""; | 104 | return ""; |
| 105 | } | 105 | } |
| 106 | } | 106 | } |
| 107 | 107 | ||
| 108 | bool isSupportHcclCommName() | 108 | bool isSupportHcclCommName() |
| 109 | { | 109 | { |
| 110 | return at_npu::hccl::isHcclFeatureSupported(HcclCommConfigCapability::HCCL_COMM_CONFIG_COMM_NAME); | 110 | return at_npu::hccl::isHcclFeatureSupported(HcclCommConfigCapability::HCCL_COMM_CONFIG_COMM_NAME); |
| 111 | } | 111 | } |
| 112 | 112 | ||
| 113 | HCCLComm::HCCLComm(HcclComm hcclComm) : hcclComm_(hcclComm), hcclAsyncErr_(HCCL_SUCCESS), | 113 | HCCLComm::HCCLComm(HcclComm hcclComm) : hcclComm_(hcclComm), hcclAsyncErr_(HCCL_SUCCESS), |
| 114 | hcclCommType(0), p2pPeer(0) {} | 114 | hcclCommType(0), p2pPeer(0) {} |
| 115 | 115 | ||
| 116 | HCCLComm::~HCCLComm() | 116 | HCCLComm::~HCCLComm() |
| 117 | { | 117 | { |
| 118 | destroyHcclComm(); | 118 | destroyHcclComm(); |
| 119 | } | 119 | } |
| 120 | 120 | ||
| 121 | std::shared_ptr<HCCLComm> HCCLComm::create( | 121 | std::shared_ptr<HCCLComm> HCCLComm::create( |
| 122 | int numRanks, | 122 | int numRanks, |
| 123 | int rank, | 123 | int rank, |
| 124 | HcclRootInfo& rootInfo) | 124 | HcclRootInfo& rootInfo) |
| 125 | { | 125 | { |
| 126 | auto comm = std::make_shared<HCCLComm>(); | 126 | auto comm = std::make_shared<HCCLComm>(); |
| 127 | HCCL_CHECK_ERROR(hcclCommInitRootInfo(numRanks, &rootInfo, rank, &(comm->hcclComm_))); | 127 | HCCL_CHECK_ERROR(hcclCommInitRootInfo(numRanks, &rootInfo, rank, &(comm->hcclComm_))); |
| 128 | c10_npu::NpuSysCtrl::GetInstance().RegisterReleaseFn([=]() ->void {comm->destroyHcclComm();}, | 128 | c10_npu::NpuSysCtrl::GetInstance().RegisterReleaseFn([=]() ->void {comm->destroyHcclComm();}, |
| 129 | c10_npu::ReleasePriority::PriorityMiddle); | 129 | c10_npu::ReleasePriority::PriorityMiddle); |
| 130 | return comm; | 130 | return comm; |
| 131 | } | 131 | } |
| 132 | 132 | ||
| 133 | std::shared_ptr<HCCLComm> HCCLComm::create_config( | 133 | std::shared_ptr<HCCLComm> HCCLComm::create_config( |
| 134 | int numRanks, | 134 | int numRanks, |
| 135 | int rank, | 135 | int rank, |
| 136 | HcclRootInfo& rootInfo, | 136 | HcclRootInfo& rootInfo, |
| 137 | HcclCommConfig* config) | 137 | HcclCommConfig* config) |
| 138 | { | 138 | { |
| 139 | auto comm = std::make_shared<HCCLComm>(); | 139 | auto comm = std::make_shared<HCCLComm>(); |
| 140 | HCCL_CHECK_ERROR(hcclCommInitRootInfoConfig(numRanks, &rootInfo, rank, config, &(comm->hcclComm_))); | 140 | HCCL_CHECK_ERROR(hcclCommInitRootInfoConfig(numRanks, &rootInfo, rank, config, &(comm->hcclComm_))); |
| 141 | c10_npu::NpuSysCtrl::GetInstance().RegisterReleaseFn([=]() ->void {comm->destroyHcclComm();}, | 141 | c10_npu::NpuSysCtrl::GetInstance().RegisterReleaseFn([=]() ->void {comm->destroyHcclComm();}, |
| 142 | c10_npu::ReleasePriority::PriorityMiddle); | 142 | c10_npu::ReleasePriority::PriorityMiddle); |
| 143 | return comm; | 143 | return comm; |
| 144 | } | 144 | } |
| 145 | 145 | ||
| 146 | std::shared_ptr<HCCLComm> HCCLComm::createGlobalHcclComm( | 146 | std::shared_ptr<HCCLComm> HCCLComm::createGlobalHcclComm( |
| 147 | const char *clusterInfo, | 147 | const char *clusterInfo, |
| 148 | uint32_t rank, | 148 | uint32_t rank, |
| 149 | HcclCommConfig* config) | 149 | HcclCommConfig* config) |
| 150 | { | 150 | { |
| 151 | auto comm = std::make_shared<HCCLComm>(); | 151 | auto comm = std::make_shared<HCCLComm>(); |
| 152 | if (hcclCommInitClusterInfoConfig(clusterInfo, rank, config, &(comm->hcclComm_)) != HCCL_SUCCESS) { | 152 | if (hcclCommInitClusterInfoConfig(clusterInfo, rank, config, &(comm->hcclComm_)) != HCCL_SUCCESS) { |
| 153 | return nullptr; | 153 | return nullptr; |
| 154 | } | 154 | } |
| 155 | c10_npu::NpuSysCtrl::GetInstance().RegisterReleaseFn([=]() ->void {comm->destroyHcclComm();}, | 155 | c10_npu::NpuSysCtrl::GetInstance().RegisterReleaseFn([=]() ->void {comm->destroyHcclComm();}, |
| 156 | c10_npu::ReleasePriority::PriorityMiddle); | 156 | c10_npu::ReleasePriority::PriorityMiddle); |
| 157 | return comm; | 157 | return comm; |
| 158 | } | 158 | } |
| 159 | 159 | ||
| 160 | std::shared_ptr<HCCLComm> HCCLComm::createSubHcclComm( | 160 | std::shared_ptr<HCCLComm> HCCLComm::createSubHcclComm( |
| 161 | std::shared_ptr<HCCLComm> comm, | 161 | std::shared_ptr<HCCLComm> comm, |
| 162 | uint32_t rankNum, | 162 | uint32_t rankNum, |
| 163 | uint32_t *rankIds, | 163 | uint32_t *rankIds, |
| 164 | uint64_t subCommId, | 164 | uint64_t subCommId, |
| 165 | uint32_t subCommRankId, | 165 | uint32_t subCommRankId, |
| 166 | HcclCommConfig* config) | 166 | HcclCommConfig* config) |
| 167 | { | 167 | { |
| 168 | auto subComm = std::make_shared<HCCLComm>(); | 168 | auto subComm = std::make_shared<HCCLComm>(); |
| 169 | if (hcclCreateSubCommConfig(&(comm->hcclComm_), rankNum, rankIds, subCommId, subCommRankId, | 169 | if (hcclCreateSubCommConfig(&(comm->hcclComm_), rankNum, rankIds, subCommId, subCommRankId, |
| 170 | config, &(subComm->hcclComm_)) != HCCL_SUCCESS) { | 170 | config, &(subComm->hcclComm_)) != HCCL_SUCCESS) { |
| 171 | return nullptr; | 171 | return nullptr; |
| 172 | } | 172 | } |
| 173 | c10_npu::NpuSysCtrl::GetInstance().RegisterReleaseFn([=]() ->void {subComm->destroyHcclComm();}, | 173 | c10_npu::NpuSysCtrl::GetInstance().RegisterReleaseFn([=]() ->void {subComm->destroyHcclComm();}, |
| 174 | c10_npu::ReleasePriority::PriorityMiddle); | 174 | c10_npu::ReleasePriority::PriorityMiddle); |
| 175 | return subComm; | 175 | return subComm; |
| 176 | } | 176 | } |
| 177 | 177 | ||
| 178 | // Move constructable | 178 | // Move constructable |
| 179 | HCCLComm::HCCLComm(HCCLComm&& other) | 179 | HCCLComm::HCCLComm(HCCLComm&& other) |
| 180 | { | 180 | { |
| 181 | std::swap(hcclComm_, other.hcclComm_); | 181 | std::swap(hcclComm_, other.hcclComm_); |
| 182 | std::swap(hcclAsyncErr_, other.hcclAsyncErr_); | 182 | std::swap(hcclAsyncErr_, other.hcclAsyncErr_); |
| 183 | std::swap(hcclCommType, other.hcclCommType); | 183 | std::swap(hcclCommType, other.hcclCommType); |
| 184 | std::swap(p2pPeer, other.p2pPeer); | 184 | std::swap(p2pPeer, other.p2pPeer); |
| 185 | } | 185 | } |
| 186 | 186 | ||
| 187 | // Move assignable | 187 | // Move assignable |
| 188 | HCCLComm& HCCLComm::operator=(HCCLComm&& other) | 188 | HCCLComm& HCCLComm::operator=(HCCLComm&& other) |
| 189 | { | 189 | { |
| 190 | std::swap(hcclComm_, other.hcclComm_); | 190 | std::swap(hcclComm_, other.hcclComm_); |
| 191 | std::swap(hcclAsyncErr_, other.hcclAsyncErr_); | 191 | std::swap(hcclAsyncErr_, other.hcclAsyncErr_); |
| 192 | std::swap(hcclCommType, other.hcclCommType); | 192 | std::swap(hcclCommType, other.hcclCommType); |
| 193 | std::swap(p2pPeer, other.p2pPeer); | 193 | std::swap(p2pPeer, other.p2pPeer); |
| 194 | return *this; | 194 | return *this; |
| 195 | } | 195 | } |
| 196 | 196 | ||
| 197 | void HCCLComm::destroyHcclComm() | 197 | void HCCLComm::destroyHcclComm() |
| 198 | { | 198 | { |
| 199 | std::unique_lock<std::mutex> lock(mutex_); | 199 | std::unique_lock<std::mutex> lock(mutex_); |
| 200 | if (hcclComm_) { | 200 | if (hcclComm_) { |
| 201 | hcclCommDestroy(hcclComm_); | 201 | hcclCommDestroy(hcclComm_); |
| 202 | hcclComm_ = nullptr; | 202 | hcclComm_ = nullptr; |
| 203 | } | 203 | } |
| 204 | } | 204 | } |
| 205 | 205 | ||
| 206 | HcclResult HCCLComm::checkForHcclError() | 206 | HcclResult HCCLComm::checkForHcclError() |
| 207 | { | 207 | { |
| 208 | std::unique_lock<std::mutex> lock(mutex_); | 208 | std::unique_lock<std::mutex> lock(mutex_); |
| 209 | #ifdef ENABLE_HCCL_ERROR_CHECKING | 209 | #ifdef ENABLE_HCCL_ERROR_CHECKING |
| 210 | if (hcclAsyncErr_ != HCCL_SUCCESS) { | 210 | if (hcclAsyncErr_ != HCCL_SUCCESS) { |
| 211 | return hcclAsyncErr_; | 211 | return hcclAsyncErr_; |
| 212 | } | 212 | } |
| 213 | if (hcclComm_ != nullptr) { | 213 | if (hcclComm_ != nullptr) { |
| 214 | HcclResult result = hcclGetCommAsyncError(hcclComm_, &hcclAsyncErr_); | 214 | HcclResult result = hcclGetCommAsyncError(hcclComm_, &hcclAsyncErr_); |
| 215 | if (result != HCCL_SUCCESS) { | 215 | if (result != HCCL_SUCCESS) { |
| 216 | std::string temp_str = std::string("Failed to get HCCL error code: ") + std::to_string(result); | 216 | std::string temp_str = std::string("Failed to get HCCL error code: ") + std::to_string(result); |
| 217 | const char* errmsg = temp_str.c_str(); | 217 | const char* errmsg = temp_str.c_str(); |
| 218 | ASCEND_LOGE("%s", errmsg); | 218 | ASCEND_LOGE("%s", errmsg); |
| 219 | LOG(ERROR) << c10::str(errmsg); | 219 | LOG(ERROR) << c10::str(errmsg); |
| 220 | return result; // return this error result instead of hcclAsyncErr_ | 220 | return result; // return this error result instead of hcclAsyncErr_ |
| 221 | } | 221 | } |
| 222 | } | 222 | } |
| 223 | return hcclAsyncErr_; | 223 | return hcclAsyncErr_; |
| 224 | #else | 224 | #else |
| 225 | // Always return success, if error checks are disabled. | 225 | // Always return success, if error checks are disabled. |
| 226 | return HCCL_SUCCESS; | 226 | return HCCL_SUCCESS; |
| 227 | #endif | 227 | #endif |
| 228 | } | 228 | } |
| 229 | 229 | ||
| 230 | void DebugInfoWriter::write(const std::string &hcclTrace) | 230 | void DebugInfoWriter::write(const std::string &hcclTrace) |
| 231 | { | 231 | { |
| 232 | // Open a file for writing. The ios::binary flag is used to write data as | 232 | // Open a file for writing. The ios::binary flag is used to write data as |
| 233 | // binary. | 233 | // binary. |
| 234 | std::ofstream file(filename_, std::ios::binary); | 234 | std::ofstream file(filename_, std::ios::binary); |
| 235 | 235 | ||
| 236 | // Check if the file was opened successfully. | 236 | // Check if the file was opened successfully. |
| 237 | if (!file.is_open()) { | 237 | if (!file.is_open()) { |
| 238 | LOG(ERROR) << "Error opening file for writing HCCLPG debug info: " | 238 | LOG(ERROR) << "Error opening file for writing HCCLPG debug info: " |
| 239 | << filename_; | 239 | << filename_; |
| 240 | return; | 240 | return; |
| 241 | } | 241 | } |
| 242 | 242 | ||
| 243 | file.write(hcclTrace.data(), hcclTrace.size()); | 243 | file.write(hcclTrace.data(), hcclTrace.size()); |
| 244 | LOG(INFO) << "Finished writing HCCLPG debug info to " << filename_; | 244 | LOG(INFO) << "Finished writing HCCLPG debug info to " << filename_; |
| 245 | } | 245 | } |
| 246 | 246 | ||
| 247 | DebugInfoWriter &DebugInfoWriter::getWriter(int rank) | 247 | DebugInfoWriter &DebugInfoWriter::getWriter(int rank) |
| 248 | { | 248 | { |
| 249 | if (writer_ == nullptr) { | 249 | if (writer_ == nullptr) { |
| 250 | std::string fileNamePrefix = c10d::getCvarString( | 250 | std::string fileNamePrefix = c10d::getCvarString( |
| 251 | {"TORCH_HCCL_DEBUG_INFO_TEMP_FILE"}, "/tmp/hccl_trace_rank_"); | 251 | {"TORCH_HCCL_DEBUG_INFO_TEMP_FILE"}, "/tmp/hccl_trace_rank_"); |
| 252 | // Using std::unique_ptr here to auto-delete the writer object | 252 | // Using std::unique_ptr here to auto-delete the writer object |
| 253 | // when the pointer itself is destroyed. | 253 | // when the pointer itself is destroyed. |
| 254 | std::unique_ptr<DebugInfoWriter> writerPtr( | 254 | std::unique_ptr<DebugInfoWriter> writerPtr( |
| 255 | new DebugInfoWriter(fileNamePrefix, rank)); | 255 | new DebugInfoWriter(fileNamePrefix, rank)); |
| 256 | DebugInfoWriter::registerWriter(std::move(writerPtr)); | 256 | DebugInfoWriter::registerWriter(std::move(writerPtr)); |
| 257 | } | 257 | } |
| 258 | return *writer_; | 258 | return *writer_; |
| 259 | } | 259 | } |
| 260 | 260 | ||
| 261 | void DebugInfoWriter::registerWriter(std::unique_ptr<DebugInfoWriter> writer) | 261 | void DebugInfoWriter::registerWriter(std::unique_ptr<DebugInfoWriter> writer) |
| 262 | { | 262 | { |
| 263 | TORCH_CHECK_WITH( | 263 | TORCH_CHECK_WITH( |
| 264 | DistBackendError, | 264 | DistBackendError, |
| 265 | !hasWriterRegistered_.load(), | 265 | !hasWriterRegistered_.load(), |
| 266 | "debugInfoWriter already registered"); | 266 | "debugInfoWriter already registered"); |
| 267 | hasWriterRegistered_.store(true); | 267 | hasWriterRegistered_.store(true); |
| 268 | writer_ = std::move(writer); | 268 | writer_ = std::move(writer); |
| 269 | } | 269 | } |
| 270 | 270 | ||
| 271 | std::unique_ptr<DebugInfoWriter> DebugInfoWriter::writer_ = nullptr; | 271 | std::unique_ptr<DebugInfoWriter> DebugInfoWriter::writer_ = nullptr; |
| 272 | std::atomic<bool> DebugInfoWriter::hasWriterRegistered_(false); | 272 | std::atomic<bool> DebugInfoWriter::hasWriterRegistered_(false); |
| 273 | 273 | ||
| 274 | struct HcclBufferNameKey { | 274 | struct HcclBufferNameKey { |
| 275 | c10::DeviceIndex device_index; | 275 | c10::DeviceIndex device_index; |
| 276 | std::string name; | 276 | std::string name; |
| 277 | bool operator<(const HcclBufferNameKey& other) const | 277 | bool operator<(const HcclBufferNameKey& other) const |
| 278 | { | 278 | { |
| 279 | if (device_index != other.device_index) { | 279 | if (device_index != other.device_index) { |
| 280 | return device_index < other.device_index; | 280 | return device_index < other.device_index; |
| 281 | } | 281 | } |
| 282 | return name < other.name; // sort by name if device_index is the same | 282 | return name < other.name; // sort by name if device_index is the same |
| 283 | } | 283 | } |
| 284 | }; | 284 | }; |
| 285 | 285 | ||
| 286 | struct HcclBufferNameStreamMap { | 286 | struct HcclBufferNameStreamMap { |
| 287 | std::map<HcclBufferNameKey, c10_npu::NPUStream> map; | 287 | std::map<HcclBufferNameKey, c10_npu::NPUStream> map; |
| 288 | std::mutex mutex; | 288 | std::mutex mutex; |
| 289 | } g_BufferNameStreamMap = {}; | 289 | } g_BufferNameStreamMap = {}; |
| 290 | 290 | ||
| 291 | c10::optional<c10_npu::NPUStream> getHcclStreamByBufferName(const std::string &name, c10::DeviceIndex device_index) | 291 | c10::optional<c10_npu::NPUStream> getHcclStreamByBufferName(const std::string &name, c10::DeviceIndex device_index) |
| 292 | { | 292 | { |
| 293 | std::unique_lock<std::mutex> lock(g_BufferNameStreamMap.mutex); | 293 | std::unique_lock<std::mutex> lock(g_BufferNameStreamMap.mutex); |
| 294 | auto &map = g_BufferNameStreamMap.map; | 294 | auto &map = g_BufferNameStreamMap.map; |
| 295 | auto it = map.find({device_index, name}); | 295 | auto it = map.find({device_index, name}); |
| 296 | if (it == map.end()) { | 296 | if (it == map.end()) { |
| 297 | return {}; | 297 | return {}; |
| 298 | } | 298 | } |
| 299 | return it->second; | 299 | return it->second; |
| 300 | } | 300 | } |
| 301 | 301 | ||
| 302 | bool setHcclStreamByBufferName(const std::string &name, c10::DeviceIndex device_index, c10_npu::NPUStream steam) | 302 | bool setHcclStreamByBufferName(const std::string &name, c10::DeviceIndex device_index, c10_npu::NPUStream steam) |
| 303 | { | 303 | { |
| 304 | HcclBufferNameKey key = {device_index, name}; | 304 | HcclBufferNameKey key = {device_index, name}; |
| 305 | std::unique_lock<std::mutex> lock(g_BufferNameStreamMap.mutex); | 305 | std::unique_lock<std::mutex> lock(g_BufferNameStreamMap.mutex); |
| 306 | auto &map = g_BufferNameStreamMap.map; | 306 | auto &map = g_BufferNameStreamMap.map; |
| 307 | auto pair = map.insert({key, steam}); | 307 | auto pair = map.insert({key, steam}); |
| 308 | return pair.second; | 308 | return pair.second; |
| 309 | } | 309 | } |
| 310 | 310 | ||
| 311 | } // namespace c10d_npu | 311 | } // namespace c10d_npu |
| @@ -1,496 +1,496 @@ | |||
| 1 | #pragma once | 1 | #pragma once |
| 2 | 2 | ||
| 3 | #include <c10/util/CallOnce.h> | 3 | #include <c10/util/CallOnce.h> |
| 4 | #include "torch_npu/csrc/core/npu/NPUException.h" | 4 | #include "torch_npu/csrc/core/npu/NPUException.h" |
| 5 | #include "torch_npu/csrc/core/npu/register/FunctionLoader.h" | 5 | #include "torch_npu/csrc/core/npu/register/FunctionLoader.h" |
| 6 | #include "torch_npu/csrc/core/npu/NpuVariables.h" | 6 | #include "torch_npu/csrc/core/npu/NpuVariables.h" |
| 7 | 7 | ||
| 8 | namespace c10d_npu { | 8 | namespace c10d_npu { |
| 9 | #undef TORCH_NPU_LOAD_FUNC | 9 | #undef TORCH_NPU_LOAD_FUNC |
| 10 | #define TORCH_NPU_LOAD_FUNC(funcName) \ | 10 | #define TORCH_NPU_LOAD_FUNC(funcName) \ |
| 11 | TORCH_NPU_REGISTER_FUNCTION(libhccl, funcName) | 11 | TORCH_NPU_REGISTER_FUNCTION(libhccl, funcName) |
| 12 | 12 | ||
| 13 | #undef TORCH_NPU_GET_FUNC | 13 | #undef TORCH_NPU_GET_FUNC |
| 14 | #define TORCH_NPU_GET_FUNC(funcName) \ | 14 | #define TORCH_NPU_GET_FUNC(funcName) \ |
| 15 | TORCH_NPU_GET_FUNCTION(libhccl, funcName) | 15 | TORCH_NPU_GET_FUNCTION(libhccl, funcName) |
| 16 | 16 | ||
| 17 | TORCH_NPU_REGISTER_LIBRARY(libhccl) | 17 | TORCH_NPU_REGISTER_LIBRARY(libhccl) |
| 18 | TORCH_NPU_LOAD_FUNC(HcclAlltoAllV) | 18 | TORCH_NPU_LOAD_FUNC(HcclAlltoAllV) |
| 19 | TORCH_NPU_LOAD_FUNC(HcclAllGatherV) | 19 | TORCH_NPU_LOAD_FUNC(HcclAllGatherV) |
| 20 | TORCH_NPU_LOAD_FUNC(HcclReduceScatterV) | 20 | TORCH_NPU_LOAD_FUNC(HcclReduceScatterV) |
| 21 | TORCH_NPU_LOAD_FUNC(HcclReduce) | 21 | TORCH_NPU_LOAD_FUNC(HcclReduce) |
| 22 | TORCH_NPU_LOAD_FUNC(HcclGetCommAsyncError) | 22 | TORCH_NPU_LOAD_FUNC(HcclGetCommAsyncError) |
| 23 | TORCH_NPU_LOAD_FUNC(HcclScatter) | 23 | TORCH_NPU_LOAD_FUNC(HcclScatter) |
| 24 | TORCH_NPU_LOAD_FUNC(HcclBatchSendRecv) | 24 | TORCH_NPU_LOAD_FUNC(HcclBatchSendRecv) |
| 25 | TORCH_NPU_LOAD_FUNC(HcclAlltoAll) | 25 | TORCH_NPU_LOAD_FUNC(HcclAlltoAll) |
| 26 | TORCH_NPU_LOAD_FUNC(HcclCommInitRootInfoConfig) | 26 | TORCH_NPU_LOAD_FUNC(HcclCommInitRootInfoConfig) |
| 27 | TORCH_NPU_LOAD_FUNC(HcclGetCommConfigCapability) | 27 | TORCH_NPU_LOAD_FUNC(HcclGetCommConfigCapability) |
| 28 | TORCH_NPU_LOAD_FUNC(HcclCommInitClusterInfoConfig) | 28 | TORCH_NPU_LOAD_FUNC(HcclCommInitClusterInfoConfig) |
| 29 | TORCH_NPU_LOAD_FUNC(HcclCreateSubCommConfig) | 29 | TORCH_NPU_LOAD_FUNC(HcclCreateSubCommConfig) |
| 30 | TORCH_NPU_LOAD_FUNC(HcclCommWorkingDevNicSet) | 30 | TORCH_NPU_LOAD_FUNC(HcclCommWorkingDevNicSet) |
| 31 | TORCH_NPU_LOAD_FUNC(HcclCommRegister) | 31 | TORCH_NPU_LOAD_FUNC(HcclCommRegister) |
| 32 | TORCH_NPU_LOAD_FUNC(HcclCommDeregister) | 32 | TORCH_NPU_LOAD_FUNC(HcclCommDeregister) |
| 33 | TORCH_NPU_LOAD_FUNC(HcclCommExchangeMem) | 33 | TORCH_NPU_LOAD_FUNC(HcclCommExchangeMem) |
| 34 | TORCH_NPU_LOAD_FUNC(HcclGetRootInfo) | 34 | TORCH_NPU_LOAD_FUNC(HcclGetRootInfo) |
| 35 | TORCH_NPU_LOAD_FUNC(HcclCommDestroy) | 35 | TORCH_NPU_LOAD_FUNC(HcclCommDestroy) |
| 36 | TORCH_NPU_LOAD_FUNC(HcclSend) | 36 | TORCH_NPU_LOAD_FUNC(HcclSend) |
| 37 | TORCH_NPU_LOAD_FUNC(HcclRecv) | 37 | TORCH_NPU_LOAD_FUNC(HcclRecv) |
| 38 | TORCH_NPU_LOAD_FUNC(HcclAllReduce) | 38 | TORCH_NPU_LOAD_FUNC(HcclAllReduce) |
| 39 | TORCH_NPU_LOAD_FUNC(HcclBroadcast) | 39 | TORCH_NPU_LOAD_FUNC(HcclBroadcast) |
| 40 | TORCH_NPU_LOAD_FUNC(HcclAllGather) | 40 | TORCH_NPU_LOAD_FUNC(HcclAllGather) |
| 41 | TORCH_NPU_LOAD_FUNC(HcclReduceScatter) | 41 | TORCH_NPU_LOAD_FUNC(HcclReduceScatter) |
| 42 | TORCH_NPU_LOAD_FUNC(HcclCommInitAll) | 42 | TORCH_NPU_LOAD_FUNC(HcclCommInitAll) |
| 43 | TORCH_NPU_LOAD_FUNC(HcclCommInitRootInfo) | 43 | TORCH_NPU_LOAD_FUNC(HcclCommInitRootInfo) |
| 44 | 44 | ||
| 45 | TORCH_NPU_REGISTER_LIBRARY(libhcomm) | 45 | TORCH_NPU_REGISTER_LIBRARY(libhcomm) |
| 46 | TORCH_NPU_REGISTER_FUNCTION(libhcomm, HcclGroupStart) | 46 | TORCH_NPU_REGISTER_FUNCTION(libhcomm, HcclGroupStart) |
| 47 | TORCH_NPU_REGISTER_FUNCTION(libhcomm, HcclGroupEnd) | 47 | TORCH_NPU_REGISTER_FUNCTION(libhcomm, HcclGroupEnd) |
| 48 | 48 | ||
| 49 | extern HcclResult hcclGetRootInfo(HcclRootInfo *rootInfo) | 49 | extern HcclResult hcclGetRootInfo(HcclRootInfo *rootInfo) |
| 50 | { | 50 | { |
| 51 | using HcclGetRootInfoFunc = HcclResult(*)(HcclRootInfo *); | 51 | using HcclGetRootInfoFunc = HcclResult(*)(HcclRootInfo *); |
| 52 | static HcclGetRootInfoFunc func = nullptr; | 52 | static HcclGetRootInfoFunc func = nullptr; |
| 53 | if (func == nullptr) { | 53 | if (func == nullptr) { |
| 54 | func = (HcclGetRootInfoFunc)TORCH_NPU_GET_FUNC(HcclGetRootInfo) | 54 | func = (HcclGetRootInfoFunc)TORCH_NPU_GET_FUNC(HcclGetRootInfo) |
| 55 | } | 55 | } |
| 56 | TORCH_CHECK(func, "Failed to find function ", "HcclGetRootInfo", DIST_ERROR(ErrCode::NOT_FOUND)); | 56 | TORCH_CHECK(func, "Failed to find function ", "HcclGetRootInfo", DIST_ERROR(ErrCode::NOT_FOUND)); |
| 57 | auto ret = func(rootInfo); | 57 | auto ret = func(rootInfo); |
| 58 | return ret; | 58 | return ret; |
| 59 | } | 59 | } |
| 60 | 60 | ||
| 61 | extern HcclResult hcclCommDestroy(HcclComm comm) | 61 | extern HcclResult hcclCommDestroy(HcclComm comm) |
| 62 | { | 62 | { |
| 63 | using HcclCommDestroyFunc = HcclResult(*)(HcclComm); | 63 | using HcclCommDestroyFunc = HcclResult(*)(HcclComm); |
| 64 | static HcclCommDestroyFunc func = nullptr; | 64 | static HcclCommDestroyFunc func = nullptr; |
| 65 | if (func == nullptr) { | 65 | if (func == nullptr) { |
| 66 | func = (HcclCommDestroyFunc)TORCH_NPU_GET_FUNC(HcclCommDestroy) | 66 | func = (HcclCommDestroyFunc)TORCH_NPU_GET_FUNC(HcclCommDestroy) |
| 67 | } | 67 | } |
| 68 | TORCH_CHECK(func, "Failed to find function ", "HcclCommDestroy", DIST_ERROR(ErrCode::NOT_FOUND)); | 68 | TORCH_CHECK(func, "Failed to find function ", "HcclCommDestroy", DIST_ERROR(ErrCode::NOT_FOUND)); |
| 69 | auto ret = func(comm); | 69 | auto ret = func(comm); |
| 70 | return ret; | 70 | return ret; |
| 71 | } | 71 | } |
| 72 | 72 | ||
| 73 | extern HcclResult hcclSend(void *sendBuf, uint64_t count, HcclDataType dataType, uint32_t destRank, | 73 | extern HcclResult hcclSend(void *sendBuf, uint64_t count, HcclDataType dataType, uint32_t destRank, |
| 74 | HcclComm comm, aclrtStream stream) | 74 | HcclComm comm, aclrtStream stream) |
| 75 | { | 75 | { |
| 76 | using HcclSendFunc = HcclResult(*)( | 76 | using HcclSendFunc = HcclResult(*)( |
| 77 | void *, uint64_t, HcclDataType, uint32_t, HcclComm, aclrtStream); | 77 | void *, uint64_t, HcclDataType, uint32_t, HcclComm, aclrtStream); |
| 78 | static HcclSendFunc func = nullptr; | 78 | static HcclSendFunc func = nullptr; |
| 79 | if (func == nullptr) { | 79 | if (func == nullptr) { |
| 80 | func = (HcclSendFunc)TORCH_NPU_GET_FUNC(HcclSend) | 80 | func = (HcclSendFunc)TORCH_NPU_GET_FUNC(HcclSend) |
| 81 | } | 81 | } |
| 82 | TORCH_CHECK(func, "Failed to find function ", "HcclSend", DIST_ERROR(ErrCode::NOT_FOUND)); | 82 | TORCH_CHECK(func, "Failed to find function ", "HcclSend", DIST_ERROR(ErrCode::NOT_FOUND)); |
| 83 | auto ret = func(sendBuf, count, dataType, destRank, comm, stream); | 83 | auto ret = func(sendBuf, count, dataType, destRank, comm, stream); |
| 84 | return ret; | 84 | return ret; |
| 85 | } | 85 | } |
| 86 | 86 | ||
| 87 | extern HcclResult hcclRecv(void *recvBuf, uint64_t count, HcclDataType dataType, uint32_t srcRank, | 87 | extern HcclResult hcclRecv(void *recvBuf, uint64_t count, HcclDataType dataType, uint32_t srcRank, |
| 88 | HcclComm comm, aclrtStream stream) | 88 | HcclComm comm, aclrtStream stream) |
| 89 | { | 89 | { |
| 90 | using HcclRecvFunc = HcclResult(*)( | 90 | using HcclRecvFunc = HcclResult(*)( |
| 91 | void *, uint64_t, HcclDataType, uint32_t, HcclComm, aclrtStream); | 91 | void *, uint64_t, HcclDataType, uint32_t, HcclComm, aclrtStream); |
| 92 | static HcclRecvFunc func = nullptr; | 92 | static HcclRecvFunc func = nullptr; |
| 93 | if (func == nullptr) { | 93 | if (func == nullptr) { |
| 94 | func = (HcclRecvFunc)TORCH_NPU_GET_FUNC(HcclRecv) | 94 | func = (HcclRecvFunc)TORCH_NPU_GET_FUNC(HcclRecv) |
| 95 | } | 95 | } |
| 96 | TORCH_CHECK(func, "Failed to find function ", "HcclRecv", DIST_ERROR(ErrCode::NOT_FOUND)); | 96 | TORCH_CHECK(func, "Failed to find function ", "HcclRecv", DIST_ERROR(ErrCode::NOT_FOUND)); |
| 97 | auto ret = func(recvBuf, count, dataType, srcRank, comm, stream); | 97 | auto ret = func(recvBuf, count, dataType, srcRank, comm, stream); |
| 98 | return ret; | 98 | return ret; |
| 99 | } | 99 | } |
| 100 | 100 | ||
| 101 | extern HcclResult hcclCommInitAll(uint32_t ndev, int32_t *devices, HcclComm *comms) | 101 | extern HcclResult hcclCommInitAll(uint32_t ndev, int32_t *devices, HcclComm *comms) |
| 102 | { | 102 | { |
| 103 | using HcclCommInitAllFunc = HcclResult(*)( | 103 | using HcclCommInitAllFunc = HcclResult(*)( |
| 104 | uint32_t, int32_t *, HcclComm *); | 104 | uint32_t, int32_t *, HcclComm *); |
| 105 | static HcclCommInitAllFunc func = nullptr; | 105 | static HcclCommInitAllFunc func = nullptr; |
| 106 | if (func == nullptr) { | 106 | if (func == nullptr) { |
| 107 | func = (HcclCommInitAllFunc)TORCH_NPU_GET_FUNC(HcclCommInitAll) | 107 | func = (HcclCommInitAllFunc)TORCH_NPU_GET_FUNC(HcclCommInitAll) |
| 108 | } | 108 | } |
| 109 | TORCH_CHECK(func, "Failed to find function ", "HcclCommInitAll", DIST_ERROR(ErrCode::NOT_FOUND)); | 109 | TORCH_CHECK(func, "Failed to find function ", "HcclCommInitAll", DIST_ERROR(ErrCode::NOT_FOUND)); |
| 110 | auto ret = func(ndev, devices, comms); | 110 | auto ret = func(ndev, devices, comms); |
| 111 | return ret; | 111 | return ret; |
| 112 | } | 112 | } |
| 113 | 113 | ||
| 114 | extern HcclResult hcclAllGather(void *sendBuf, void *recvBuf, uint64_t sendCount, HcclDataType dataType, | 114 | extern HcclResult hcclAllGather(void *sendBuf, void *recvBuf, uint64_t sendCount, HcclDataType dataType, |
| 115 | HcclComm comm, aclrtStream stream) | 115 | HcclComm comm, aclrtStream stream) |
| 116 | { | 116 | { |
| 117 | using HcclAllGatherFunc = HcclResult(*)( | 117 | using HcclAllGatherFunc = HcclResult(*)( |
| 118 | void *, void *, uint64_t, HcclDataType, HcclComm, aclrtStream); | 118 | void *, void *, uint64_t, HcclDataType, HcclComm, aclrtStream); |
| 119 | static HcclAllGatherFunc func = nullptr; | 119 | static HcclAllGatherFunc func = nullptr; |
| 120 | if (func == nullptr) { | 120 | if (func == nullptr) { |
| 121 | func = (HcclAllGatherFunc)TORCH_NPU_GET_FUNC(HcclAllGather) | 121 | func = (HcclAllGatherFunc)TORCH_NPU_GET_FUNC(HcclAllGather) |
| 122 | } | 122 | } |
| 123 | TORCH_CHECK(func, "Failed to find function ", "HcclAllGather", DIST_ERROR(ErrCode::NOT_FOUND)); | 123 | TORCH_CHECK(func, "Failed to find function ", "HcclAllGather", DIST_ERROR(ErrCode::NOT_FOUND)); |
| 124 | auto ret = func(sendBuf, recvBuf, sendCount, dataType, comm, stream); | 124 | auto ret = func(sendBuf, recvBuf, sendCount, dataType, comm, stream); |
| 125 | return ret; | 125 | return ret; |
| 126 | } | 126 | } |
| 127 | 127 | ||
| 128 | extern HcclResult hcclAllReduce(void *sendBuf, void *recvBuf, uint64_t count, HcclDataType dataType, | 128 | extern HcclResult hcclAllReduce(void *sendBuf, void *recvBuf, uint64_t count, HcclDataType dataType, |
| 129 | HcclReduceOp op, HcclComm comm, aclrtStream stream) | 129 | HcclReduceOp op, HcclComm comm, aclrtStream stream) |
| 130 | { | 130 | { |
| 131 | using HcclAllReduceFunc = HcclResult(*)( | 131 | using HcclAllReduceFunc = HcclResult(*)( |
| 132 | void *, void *, uint64_t, HcclDataType, HcclReduceOp, HcclComm, aclrtStream); | 132 | void *, void *, uint64_t, HcclDataType, HcclReduceOp, HcclComm, aclrtStream); |
| 133 | static HcclAllReduceFunc func = nullptr; | 133 | static HcclAllReduceFunc func = nullptr; |
| 134 | if (func == nullptr) { | 134 | if (func == nullptr) { |
| 135 | func = (HcclAllReduceFunc)TORCH_NPU_GET_FUNC(HcclAllReduce) | 135 | func = (HcclAllReduceFunc)TORCH_NPU_GET_FUNC(HcclAllReduce) |
| 136 | } | 136 | } |
| 137 | TORCH_CHECK(func, "Failed to find function ", "HcclAllReduce", DIST_ERROR(ErrCode::NOT_FOUND)); | 137 | TORCH_CHECK(func, "Failed to find function ", "HcclAllReduce", DIST_ERROR(ErrCode::NOT_FOUND)); |
| 138 | auto ret = func(sendBuf, recvBuf, count, dataType, op, comm, stream); | 138 | auto ret = func(sendBuf, recvBuf, count, dataType, op, comm, stream); |
| 139 | return ret; | 139 | return ret; |
| 140 | } | 140 | } |
| 141 | 141 | ||
| 142 | extern HcclResult hcclBroadcast(void *buf, uint64_t count, HcclDataType dataType, uint32_t root, HcclComm comm, | 142 | extern HcclResult hcclBroadcast(void *buf, uint64_t count, HcclDataType dataType, uint32_t root, HcclComm comm, |
| 143 | aclrtStream stream) | 143 | aclrtStream stream) |
| 144 | { | 144 | { |
| 145 | using HcclBroadcastFunc = HcclResult(*)( | 145 | using HcclBroadcastFunc = HcclResult(*)( |
| 146 | void *, uint64_t, HcclDataType, uint32_t, HcclComm, aclrtStream); | 146 | void *, uint64_t, HcclDataType, uint32_t, HcclComm, aclrtStream); |
| 147 | static HcclBroadcastFunc func = nullptr; | 147 | static HcclBroadcastFunc func = nullptr; |
| 148 | if (func == nullptr) { | 148 | if (func == nullptr) { |
| 149 | func = (HcclBroadcastFunc)TORCH_NPU_GET_FUNC(HcclBroadcast) | 149 | func = (HcclBroadcastFunc)TORCH_NPU_GET_FUNC(HcclBroadcast) |
| 150 | } | 150 | } |
| 151 | TORCH_CHECK(func, "Failed to find function ", "HcclBroadcast", DIST_ERROR(ErrCode::NOT_FOUND)); | 151 | TORCH_CHECK(func, "Failed to find function ", "HcclBroadcast", DIST_ERROR(ErrCode::NOT_FOUND)); |
| 152 | auto ret = func(buf, count, dataType, root, comm, stream); | 152 | auto ret = func(buf, count, dataType, root, comm, stream); |
| 153 | return ret; | 153 | return ret; |
| 154 | } | 154 | } |
| 155 | 155 | ||
| 156 | extern HcclResult hcclCommInitRootInfo(uint32_t nRanks, const HcclRootInfo *rootInfo, uint32_t rank, HcclComm *comm) | 156 | extern HcclResult hcclCommInitRootInfo(uint32_t nRanks, const HcclRootInfo *rootInfo, uint32_t rank, HcclComm *comm) |
| 157 | { | 157 | { |
| 158 | using HcclCommInitRootInfoFunc = HcclResult(*)( | 158 | using HcclCommInitRootInfoFunc = HcclResult(*)( |
| 159 | uint32_t, const HcclRootInfo *, uint32_t, HcclComm *); | 159 | uint32_t, const HcclRootInfo *, uint32_t, HcclComm *); |
| 160 | static HcclCommInitRootInfoFunc func = nullptr; | 160 | static HcclCommInitRootInfoFunc func = nullptr; |
| 161 | if (func == nullptr) { | 161 | if (func == nullptr) { |
| 162 | func = (HcclCommInitRootInfoFunc)TORCH_NPU_GET_FUNC(HcclCommInitRootInfo) | 162 | func = (HcclCommInitRootInfoFunc)TORCH_NPU_GET_FUNC(HcclCommInitRootInfo) |
| 163 | } | 163 | } |
| 164 | TORCH_CHECK(func, "Failed to find function ", "HcclCommInitRootInfo", DIST_ERROR(ErrCode::NOT_FOUND)); | 164 | TORCH_CHECK(func, "Failed to find function ", "HcclCommInitRootInfo", DIST_ERROR(ErrCode::NOT_FOUND)); |
| 165 | auto ret = func(nRanks, rootInfo, rank, comm); | 165 | auto ret = func(nRanks, rootInfo, rank, comm); |
| 166 | return ret; | 166 | return ret; |
| 167 | } | 167 | } |
| 168 | 168 | ||
| 169 | extern HcclResult hcclReduceScatter(void *sendBuf, void *recvBuf, uint64_t recvCount, HcclDataType dataType, | 169 | extern HcclResult hcclReduceScatter(void *sendBuf, void *recvBuf, uint64_t recvCount, HcclDataType dataType, |
| 170 | HcclReduceOp op, HcclComm comm, aclrtStream stream) | 170 | HcclReduceOp op, HcclComm comm, aclrtStream stream) |
| 171 | { | 171 | { |
| 172 | using HcclReduceScatterFunc = HcclResult(*)( | 172 | using HcclReduceScatterFunc = HcclResult(*)( |
| 173 | void *, void *, uint64_t, HcclDataType, HcclReduceOp, HcclComm, aclrtStream); | 173 | void *, void *, uint64_t, HcclDataType, HcclReduceOp, HcclComm, aclrtStream); |
| 174 | static HcclReduceScatterFunc func = nullptr; | 174 | static HcclReduceScatterFunc func = nullptr; |
| 175 | if (func == nullptr) { | 175 | if (func == nullptr) { |
| 176 | func = (HcclReduceScatterFunc)TORCH_NPU_GET_FUNC(HcclReduceScatter); | 176 | func = (HcclReduceScatterFunc)TORCH_NPU_GET_FUNC(HcclReduceScatter); |
| 177 | } | 177 | } |
| 178 | TORCH_CHECK(func, "Failed to find function ", "HcclReduceScatter", DIST_ERROR(ErrCode::NOT_FOUND)); | 178 | TORCH_CHECK(func, "Failed to find function ", "HcclReduceScatter", DIST_ERROR(ErrCode::NOT_FOUND)); |
| 179 | auto ret = func(sendBuf, recvBuf, recvCount, dataType, op, comm, stream); | 179 | auto ret = func(sendBuf, recvBuf, recvCount, dataType, op, comm, stream); |
| 180 | return ret; | 180 | return ret; |
| 181 | } | 181 | } |
| 182 | 182 | ||
| 183 | extern HcclResult hcclAlltoAllV(const void *sendBuf, const void *sendCounts, const void *sdispls, | 183 | extern HcclResult hcclAlltoAllV(const void *sendBuf, const void *sendCounts, const void *sdispls, |
| 184 | HcclDataType sendType, const void *recvBuf, const void *recvCounts, const void *rdispls, | 184 | HcclDataType sendType, const void *recvBuf, const void *recvCounts, const void *rdispls, |
| 185 | HcclDataType recvType, HcclComm comm, aclrtStream stream) | 185 | HcclDataType recvType, HcclComm comm, aclrtStream stream) |
| 186 | { | 186 | { |
| 187 | using HcclAlltoAllVFunc = HcclResult(*)( | 187 | using HcclAlltoAllVFunc = HcclResult(*)( |
| 188 | const void *, const void *, const void *, HcclDataType, | 188 | const void *, const void *, const void *, HcclDataType, |
| 189 | const void *, const void *, const void *, HcclDataType, | 189 | const void *, const void *, const void *, HcclDataType, |
| 190 | HcclComm, aclrtStream); | 190 | HcclComm, aclrtStream); |
| 191 | static HcclAlltoAllVFunc func = nullptr; | 191 | static HcclAlltoAllVFunc func = nullptr; |
| 192 | if (func == nullptr) { | 192 | if (func == nullptr) { |
| 193 | func = (HcclAlltoAllVFunc)TORCH_NPU_GET_FUNC(HcclAlltoAllV); | 193 | func = (HcclAlltoAllVFunc)TORCH_NPU_GET_FUNC(HcclAlltoAllV); |
| 194 | } | 194 | } |
| 195 | TORCH_CHECK(func, "Failed to find function ", "HcclAlltoAllV", DIST_ERROR(ErrCode::NOT_FOUND)); | 195 | TORCH_CHECK(func, "Failed to find function ", "HcclAlltoAllV", DIST_ERROR(ErrCode::NOT_FOUND)); |
| 196 | auto ret = func(sendBuf, sendCounts, sdispls, sendType, | 196 | auto ret = func(sendBuf, sendCounts, sdispls, sendType, |
| 197 | recvBuf, recvCounts, rdispls, recvType, comm, stream); | 197 | recvBuf, recvCounts, rdispls, recvType, comm, stream); |
| 198 | return ret; | 198 | return ret; |
| 199 | } | 199 | } |
| 200 | 200 | ||
| 201 | extern HcclResult hcclAllGatherV(const void *sendBuf, uint64_t sendCount, | 201 | extern HcclResult hcclAllGatherV(const void *sendBuf, uint64_t sendCount, |
| 202 | const void *recvBuf, const void *recvCounts, const void *rdispls, | 202 | const void *recvBuf, const void *recvCounts, const void *rdispls, |
| 203 | HcclDataType dataType, HcclComm comm, aclrtStream stream) | 203 | HcclDataType dataType, HcclComm comm, aclrtStream stream) |
| 204 | { | 204 | { |
| 205 | using HcclAllGatherVFunc = HcclResult(*)( | 205 | using HcclAllGatherVFunc = HcclResult(*)( |
| 206 | const void *, uint64_t, | 206 | const void *, uint64_t, |
| 207 | const void *, const void *, const void *, | 207 | const void *, const void *, const void *, |
| 208 | HcclDataType, HcclComm, aclrtStream); | 208 | HcclDataType, HcclComm, aclrtStream); |
| 209 | static HcclAllGatherVFunc func = nullptr; | 209 | static HcclAllGatherVFunc func = nullptr; |
| 210 | if (func == nullptr) { | 210 | if (func == nullptr) { |
| 211 | func = (HcclAllGatherVFunc)TORCH_NPU_GET_FUNC(HcclAllGatherV); | 211 | func = (HcclAllGatherVFunc)TORCH_NPU_GET_FUNC(HcclAllGatherV); |
| 212 | } | 212 | } |
| 213 | TORCH_CHECK(func, "Failed to find function ", "HcclAllGatherV", DIST_ERROR(ErrCode::NOT_FOUND)); | 213 | TORCH_CHECK(func, "Failed to find function ", "HcclAllGatherV", DIST_ERROR(ErrCode::NOT_FOUND)); |
| 214 | auto ret = func(sendBuf, sendCount, recvBuf, recvCounts, rdispls, dataType, comm, stream); | 214 | auto ret = func(sendBuf, sendCount, recvBuf, recvCounts, rdispls, dataType, comm, stream); |
| 215 | return ret; | 215 | return ret; |
| 216 | } | 216 | } |
| 217 | 217 | ||
| 218 | extern HcclResult hcclReduceScatterV(const void *sendBuf, const void *sendCounts, const void *sdispls, | 218 | extern HcclResult hcclReduceScatterV(const void *sendBuf, const void *sendCounts, const void *sdispls, |
| 219 | const void *recvBuf, uint64_t recvCount, | 219 | const void *recvBuf, uint64_t recvCount, |
| 220 | HcclDataType dataType, HcclReduceOp op, HcclComm comm, aclrtStream stream) | 220 | HcclDataType dataType, HcclReduceOp op, HcclComm comm, aclrtStream stream) |
| 221 | { | 221 | { |
| 222 | using HcclReduceScatterVFunc = HcclResult(*)( | 222 | using HcclReduceScatterVFunc = HcclResult(*)( |
| 223 | const void *, const void *, const void *, | 223 | const void *, const void *, const void *, |
| 224 | const void *, uint64_t, | 224 | const void *, uint64_t, |
| 225 | HcclDataType, HcclReduceOp, HcclComm, aclrtStream); | 225 | HcclDataType, HcclReduceOp, HcclComm, aclrtStream); |
| 226 | static HcclReduceScatterVFunc func = nullptr; | 226 | static HcclReduceScatterVFunc func = nullptr; |
| 227 | if (func == nullptr) { | 227 | if (func == nullptr) { |
| 228 | func = (HcclReduceScatterVFunc)TORCH_NPU_GET_FUNC(HcclReduceScatterV); | 228 | func = (HcclReduceScatterVFunc)TORCH_NPU_GET_FUNC(HcclReduceScatterV); |
| 229 | } | 229 | } |
| 230 | TORCH_CHECK(func, "Failed to find function ", "HcclReduceScatterV", DIST_ERROR(ErrCode::NOT_FOUND)); | 230 | TORCH_CHECK(func, "Failed to find function ", "HcclReduceScatterV", DIST_ERROR(ErrCode::NOT_FOUND)); |
| 231 | auto ret = func(sendBuf, sendCounts, sdispls, recvBuf, recvCount, dataType, op, comm, stream); | 231 | auto ret = func(sendBuf, sendCounts, sdispls, recvBuf, recvCount, dataType, op, comm, stream); |
| 232 | return ret; | 232 | return ret; |
| 233 | } | 233 | } |
| 234 | 234 | ||
| 235 | extern HcclResult hcclReduce(void *sendBuf, void *recvBuf, uint64_t count, HcclDataType sendType, | 235 | extern HcclResult hcclReduce(void *sendBuf, void *recvBuf, uint64_t count, HcclDataType sendType, |
| 236 | HcclReduceOp op, uint32_t root, HcclComm comm, aclrtStream stream) | 236 | HcclReduceOp op, uint32_t root, HcclComm comm, aclrtStream stream) |
| 237 | { | 237 | { |
| 238 | using HcclReduceVFunc = HcclResult(*)( | 238 | using HcclReduceVFunc = HcclResult(*)( |
| 239 | void *, void *, uint64_t, HcclDataType, HcclReduceOp, uint32_t, HcclComm, aclrtStream); | 239 | void *, void *, uint64_t, HcclDataType, HcclReduceOp, uint32_t, HcclComm, aclrtStream); |
| 240 | static HcclReduceVFunc func = nullptr; | 240 | static HcclReduceVFunc func = nullptr; |
| 241 | if (func == nullptr) { | 241 | if (func == nullptr) { |
| 242 | func = (HcclReduceVFunc)TORCH_NPU_GET_FUNC(HcclReduce); | 242 | func = (HcclReduceVFunc)TORCH_NPU_GET_FUNC(HcclReduce); |
| 243 | } | 243 | } |
| 244 | TORCH_CHECK(func, "Failed to find function ", "HcclReduce", DIST_ERROR(ErrCode::NOT_FOUND)); | 244 | TORCH_CHECK(func, "Failed to find function ", "HcclReduce", DIST_ERROR(ErrCode::NOT_FOUND)); |
| 245 | auto ret = func(sendBuf, recvBuf, count, sendType, op, root, comm, stream); | 245 | auto ret = func(sendBuf, recvBuf, count, sendType, op, root, comm, stream); |
| 246 | return ret; | 246 | return ret; |
| 247 | } | 247 | } |
| 248 | 248 | ||
| 249 | HcclResult hcclGetCommAsyncError(HcclComm comm, HcclResult* asyncError) | 249 | HcclResult hcclGetCommAsyncError(HcclComm comm, HcclResult* asyncError) |
| 250 | { | 250 | { |
| 251 | using HcclGetCommAsyncErrorVFunc = HcclResult(*)(HcclComm, HcclResult*); | 251 | using HcclGetCommAsyncErrorVFunc = HcclResult(*)(HcclComm, HcclResult*); |
| 252 | static HcclGetCommAsyncErrorVFunc func = nullptr; | 252 | static HcclGetCommAsyncErrorVFunc func = nullptr; |
| 253 | if (func == nullptr) { | 253 | if (func == nullptr) { |
| 254 | func = (HcclGetCommAsyncErrorVFunc)TORCH_NPU_GET_FUNC(HcclGetCommAsyncError); | 254 | func = (HcclGetCommAsyncErrorVFunc)TORCH_NPU_GET_FUNC(HcclGetCommAsyncError); |
| 255 | } | 255 | } |
| 256 | TORCH_CHECK(func, "Failed to find function ", "HcclGetCommAsyncError", DIST_ERROR(ErrCode::NOT_FOUND)); | 256 | TORCH_CHECK(func, "Failed to find function ", "HcclGetCommAsyncError", DIST_ERROR(ErrCode::NOT_FOUND)); |
| 257 | auto ret = func(comm, asyncError); | 257 | auto ret = func(comm, asyncError); |
| 258 | return ret; | 258 | return ret; |
| 259 | } | 259 | } |
| 260 | 260 | ||
| 261 | HcclResult hcclScatter(void *sendBuf, void *recvBuf, uint64_t count, HcclDataType dataType, uint32_t root, | 261 | HcclResult hcclScatter(void *sendBuf, void *recvBuf, uint64_t count, HcclDataType dataType, uint32_t root, |
| 262 | HcclComm comm, aclrtStream stream) | 262 | HcclComm comm, aclrtStream stream) |
| 263 | { | 263 | { |
| 264 | using HcclScatterVFunc = HcclResult(*)(void *, void *, uint64_t, HcclDataType, uint32_t, HcclComm, aclrtStream); | 264 | using HcclScatterVFunc = HcclResult(*)(void *, void *, uint64_t, HcclDataType, uint32_t, HcclComm, aclrtStream); |
| 265 | static HcclScatterVFunc func = nullptr; | 265 | static HcclScatterVFunc func = nullptr; |
| 266 | if (func == nullptr) { | 266 | if (func == nullptr) { |
| 267 | func = (HcclScatterVFunc)TORCH_NPU_GET_FUNC(HcclScatter); | 267 | func = (HcclScatterVFunc)TORCH_NPU_GET_FUNC(HcclScatter); |
| 268 | } | 268 | } |
| 269 | TORCH_CHECK(func, "Failed to find function ", "HcclScatter", DIST_ERROR(ErrCode::NOT_FOUND)); | 269 | TORCH_CHECK(func, "Failed to find function ", "HcclScatter", DIST_ERROR(ErrCode::NOT_FOUND)); |
| 270 | auto ret = func(sendBuf, recvBuf, count, dataType, root, comm, stream); | 270 | auto ret = func(sendBuf, recvBuf, count, dataType, root, comm, stream); |
| 271 | return ret; | 271 | return ret; |
| 272 | } | 272 | } |
| 273 | 273 | ||
| 274 | HcclResult hcclBatchIsendIrecv(void* sendRecvInfo, uint32_t itemNum, HcclComm comm, aclrtStream stream) | 274 | HcclResult hcclBatchIsendIrecv(void* sendRecvInfo, uint32_t itemNum, HcclComm comm, aclrtStream stream) |
| 275 | { | 275 | { |
| 276 | using HcclBatchIsendIrecvVFunc = HcclResult(*)( | 276 | using HcclBatchIsendIrecvVFunc = HcclResult(*)( |
| 277 | void *, uint32_t, HcclComm, aclrtStream); | 277 | void *, uint32_t, HcclComm, aclrtStream); |
| 278 | static HcclBatchIsendIrecvVFunc func = nullptr; | 278 | static HcclBatchIsendIrecvVFunc func = nullptr; |
| 279 | if (func == nullptr) { | 279 | if (func == nullptr) { |
| 280 | func = (HcclBatchIsendIrecvVFunc)TORCH_NPU_GET_FUNC(HcclBatchSendRecv); | 280 | func = (HcclBatchIsendIrecvVFunc)TORCH_NPU_GET_FUNC(HcclBatchSendRecv); |
| 281 | } | 281 | } |
| 282 | TORCH_CHECK(func, "Failed to find function ", "HcclBatchSendRecv", DIST_ERROR(ErrCode::NOT_FOUND)); | 282 | TORCH_CHECK(func, "Failed to find function ", "HcclBatchSendRecv", DIST_ERROR(ErrCode::NOT_FOUND)); |
| 283 | auto ret = func(sendRecvInfo, itemNum, comm, stream); | 283 | auto ret = func(sendRecvInfo, itemNum, comm, stream); |
| 284 | return ret; | 284 | return ret; |
| 285 | } | 285 | } |
| 286 | 286 | ||
| 287 | HcclResult hcclAlltoAll(const void *sendBuf, uint64_t sendCount, HcclDataType sendType, | 287 | HcclResult hcclAlltoAll(const void *sendBuf, uint64_t sendCount, HcclDataType sendType, |
| 288 | const void *recvBuf, uint64_t recvCount, HcclDataType recvType, | 288 | const void *recvBuf, uint64_t recvCount, HcclDataType recvType, |
| 289 | HcclComm comm, aclrtStream stream) | 289 | HcclComm comm, aclrtStream stream) |
| 290 | { | 290 | { |
| 291 | using HcclAlltoAllFunc = HcclResult(*)( | 291 | using HcclAlltoAllFunc = HcclResult(*)( |
| 292 | const void *, uint64_t, HcclDataType, | 292 | const void *, uint64_t, HcclDataType, |
| 293 | const void *, uint64_t, HcclDataType, | 293 | const void *, uint64_t, HcclDataType, |
| 294 | HcclComm, aclrtStream); | 294 | HcclComm, aclrtStream); |
| 295 | static HcclAlltoAllFunc func = nullptr; | 295 | static HcclAlltoAllFunc func = nullptr; |
| 296 | if (func == nullptr) { | 296 | if (func == nullptr) { |
| 297 | func = (HcclAlltoAllFunc)TORCH_NPU_GET_FUNC(HcclAlltoAll); | 297 | func = (HcclAlltoAllFunc)TORCH_NPU_GET_FUNC(HcclAlltoAll); |
| 298 | } | 298 | } |
| 299 | TORCH_CHECK(func, "Failed to find function ", "HcclAlltoAll", DIST_ERROR(ErrCode::NOT_FOUND)); | 299 | TORCH_CHECK(func, "Failed to find function ", "HcclAlltoAll", DIST_ERROR(ErrCode::NOT_FOUND)); |
| 300 | auto ret = func(sendBuf, sendCount, sendType, | 300 | auto ret = func(sendBuf, sendCount, sendType, |
| 301 | recvBuf, recvCount, recvType, comm, stream); | 301 | recvBuf, recvCount, recvType, comm, stream); |
| 302 | return ret; | 302 | return ret; |
| 303 | } | 303 | } |
| 304 | 304 | ||
| 305 | bool hcclCommInitRootInfoConfigExist() | 305 | bool hcclCommInitRootInfoConfigExist() |
| 306 | { | 306 | { |
| 307 | static c10::once_flag flag; | 307 | static c10::once_flag flag; |
| 308 | static bool exist = false; | 308 | static bool exist = false; |
| 309 | c10::call_once(flag, [&]() { | 309 | c10::call_once(flag, [&]() { |
| 310 | auto func = TORCH_NPU_GET_FUNC(HcclCommInitRootInfoConfig) | 310 | auto func = TORCH_NPU_GET_FUNC(HcclCommInitRootInfoConfig) |
| 311 | if (func != nullptr) { | 311 | if (func != nullptr) { |
| 312 | exist = true; | 312 | exist = true; |
| 313 | } | 313 | } |
| 314 | }); | 314 | }); |
| 315 | return exist; | 315 | return exist; |
| 316 | } | 316 | } |
| 317 | 317 | ||
| 318 | bool hcclAllGatherVExist() | 318 | bool hcclAllGatherVExist() |
| 319 | { | 319 | { |
| 320 | static c10::once_flag flag; | 320 | static c10::once_flag flag; |
| 321 | static bool exist = false; | 321 | static bool exist = false; |
| 322 | c10::call_once(flag, [&]() { | 322 | c10::call_once(flag, [&]() { |
| 323 | auto func = TORCH_NPU_GET_FUNC(HcclAllGatherV) | 323 | auto func = TORCH_NPU_GET_FUNC(HcclAllGatherV) |
| 324 | if (func != nullptr && | 324 | if (func != nullptr && |
| 325 | c10_npu::GetSocVersion() >= c10_npu::SocVersion::Ascend310P1 && | 325 | c10_npu::GetSocVersion() >= c10_npu::SocVersion::Ascend310P1 && |
| 326 | c10_npu::GetSocVersion() < c10_npu::SocVersion::Ascend310B1) { | 326 | c10_npu::GetSocVersion() < c10_npu::SocVersion::Ascend310B1) { |
| 327 | exist = true; | 327 | exist = true; |
| 328 | } | 328 | } |
| 329 | }); | 329 | }); |
| 330 | return exist; | 330 | return exist; |
| 331 | } | 331 | } |
| 332 | 332 | ||
| 333 | bool hcclReduceScatterVExist() | 333 | bool hcclReduceScatterVExist() |
| 334 | { | 334 | { |
| 335 | static c10::once_flag flag; | 335 | static c10::once_flag flag; |
| 336 | static bool exist = false; | 336 | static bool exist = false; |
| 337 | c10::call_once(flag, [&]() { | 337 | c10::call_once(flag, [&]() { |
| 338 | auto func = TORCH_NPU_GET_FUNC(HcclReduceScatterV) | 338 | auto func = TORCH_NPU_GET_FUNC(HcclReduceScatterV) |
| 339 | if (func != nullptr && | 339 | if (func != nullptr && |
| 340 | ((c10_npu::GetSocVersion() >= c10_npu::SocVersion::Ascend310P1 && | 340 | ((c10_npu::GetSocVersion() >= c10_npu::SocVersion::Ascend310P1 && |
| 341 | c10_npu::GetSocVersion() < c10_npu::SocVersion::Ascend310B1) || | 341 | c10_npu::GetSocVersion() < c10_npu::SocVersion::Ascend310B1) || |
| 342 | c10_npu::GetSocVersion() == c10_npu::SocVersion::Ascend950)) { | 342 | c10_npu::GetSocVersion() == c10_npu::SocVersion::Ascend950)) { |
| 343 | exist = true; | 343 | exist = true; |
| 344 | } | 344 | } |
| 345 | }); | 345 | }); |
| 346 | return exist; | 346 | return exist; |
| 347 | } | 347 | } |
| 348 | 348 | ||
| 349 | HcclResult hcclCommInitRootInfoConfig(uint32_t nRanks, const HcclRootInfo *rootInfo, uint32_t rank, HcclCommConfig* config, HcclComm *comm) | 349 | HcclResult hcclCommInitRootInfoConfig(uint32_t nRanks, const HcclRootInfo *rootInfo, uint32_t rank, HcclCommConfig* config, HcclComm *comm) |
| 350 | { | 350 | { |
| 351 | using HcclCommInitRootInfoConfigFunc = HcclResult(*)( | 351 | using HcclCommInitRootInfoConfigFunc = HcclResult(*)( |
| 352 | uint32_t, const HcclRootInfo *, uint32_t, HcclCommConfig*, HcclComm *); | 352 | uint32_t, const HcclRootInfo *, uint32_t, HcclCommConfig*, HcclComm *); |
| 353 | static HcclCommInitRootInfoConfigFunc func = nullptr; | 353 | static HcclCommInitRootInfoConfigFunc func = nullptr; |
| 354 | if (func == nullptr) { | 354 | if (func == nullptr) { |
| 355 | func = (HcclCommInitRootInfoConfigFunc)TORCH_NPU_GET_FUNC(HcclCommInitRootInfoConfig) | 355 | func = (HcclCommInitRootInfoConfigFunc)TORCH_NPU_GET_FUNC(HcclCommInitRootInfoConfig) |
| 356 | } | 356 | } |
| 357 | TORCH_CHECK(func, "Failed to find function ", "HcclCommInitRootInfoConfig", DIST_ERROR(ErrCode::NOT_FOUND)); | 357 | TORCH_CHECK(func, "Failed to find function ", "HcclCommInitRootInfoConfig", DIST_ERROR(ErrCode::NOT_FOUND)); |
| 358 | auto ret = func(nRanks, rootInfo, rank, config, comm); | 358 | auto ret = func(nRanks, rootInfo, rank, config, comm); |
| 359 | return ret; | 359 | return ret; |
| 360 | } | 360 | } |
| 361 | 361 | ||
| 362 | bool isHcclFeatureSupported(HcclCommConfigCapability configParameter) | 362 | bool isHcclFeatureSupported(HcclCommConfigCapability configParameter) |
| 363 | { | 363 | { |
| 364 | using HcclGetCommConfigCapabilityFunc = uint32_t(*)(); | 364 | using HcclGetCommConfigCapabilityFunc = uint32_t(*)(); |
| 365 | static HcclGetCommConfigCapabilityFunc func = (HcclGetCommConfigCapabilityFunc) TORCH_NPU_GET_FUNC( | 365 | static HcclGetCommConfigCapabilityFunc func = (HcclGetCommConfigCapabilityFunc) TORCH_NPU_GET_FUNC( |
| 366 | HcclGetCommConfigCapability); | 366 | HcclGetCommConfigCapability); |
| 367 | if (func == nullptr) { | 367 | if (func == nullptr) { |
| 368 | return false; | 368 | return false; |
| 369 | } | 369 | } |
| 370 | return configParameter < func(); | 370 | return configParameter < func(); |
| 371 | } | 371 | } |
| 372 | 372 | ||
| 373 | bool hcclCommInitClusterInfoConfigExist() | 373 | bool hcclCommInitClusterInfoConfigExist() |
| 374 | { | 374 | { |
| 375 | const static bool isClusterInitExist = []() -> bool { | 375 | const static bool isClusterInitExist = []() -> bool { |
| 376 | auto func = TORCH_NPU_GET_FUNC(HcclCommInitClusterInfoConfig) | 376 | auto func = TORCH_NPU_GET_FUNC(HcclCommInitClusterInfoConfig) |
| 377 | return func != nullptr; | 377 | return func != nullptr; |
| 378 | }(); | 378 | }(); |
| 379 | return isClusterInitExist; | 379 | return isClusterInitExist; |
| 380 | } | 380 | } |
| 381 | 381 | ||
| 382 | HcclResult hcclCommInitClusterInfoConfig(const char *clusterInfo, uint32_t rank, HcclCommConfig *config, HcclComm *comm) | 382 | HcclResult hcclCommInitClusterInfoConfig(const char *clusterInfo, uint32_t rank, HcclCommConfig *config, HcclComm *comm) |
| 383 | { | 383 | { |
| 384 | using HcclCommInitClusterInfoConfigFunc = HcclResult(*)(const char *, uint32_t, HcclCommConfig *, HcclComm *); | 384 | using HcclCommInitClusterInfoConfigFunc = HcclResult(*)(const char *, uint32_t, HcclCommConfig *, HcclComm *); |
| 385 | static HcclCommInitClusterInfoConfigFunc func = nullptr; | 385 | static HcclCommInitClusterInfoConfigFunc func = nullptr; |
| 386 | if (func == nullptr) { | 386 | if (func == nullptr) { |
| 387 | func = (HcclCommInitClusterInfoConfigFunc)TORCH_NPU_GET_FUNC(HcclCommInitClusterInfoConfig) | 387 | func = (HcclCommInitClusterInfoConfigFunc)TORCH_NPU_GET_FUNC(HcclCommInitClusterInfoConfig) |
| 388 | } | 388 | } |
| 389 | TORCH_CHECK(func, "Failed to find function ", "HcclCommInitClusterInfoConfig", DIST_ERROR(ErrCode::NOT_FOUND)); | 389 | TORCH_CHECK(func, "Failed to find function ", "HcclCommInitClusterInfoConfig", DIST_ERROR(ErrCode::NOT_FOUND)); |
| 390 | auto ret = func(clusterInfo, rank, config, comm); | 390 | auto ret = func(clusterInfo, rank, config, comm); |
| 391 | return ret; | 391 | return ret; |
| 392 | } | 392 | } |
| 393 | 393 | ||
| 394 | bool hcclCreateSubCommConfigExist() | 394 | bool hcclCreateSubCommConfigExist() |
| 395 | { | 395 | { |
| 396 | const static bool isCreateSubCommExist = []() -> bool { | 396 | const static bool isCreateSubCommExist = []() -> bool { |
| 397 | auto func = TORCH_NPU_GET_FUNC(HcclCreateSubCommConfig) | 397 | auto func = TORCH_NPU_GET_FUNC(HcclCreateSubCommConfig) |
| 398 | return func != nullptr; | 398 | return func != nullptr; |
| 399 | }(); | 399 | }(); |
| 400 | return isCreateSubCommExist; | 400 | return isCreateSubCommExist; |
| 401 | } | 401 | } |
| 402 | 402 | ||
| 403 | HcclResult hcclCreateSubCommConfig(HcclComm *comm, uint32_t rankNum, uint32_t *rankIds, uint64_t subCommId, uint32_t subCommRankId, | 403 | HcclResult hcclCreateSubCommConfig(HcclComm *comm, uint32_t rankNum, uint32_t *rankIds, uint64_t subCommId, uint32_t subCommRankId, |
| 404 | HcclCommConfig* config, HcclComm *subComm) | 404 | HcclCommConfig* config, HcclComm *subComm) |
| 405 | { | 405 | { |
| 406 | using HcclCreateSubCommConfigFunc = HcclResult(*)(HcclComm *, uint32_t, uint32_t *, uint64_t, uint32_t, HcclCommConfig *, HcclComm *); | 406 | using HcclCreateSubCommConfigFunc = HcclResult(*)(HcclComm *, uint32_t, uint32_t *, uint64_t, uint32_t, HcclCommConfig *, HcclComm *); |
| 407 | static HcclCreateSubCommConfigFunc func = nullptr; | 407 | static HcclCreateSubCommConfigFunc func = nullptr; |
| 408 | if (func == nullptr) { | 408 | if (func == nullptr) { |
| 409 | func = (HcclCreateSubCommConfigFunc)TORCH_NPU_GET_FUNC(HcclCreateSubCommConfig) | 409 | func = (HcclCreateSubCommConfigFunc)TORCH_NPU_GET_FUNC(HcclCreateSubCommConfig) |
| 410 | } | 410 | } |
| 411 | TORCH_CHECK(func, "Failed to find function ", "HcclCreateSubCommConfig", DIST_ERROR(ErrCode::NOT_FOUND)); | 411 | TORCH_CHECK(func, "Failed to find function ", "HcclCreateSubCommConfig", DIST_ERROR(ErrCode::NOT_FOUND)); |
| 412 | auto ret = func(comm, rankNum, rankIds, subCommId, subCommRankId, config, subComm); | 412 | auto ret = func(comm, rankNum, rankIds, subCommId, subCommRankId, config, subComm); |
| 413 | return ret; | 413 | return ret; |
| 414 | } | 414 | } |
| 415 | 415 | ||
| 416 | bool hcclCommWorkingDevNicSetExist() | 416 | bool hcclCommWorkingDevNicSetExist() |
| 417 | { | 417 | { |
| 418 | const static bool isHcclCommWorkingDevNicSetExist = []() -> bool { | 418 | const static bool isHcclCommWorkingDevNicSetExist = []() -> bool { |
| 419 | auto func = TORCH_NPU_GET_FUNC(HcclCommWorkingDevNicSet) | 419 | auto func = TORCH_NPU_GET_FUNC(HcclCommWorkingDevNicSet) |
| 420 | return func != nullptr; | 420 | return func != nullptr; |
| 421 | }(); | 421 | }(); |
| 422 | return isHcclCommWorkingDevNicSetExist; | 422 | return isHcclCommWorkingDevNicSetExist; |
| 423 | } | 423 | } |
| 424 | 424 | ||
| 425 | HcclResult hcclCommWorkingDevNicSet(HcclComm comm, uint32_t *ranks, bool *useBackup, uint32_t nRanks) | 425 | HcclResult hcclCommWorkingDevNicSet(HcclComm comm, uint32_t *ranks, bool *useBackup, uint32_t nRanks) |
| 426 | { | 426 | { |
| 427 | using HcclCommWorkingDevNicSetFunc = HcclResult(*)(HcclComm, uint32_t *, bool *, uint32_t); | 427 | using HcclCommWorkingDevNicSetFunc = HcclResult(*)(HcclComm, uint32_t *, bool *, uint32_t); |
| 428 | static HcclCommWorkingDevNicSetFunc func = nullptr; | 428 | static HcclCommWorkingDevNicSetFunc func = nullptr; |
| 429 | if (func == nullptr) { | 429 | if (func == nullptr) { |
| 430 | func = (HcclCommWorkingDevNicSetFunc)TORCH_NPU_GET_FUNC(HcclCommWorkingDevNicSet) | 430 | func = (HcclCommWorkingDevNicSetFunc)TORCH_NPU_GET_FUNC(HcclCommWorkingDevNicSet) |
| 431 | } | 431 | } |
| 432 | TORCH_CHECK(func, "Failed to find function ", "HcclCommWorkingDevNicSet", DIST_ERROR(ErrCode::NOT_FOUND)); | 432 | TORCH_CHECK(func, "Failed to find function ", "HcclCommWorkingDevNicSet", DIST_ERROR(ErrCode::NOT_FOUND)); |
| 433 | auto ret = func(comm, ranks, useBackup, nRanks); | 433 | auto ret = func(comm, ranks, useBackup, nRanks); |
| 434 | return ret; | 434 | return ret; |
| 435 | } | 435 | } |
| 436 | 436 | ||
| 437 | HcclResult hcclCommRegister(HcclComm comm, void *addr, uint64_t size, void **handle, uint32_t flag) | 437 | HcclResult hcclCommRegister(HcclComm comm, void *addr, uint64_t size, void **handle, uint32_t flag) |
| 438 | { | 438 | { |
| 439 | using HcclCommRegisterFunc = HcclResult(*)(HcclComm, void *, uint64_t, void **, uint32_t); | 439 | using HcclCommRegisterFunc = HcclResult(*)(HcclComm, void *, uint64_t, void **, uint32_t); |
| 440 | static HcclCommRegisterFunc func = nullptr; | 440 | static HcclCommRegisterFunc func = nullptr; |
| 441 | if (func == nullptr) { | 441 | if (func == nullptr) { |
| 442 | func = (HcclCommRegisterFunc)TORCH_NPU_GET_FUNC(HcclCommRegister) | 442 | func = (HcclCommRegisterFunc)TORCH_NPU_GET_FUNC(HcclCommRegister) |
| 443 | } | 443 | } |
| 444 | TORCH_CHECK(func, "Failed to find function ", "HcclCommRegister", DIST_ERROR(ErrCode::NOT_FOUND)); | 444 | TORCH_CHECK(func, "Failed to find function ", "HcclCommRegister", DIST_ERROR(ErrCode::NOT_FOUND)); |
| 445 | auto ret = func(comm, addr, size, handle, flag); | 445 | auto ret = func(comm, addr, size, handle, flag); |
| 446 | return ret; | 446 | return ret; |
| 447 | } | 447 | } |
| 448 | 448 | ||
| 449 | HcclResult hcclCommDeregister(HcclComm comm, void *handle) | 449 | HcclResult hcclCommDeregister(HcclComm comm, void *handle) |
| 450 | { | 450 | { |
| 451 | using HcclCommDeregisterFunc = HcclResult(*)(HcclComm, void *); | 451 | using HcclCommDeregisterFunc = HcclResult(*)(HcclComm, void *); |
| 452 | static HcclCommDeregisterFunc func = nullptr; | 452 | static HcclCommDeregisterFunc func = nullptr; |
| 453 | if (func == nullptr) { | 453 | if (func == nullptr) { |
| 454 | func = (HcclCommDeregisterFunc)TORCH_NPU_GET_FUNC(HcclCommDeregister) | 454 | func = (HcclCommDeregisterFunc)TORCH_NPU_GET_FUNC(HcclCommDeregister) |
| 455 | } | 455 | } |
| 456 | TORCH_CHECK(func, "Failed to find function ", "HcclCommDeregister", DIST_ERROR(ErrCode::NOT_FOUND)); | 456 | TORCH_CHECK(func, "Failed to find function ", "HcclCommDeregister", DIST_ERROR(ErrCode::NOT_FOUND)); |
| 457 | auto ret = func(comm, handle); | 457 | auto ret = func(comm, handle); |
| 458 | return ret; | 458 | return ret; |
| 459 | } | 459 | } |
| 460 | 460 | ||
| 461 | HcclResult hcclCommExchangeMem(HcclComm comm, void *windowHandle, uint32_t *peerRanks, uint32_t peerRankNum) | 461 | HcclResult hcclCommExchangeMem(HcclComm comm, void *windowHandle, uint32_t *peerRanks, uint32_t peerRankNum) |
| 462 | { | 462 | { |
| 463 | using HcclCommExchangeMemFunc = HcclResult(*)(HcclComm, void *, uint32_t *, uint32_t); | 463 | using HcclCommExchangeMemFunc = HcclResult(*)(HcclComm, void *, uint32_t *, uint32_t); |
| 464 | static HcclCommExchangeMemFunc func = nullptr; | 464 | static HcclCommExchangeMemFunc func = nullptr; |
| 465 | if (func == nullptr) { | 465 | if (func == nullptr) { |
| 466 | func = (HcclCommExchangeMemFunc)TORCH_NPU_GET_FUNC(HcclCommExchangeMem) | 466 | func = (HcclCommExchangeMemFunc)TORCH_NPU_GET_FUNC(HcclCommExchangeMem) |
| 467 | } | 467 | } |
| 468 | TORCH_CHECK(func, "Failed to find function ", "HcclCommExchangeMem", DIST_ERROR(ErrCode::NOT_FOUND)); | 468 | TORCH_CHECK(func, "Failed to find function ", "HcclCommExchangeMem", DIST_ERROR(ErrCode::NOT_FOUND)); |
| 469 | auto ret = func(comm, windowHandle, peerRanks, peerRankNum); | 469 | auto ret = func(comm, windowHandle, peerRanks, peerRankNum); |
| 470 | return ret; | 470 | return ret; |
| 471 | } | 471 | } |
| 472 | 472 | ||
| 473 | HcclResult hcclGroupStart() | 473 | HcclResult hcclGroupStart() |
| 474 | { | 474 | { |
| 475 | using hcclGroupStartFunc = HcclResult(*)(); | 475 | using hcclGroupStartFunc = HcclResult(*)(); |
| 476 | static hcclGroupStartFunc func = nullptr; | 476 | static hcclGroupStartFunc func = nullptr; |
| 477 | if (func == nullptr) { | 477 | if (func == nullptr) { |
| 478 | func = (hcclGroupStartFunc)TORCH_NPU_GET_FUNCTION(libhcomm, HcclGroupStart) | 478 | func = (hcclGroupStartFunc)TORCH_NPU_GET_FUNCTION(libhcomm, HcclGroupStart) |
| 479 | } | 479 | } |
| 480 | TORCH_CHECK(func, "Failed to find function ", "HcclGroupStart", DIST_ERROR(ErrCode::NOT_FOUND)); | 480 | TORCH_CHECK(func, "Failed to find function ", "HcclGroupStart", DIST_ERROR(ErrCode::NOT_FOUND)); |
| 481 | auto ret = func(); | 481 | auto ret = func(); |
| 482 | return ret; | 482 | return ret; |
| 483 | } | 483 | } |
| 484 | 484 | ||
| 485 | HcclResult hcclGroupEnd() | 485 | HcclResult hcclGroupEnd() |
| 486 | { | 486 | { |
| 487 | using hcclGroupEndFunc = HcclResult(*)(); | 487 | using hcclGroupEndFunc = HcclResult(*)(); |
| 488 | static hcclGroupEndFunc func = nullptr; | 488 | static hcclGroupEndFunc func = nullptr; |
| 489 | if (func == nullptr) { | 489 | if (func == nullptr) { |
| 490 | func = (hcclGroupEndFunc)TORCH_NPU_GET_FUNCTION(libhcomm, HcclGroupEnd) | 490 | func = (hcclGroupEndFunc)TORCH_NPU_GET_FUNCTION(libhcomm, HcclGroupEnd) |
| 491 | } | 491 | } |
| 492 | TORCH_CHECK(func, "Failed to find function ", "HcclGroupEnd", DIST_ERROR(ErrCode::NOT_FOUND)); | 492 | TORCH_CHECK(func, "Failed to find function ", "HcclGroupEnd", DIST_ERROR(ErrCode::NOT_FOUND)); |
| 493 | auto ret = func(); | 493 | auto ret = func(); |
| 494 | return ret; | 494 | return ret; |
| 495 | } | 495 | } |
| 496 | } // namespace c10d_npu | 496 | } // namespace c10d_npu |
| @@ -1,614 +1,614 @@ | |||
| 1 | #include <deque> | 1 | #include <deque> |
| 2 | 2 | ||
| 3 | #include <torch/custom_class.h> | 3 | #include <torch/custom_class.h> |
| 4 | #include <torch/csrc/python_headers.h> | 4 | #include <torch/csrc/python_headers.h> |
| 5 | #include <c10/util/intrusive_ptr.h> | 5 | #include <c10/util/intrusive_ptr.h> |
| 6 | #include <c10/util/irange.h> | 6 | #include <c10/util/irange.h> |
| 7 | #include <c10d/ProcessGroup.hpp> | 7 | #include <c10d/ProcessGroup.hpp> |
| 8 | #include <c10d/comm.hpp> | 8 | #include <c10d/comm.hpp> |
| 9 | #include <c10d/Work.hpp> | 9 | #include <c10d/Work.hpp> |
| 10 | #include <pybind11/chrono.h> | 10 | #include <pybind11/chrono.h> |
| 11 | 11 | ||
| 12 | #include <torch/csrc/Exceptions.h> | 12 | #include <torch/csrc/Exceptions.h> |
| 13 | #include <ATen/core/functional.h> | 13 | #include <ATen/core/functional.h> |
| 14 | #include <torch/csrc/jit/python/pybind_utils.h> | 14 | #include <torch/csrc/jit/python/pybind_utils.h> |
| 15 | #include <torch/csrc/utils/object_ptr.h> | 15 | #include <torch/csrc/utils/object_ptr.h> |
| 16 | #include <torch/csrc/utils/pybind.h> | 16 | #include <torch/csrc/utils/pybind.h> |
| 17 | #include <torch/csrc/utils/tensor_flatten.h> | 17 | #include <torch/csrc/utils/tensor_flatten.h> |
| 18 | #include <torch/csrc/distributed/c10d/python_comm_hook.h> | 18 | #include <torch/csrc/distributed/c10d/python_comm_hook.h> |
| 19 | 19 | ||
| 20 | #include "torch_npu/csrc/distributed/rpc/init.h" | 20 | #include "torch_npu/csrc/distributed/rpc/init.h" |
| 21 | #include "torch_npu/csrc/distributed/ProcessGroupHCCL.hpp" | 21 | #include "torch_npu/csrc/distributed/ProcessGroupHCCL.hpp" |
| 22 | #include "torch_npu/csrc/distributed/ProcessGroupLCCL.hpp" | 22 | #include "torch_npu/csrc/distributed/ProcessGroupLCCL.hpp" |
| 23 | #include "torch_npu/csrc/distributed/reducer.hpp" | 23 | #include "torch_npu/csrc/distributed/reducer.hpp" |
| 24 | #include "torch_npu/csrc/distributed/ParallelTcpStore.hpp" | 24 | #include "torch_npu/csrc/distributed/ParallelTcpStore.hpp" |
| 25 | #include "torch_npu/csrc/aten/NPUNativeFunctions.h" | 25 | #include "torch_npu/csrc/aten/NPUNativeFunctions.h" |
| 26 | #include "torch_npu/csrc/aten/CustomFunctions.h" | 26 | #include "torch_npu/csrc/aten/CustomFunctions.h" |
| 27 | #include "torch_npu/csrc/core/NPUBridge.h" | 27 | #include "torch_npu/csrc/core/NPUBridge.h" |
| 28 | #include "torch_npu/csrc/distributed/Init.h" | 28 | #include "torch_npu/csrc/distributed/Init.h" |
| 29 | 29 | ||
| 30 | 30 | ||
| 31 | namespace { | 31 | namespace { |
| 32 | 32 | ||
| 33 | // Wrapper to ensure GIL is released before destructing ProcessGroupGloo | 33 | // Wrapper to ensure GIL is released before destructing ProcessGroupGloo |
| 34 | template <typename T> | 34 | template <typename T> |
| 35 | class IntrusivePtrNoGilDestructor { | 35 | class IntrusivePtrNoGilDestructor { |
| 36 | c10::intrusive_ptr<T> impl_; | 36 | c10::intrusive_ptr<T> impl_; |
| 37 | 37 | ||
| 38 | public: | 38 | public: |
| 39 | IntrusivePtrNoGilDestructor() = default; | 39 | IntrusivePtrNoGilDestructor() = default; |
| 40 | IntrusivePtrNoGilDestructor(const IntrusivePtrNoGilDestructor&) = default; | 40 | IntrusivePtrNoGilDestructor(const IntrusivePtrNoGilDestructor&) = default; |
| 41 | IntrusivePtrNoGilDestructor(IntrusivePtrNoGilDestructor&&) = default; | 41 | IntrusivePtrNoGilDestructor(IntrusivePtrNoGilDestructor&&) = default; |
| 42 | IntrusivePtrNoGilDestructor& operator=(const IntrusivePtrNoGilDestructor&) = | 42 | IntrusivePtrNoGilDestructor& operator=(const IntrusivePtrNoGilDestructor&) = |
| 43 | default; | 43 | default; |
| 44 | IntrusivePtrNoGilDestructor& operator=(IntrusivePtrNoGilDestructor&&) = | 44 | IntrusivePtrNoGilDestructor& operator=(IntrusivePtrNoGilDestructor&&) = |
| 45 | default; | 45 | default; |
| 46 | IntrusivePtrNoGilDestructor(c10::intrusive_ptr<T> impl) | 46 | IntrusivePtrNoGilDestructor(c10::intrusive_ptr<T> impl) |
| 47 | : impl_(std::move(impl)) {} | 47 | : impl_(std::move(impl)) {} |
| 48 | explicit IntrusivePtrNoGilDestructor(T* impl) | 48 | explicit IntrusivePtrNoGilDestructor(T* impl) |
| 49 | : impl_(c10::intrusive_ptr<T>::unsafe_steal_from_new(impl)) {} | 49 | : impl_(c10::intrusive_ptr<T>::unsafe_steal_from_new(impl)) {} |
| 50 | ~IntrusivePtrNoGilDestructor() { | 50 | ~IntrusivePtrNoGilDestructor() { |
| 51 | if (impl_) { | 51 | if (impl_) { |
| 52 | if (PyGILState_Check() != 0) { | 52 | if (PyGILState_Check() != 0) { |
| 53 | pybind11::gil_scoped_release release; | 53 | pybind11::gil_scoped_release release; |
| 54 | impl_.reset(); | 54 | impl_.reset(); |
| 55 | } else { | 55 | } else { |
| 56 | impl_.reset(); | 56 | impl_.reset(); |
| 57 | } | 57 | } |
| 58 | } | 58 | } |
| 59 | } | 59 | } |
| 60 | T& operator*() const noexcept { | 60 | T& operator*() const noexcept { |
| 61 | return *impl_; | 61 | return *impl_; |
| 62 | } | 62 | } |
| 63 | T* operator->() const noexcept { | 63 | T* operator->() const noexcept { |
| 64 | return impl_.get(); | 64 | return impl_.get(); |
| 65 | } | 65 | } |
| 66 | C10_NODISCARD T* get() const noexcept { | 66 | C10_NODISCARD T* get() const noexcept { |
| 67 | return impl_.get(); | 67 | return impl_.get(); |
| 68 | } | 68 | } |
| 69 | void reset() noexcept { | 69 | void reset() noexcept { |
| 70 | impl_.reset(); | 70 | impl_.reset(); |
| 71 | } | 71 | } |
| 72 | operator bool() const noexcept { | 72 | operator bool() const noexcept { |
| 73 | return impl_; | 73 | return impl_; |
| 74 | } | 74 | } |
| 75 | }; | 75 | }; |
| 76 | 76 | ||
| 77 | } // anonymous namespace | 77 | } // anonymous namespace |
| 78 | 78 | ||
| 79 | PYBIND11_DECLARE_HOLDER_TYPE(T, IntrusivePtrNoGilDestructor<T>, true); | 79 | PYBIND11_DECLARE_HOLDER_TYPE(T, IntrusivePtrNoGilDestructor<T>, true); |
| 80 | 80 | ||
| 81 | 81 | ||
| 82 | namespace torch_npu { | 82 | namespace torch_npu { |
| 83 | namespace distributed { | 83 | namespace distributed { |
| 84 | 84 | ||
| 85 | template <typename T> | 85 | template <typename T> |
| 86 | using shared_ptr_class_ = py::class_<T, std::shared_ptr<T>>; | 86 | using shared_ptr_class_ = py::class_<T, std::shared_ptr<T>>; |
| 87 | 87 | ||
| 88 | template <typename T> | 88 | template <typename T> |
| 89 | using intrusive_ptr_class_ = py::class_<T, c10::intrusive_ptr<T>>; | 89 | using intrusive_ptr_class_ = py::class_<T, c10::intrusive_ptr<T>>; |
| 90 | 90 | ||
| 91 | template <typename T> | 91 | template <typename T> |
| 92 | using intrusive_ptr_no_gil_destructor_class_ = | 92 | using intrusive_ptr_no_gil_destructor_class_ = |
| 93 | py::class_<T, IntrusivePtrNoGilDestructor<T>>; | 93 | py::class_<T, IntrusivePtrNoGilDestructor<T>>; |
| 94 | 94 | ||
| 95 | 95 | ||
| 96 | class BroadcastWork { | 96 | class BroadcastWork { |
| 97 | public: | 97 | public: |
| 98 | inline std::vector<at::Tensor> cast_tensors(at::TensorList tensors) const | 98 | inline std::vector<at::Tensor> cast_tensors(at::TensorList tensors) const |
| 99 | { | 99 | { |
| 100 | static auto cast_back_to_ori_format = [](const at::Tensor &t) { | 100 | static auto cast_back_to_ori_format = [](const at::Tensor &t) { |
| 101 | return at_npu::native::custom_ops::npu_format_cast(t, torch_npu::NPUBridge::GetNpuStorageImpl(t)->npu_desc_.origin_format_); | 101 | return at_npu::native::custom_ops::npu_format_cast(t, torch_npu::NPUBridge::GetNpuStorageImpl(t)->npu_desc_.origin_format_); |
| 102 | }; | 102 | }; |
| 103 | return c10::fmap(tensors, cast_back_to_ori_format); | 103 | return c10::fmap(tensors, cast_back_to_ori_format); |
| 104 | } | 104 | } |
| 105 | 105 | ||
| 106 | BroadcastWork( | 106 | BroadcastWork( |
| 107 | const c10::intrusive_ptr<c10d::ProcessGroup>& process_group, | 107 | const c10::intrusive_ptr<c10d::ProcessGroup>& process_group, |
| 108 | std::vector<at::Tensor> bucket_tensors, | 108 | std::vector<at::Tensor> bucket_tensors, |
| 109 | int root_rank = 0) | 109 | int root_rank = 0) |
| 110 | : bucket_tensors_(std::move(bucket_tensors)), | 110 | : bucket_tensors_(std::move(bucket_tensors)), |
| 111 | cast_tensors_(cast_tensors(bucket_tensors_)), | 111 | cast_tensors_(cast_tensors(bucket_tensors_)), |
| 112 | flat_tensor_({torch::utils::flatten_dense_tensors(cast_tensors_)}) { | 112 | flat_tensor_({torch::utils::flatten_dense_tensors(cast_tensors_)}) { |
| 113 | c10d::BroadcastOptions broadcastOptions; | 113 | c10d::BroadcastOptions broadcastOptions; |
| 114 | broadcastOptions.rootRank = root_rank; | 114 | broadcastOptions.rootRank = root_rank; |
| 115 | work_ = process_group->broadcast(flat_tensor_, broadcastOptions); | 115 | work_ = process_group->broadcast(flat_tensor_, broadcastOptions); |
| 116 | } | 116 | } |
| 117 | 117 | ||
| 118 | void finish() | 118 | void finish() |
| 119 | { | 119 | { |
| 120 | work_->wait(); | 120 | work_->wait(); |
| 121 | auto output_tensors = torch::utils::unflatten_dense_tensors( | 121 | auto output_tensors = torch::utils::unflatten_dense_tensors( |
| 122 | flat_tensor_.front(), cast_tensors_); | 122 | flat_tensor_.front(), cast_tensors_); |
| 123 | TORCH_INTERNAL_ASSERT(output_tensors.size() == bucket_tensors_.size(), DIST_ERROR(ErrCode::PARAM)); | 123 | TORCH_INTERNAL_ASSERT(output_tensors.size() == bucket_tensors_.size(), DIST_ERROR(ErrCode::PARAM)); |
| 124 | for (const auto i : c10::irange(output_tensors.size())) { | 124 | for (const auto i : c10::irange(output_tensors.size())) { |
| 125 | bucket_tensors_[i].copy_(output_tensors[i], true); | 125 | bucket_tensors_[i].copy_(output_tensors[i], true); |
| 126 | } | 126 | } |
| 127 | } | 127 | } |
| 128 | 128 | ||
| 129 | protected: | 129 | protected: |
| 130 | // The list of tensors to broadcast. They are guaranteed to be | 130 | // The list of tensors to broadcast. They are guaranteed to be |
| 131 | // placed on the same device and have the same dtype. | 131 | // placed on the same device and have the same dtype. |
| 132 | std::vector<at::Tensor> bucket_tensors_; | 132 | std::vector<at::Tensor> bucket_tensors_; |
| 133 | // Some tensors with format, such as FRACTAL_Z, 5HD, may be padded to | 133 | // Some tensors with format, such as FRACTAL_Z, 5HD, may be padded to |
| 134 | // keep alignment with 16*16 cube kernel which will modify storage as | 134 | // keep alignment with 16*16 cube kernel which will modify storage as |
| 135 | // input tensor for cat operation during flatten to a buffer tensor. | 135 | // input tensor for cat operation during flatten to a buffer tensor. |
| 136 | // So, it needs to cast all bucket tensors to tensors with format HCHW | 136 | // So, it needs to cast all bucket tensors to tensors with format HCHW |
| 137 | std::vector<at::Tensor> cast_tensors_; | 137 | std::vector<at::Tensor> cast_tensors_; |
| 138 | // The vector with a single flattened tensor containing the contents | 138 | // The vector with a single flattened tensor containing the contents |
| 139 | // of the tensors in bucket_tensors_. It must be stored in a vector | 139 | // of the tensors in bucket_tensors_. It must be stored in a vector |
| 140 | // because c10d::ProcessGroup::broadcast takes a vector argument. | 140 | // because c10d::ProcessGroup::broadcast takes a vector argument. |
| 141 | std::vector<at::Tensor> flat_tensor_; | 141 | std::vector<at::Tensor> flat_tensor_; |
| 142 | 142 | ||
| 143 | private: | 143 | private: |
| 144 | 144 | ||
| 145 | // The broadcast work that is kicked off upon construction. | 145 | // The broadcast work that is kicked off upon construction. |
| 146 | c10::intrusive_ptr<c10d::Work> work_; | 146 | c10::intrusive_ptr<c10d::Work> work_; |
| 147 | }; | 147 | }; |
| 148 | 148 | ||
| 149 | // Broadcast many tensors to all processes in the process group. | 149 | // Broadcast many tensors to all processes in the process group. |
| 150 | void broadcast_coalesced( | 150 | void broadcast_coalesced( |
| 151 | c10::intrusive_ptr<c10d::ProcessGroup> process_group, | 151 | c10::intrusive_ptr<c10d::ProcessGroup> process_group, |
| 152 | at::TensorList tensors, | 152 | at::TensorList tensors, |
| 153 | size_t buffer_size, | 153 | size_t buffer_size, |
| 154 | int rank) | 154 | int rank) |
| 155 | { | 155 | { |
| 156 | // Coalesce tensors into buckets taking into account the maximum buffer size. | 156 | // Coalesce tensors into buckets taking into account the maximum buffer size. |
| 157 | // This routine is multi-device aware, so the tensors can be split across | 157 | // This routine is multi-device aware, so the tensors can be split across |
| 158 | // multiple devices and can contain a mix of CPU and CUDA tensors. | 158 | // multiple devices and can contain a mix of CPU and CUDA tensors. |
| 159 | std::vector<std::vector<size_t>> buckets; | 159 | std::vector<std::vector<size_t>> buckets; |
| 160 | std::tie(buckets, std::ignore) = | 160 | std::tie(buckets, std::ignore) = |
| 161 | c10d_npu::compute_bucket_assignment_by_size(tensors.vec(), {buffer_size}); | 161 | c10d_npu::compute_bucket_assignment_by_size(tensors.vec(), {buffer_size}); |
| 162 | 162 | ||
| 163 | // Returns tensor at specified index in input tensor list. | 163 | // Returns tensor at specified index in input tensor list. |
| 164 | const auto lookup = [&tensors](size_t index) { return tensors[index]; }; | 164 | const auto lookup = [&tensors](size_t index) { return tensors[index]; }; |
| 165 | 165 | ||
| 166 | // We maintain a maximum of 2 in flight broadcast operations to avoid | 166 | // We maintain a maximum of 2 in flight broadcast operations to avoid |
| 167 | // allocating too much memory (in case the specified tensors are very large). | 167 | // allocating too much memory (in case the specified tensors are very large). |
| 168 | std::deque<BroadcastWork> in_flight; | 168 | std::deque<BroadcastWork> in_flight; |
| 169 | constexpr auto max_in_flight = 2; | 169 | constexpr auto max_in_flight = 2; |
| 170 | for (const auto& bucket : buckets) { | 170 | for (const auto& bucket : buckets) { |
| 171 | if (in_flight.size() >= max_in_flight) { | 171 | if (in_flight.size() >= max_in_flight) { |
| 172 | in_flight.front().finish(); | 172 | in_flight.front().finish(); |
| 173 | in_flight.pop_front(); | 173 | in_flight.pop_front(); |
| 174 | } | 174 | } |
| 175 | in_flight.emplace_back(process_group, c10::fmap(bucket, lookup), rank); | 175 | in_flight.emplace_back(process_group, c10::fmap(bucket, lookup), rank); |
| 176 | } | 176 | } |
| 177 | 177 | ||
| 178 | while (!in_flight.empty()) { | 178 | while (!in_flight.empty()) { |
| 179 | in_flight.front().finish(); | 179 | in_flight.front().finish(); |
| 180 | in_flight.pop_front(); | 180 | in_flight.pop_front(); |
| 181 | } | 181 | } |
| 182 | } | 182 | } |
| 183 | 183 | ||
| 184 | // Called from DDP's Python API to create a c10d Python comm hook object. | 184 | // Called from DDP's Python API to create a c10d Python comm hook object. |
| 185 | // The input state and callable comm_hook are Python objects. It later calls | 185 | // The input state and callable comm_hook are Python objects. It later calls |
| 186 | // register_comm_hook function of the reducer input to register the hook. | 186 | // register_comm_hook function of the reducer input to register the hook. |
| 187 | void _register_comm_hook( | 187 | void _register_comm_hook( |
| 188 | c10d_npu::Reducer& reducer, | 188 | c10d_npu::Reducer& reducer, |
| 189 | py::object state, | 189 | py::object state, |
| 190 | py::object comm_hook) | 190 | py::object comm_hook) |
| 191 | { | 191 | { |
| 192 | reducer.register_comm_hook(std::make_unique<::c10d::PythonCommHook>( | 192 | reducer.register_comm_hook(std::make_unique<::c10d::PythonCommHook>( |
| 193 | std::move(state), std::move(comm_hook))); | 193 | std::move(state), std::move(comm_hook))); |
| 194 | } | 194 | } |
| 195 | 195 | ||
| 196 | // Called from DDP's Python API to create a c10d C++ comm hook. | 196 | // Called from DDP's Python API to create a c10d C++ comm hook. |
| 197 | // The input is an enum hook type. It later calls register_builtin_comm_hook | 197 | // The input is an enum hook type. It later calls register_builtin_comm_hook |
| 198 | // function of the reducer input to set the hook type. | 198 | // function of the reducer input to set the hook type. |
| 199 | void _register_builtin_comm_hook( | 199 | void _register_builtin_comm_hook( |
| 200 | c10d_npu::Reducer& reducer, | 200 | c10d_npu::Reducer& reducer, |
| 201 | ::c10d::BuiltinCommHookType comm_hook_type) | 201 | ::c10d::BuiltinCommHookType comm_hook_type) |
| 202 | { | 202 | { |
| 203 | reducer.register_builtin_comm_hook(comm_hook_type); | 203 | reducer.register_builtin_comm_hook(comm_hook_type); |
| 204 | } | 204 | } |
| 205 | 205 | ||
| 206 | PyObject* c10d_npu_init(PyObject* _unused, PyObject* noargs) | 206 | PyObject* c10d_npu_init(PyObject* _unused, PyObject* noargs) |
| 207 | { | 207 | { |
| 208 | auto torch_npu_C_module = THPObjectPtr(PyImport_ImportModule("torch_npu._C")); | 208 | auto torch_npu_C_module = THPObjectPtr(PyImport_ImportModule("torch_npu._C")); |
| 209 | if (!torch_npu_C_module) { | 209 | if (!torch_npu_C_module) { |
| 210 | throw python_error(); | 210 | throw python_error(); |
| 211 | } | 211 | } |
| 212 | auto torch_npu_C_m = py::handle(torch_npu_C_module).cast<py::module>(); | 212 | auto torch_npu_C_m = py::handle(torch_npu_C_module).cast<py::module>(); |
| 213 | 213 | ||
| 214 | auto m = | 214 | auto m = |
| 215 | torch_npu_C_m.def_submodule("_distributed_c10d", "distributed c10d bindings"); | 215 | torch_npu_C_m.def_submodule("_distributed_c10d", "distributed c10d bindings"); |
| 216 | auto module = py::handle(m).cast<py::module>(); | 216 | auto module = py::handle(m).cast<py::module>(); |
| 217 | 217 | ||
| 218 | module.def("_compute_bucket_assignment_by_size", | 218 | module.def("_compute_bucket_assignment_by_size", |
| 219 | [](const std::vector<at::Tensor>& tensors, | 219 | [](const std::vector<at::Tensor>& tensors, |
| 220 | const std::vector<size_t>& bucket_size_limits, | 220 | const std::vector<size_t>& bucket_size_limits, |
| 221 | const std::vector<bool>& expect_sparse_gradient, | 221 | const std::vector<bool>& expect_sparse_gradient, |
| 222 | const std::vector<int64_t>& tensor_indices, | 222 | const std::vector<int64_t>& tensor_indices, |
| 223 | const c10::optional<std::shared_ptr<::c10d::Logger>>& logger) { | 223 | const c10::optional<std::shared_ptr<::c10d::Logger>>& logger) { |
| 224 | if (logger.has_value()) { | 224 | if (logger.has_value()) { |
| 225 | std::weak_ptr<::c10d::Logger> logger_weakref = logger.value(); | 225 | std::weak_ptr<::c10d::Logger> logger_weakref = logger.value(); |
| 226 | return ::c10d_npu::compute_bucket_assignment_by_size(tensors, bucket_size_limits, expect_sparse_gradient, tensor_indices, {logger_weakref}); | 226 | return ::c10d_npu::compute_bucket_assignment_by_size(tensors, bucket_size_limits, expect_sparse_gradient, tensor_indices, {logger_weakref}); |
| 227 | } else { | 227 | } else { |
| 228 | return ::c10d_npu::compute_bucket_assignment_by_size(tensors, bucket_size_limits, expect_sparse_gradient, tensor_indices, {}); | 228 | return ::c10d_npu::compute_bucket_assignment_by_size(tensors, bucket_size_limits, expect_sparse_gradient, tensor_indices, {}); |
| 229 | } | 229 | } |
| 230 | }, | 230 | }, |
| 231 | py::arg("tensors"), | 231 | py::arg("tensors"), |
| 232 | py::arg("bucket_size"), | 232 | py::arg("bucket_size"), |
| 233 | py::arg("expect_sparse_gradient") = std::vector<bool>(), | 233 | py::arg("expect_sparse_gradient") = std::vector<bool>(), |
| 234 | py::arg("tensor_indices") = std::vector<int64_t>(), | 234 | py::arg("tensor_indices") = std::vector<int64_t>(), |
| 235 | py::arg("logger") = c10::optional<std::shared_ptr<::c10d::Logger>>{}, | 235 | py::arg("logger") = c10::optional<std::shared_ptr<::c10d::Logger>>{}, |
| 236 | py::call_guard<py::gil_scoped_release>()); | 236 | py::call_guard<py::gil_scoped_release>()); |
| 237 | 237 | ||
| 238 | module.def("_verify_params_across_processes", | 238 | module.def("_verify_params_across_processes", |
| 239 | [](const c10::intrusive_ptr<::c10d::ProcessGroup>& process_group, | 239 | [](const c10::intrusive_ptr<::c10d::ProcessGroup>& process_group, |
| 240 | const std::vector<at::Tensor>& params, | 240 | const std::vector<at::Tensor>& params, |
| 241 | const c10::optional<std::shared_ptr<::c10d::Logger>>& logger) { | 241 | const c10::optional<std::shared_ptr<::c10d::Logger>>& logger) { |
| 242 | if (logger.has_value()) { | 242 | if (logger.has_value()) { |
| 243 | std::weak_ptr<::c10d::Logger> logger_weakref = logger.value(); | 243 | std::weak_ptr<::c10d::Logger> logger_weakref = logger.value(); |
| 244 | c10d_npu::verify_params_across_processes(process_group, params, {logger_weakref}); | 244 | c10d_npu::verify_params_across_processes(process_group, params, {logger_weakref}); |
| 245 | } else { | 245 | } else { |
| 246 | c10d_npu::verify_params_across_processes(process_group, params, {}); | 246 | c10d_npu::verify_params_across_processes(process_group, params, {}); |
| 247 | } | 247 | } |
| 248 | }, | 248 | }, |
| 249 | py::arg("process_group"), | 249 | py::arg("process_group"), |
| 250 | py::arg("params"), | 250 | py::arg("params"), |
| 251 | py::arg("logger") = c10::optional<std::shared_ptr<::c10d::Logger>>{}, | 251 | py::arg("logger") = c10::optional<std::shared_ptr<::c10d::Logger>>{}, |
| 252 | py::call_guard<py::gil_scoped_release>()); | 252 | py::call_guard<py::gil_scoped_release>()); |
| 253 | 253 | ||
| 254 | module | 254 | module |
| 255 | .def("_register_comm_hook", | 255 | .def("_register_comm_hook", |
| 256 | &_register_comm_hook, | 256 | &_register_comm_hook, |
| 257 | py::arg("reducer"), | 257 | py::arg("reducer"), |
| 258 | py::arg("state"), | 258 | py::arg("state"), |
| 259 | py::arg("comm_hook"), | 259 | py::arg("comm_hook"), |
| 260 | py::call_guard<py::gil_scoped_release>()) | 260 | py::call_guard<py::gil_scoped_release>()) |
| 261 | .def("_register_builtin_comm_hook", | 261 | .def("_register_builtin_comm_hook", |
| 262 | &_register_builtin_comm_hook, | 262 | &_register_builtin_comm_hook, |
| 263 | py::arg("reducer"), | 263 | py::arg("reducer"), |
| 264 | py::arg("comm_hook_type")); | 264 | py::arg("comm_hook_type")); |
| 265 | 265 | ||
| 266 | module.def("_broadcast_coalesced", | 266 | module.def("_broadcast_coalesced", |
| 267 | // Define a lambda such that the pybind11 prototype can take a std::vector | 267 | // Define a lambda such that the pybind11 prototype can take a std::vector |
| 268 | // for the tensor list argument, but still pass it to the underlying | 268 | // for the tensor list argument, but still pass it to the underlying |
| 269 | // function as a c10::ArrayRef. | 269 | // function as a c10::ArrayRef. |
| 270 | [](c10::intrusive_ptr<::c10d::ProcessGroup> process_group, | 270 | [](c10::intrusive_ptr<::c10d::ProcessGroup> process_group, |
| 271 | std::vector<at::Tensor> tensors, // NOLINT | 271 | std::vector<at::Tensor> tensors, // NOLINT |
| 272 | size_t buffer_size, | 272 | size_t buffer_size, |
| 273 | int rank) { | 273 | int rank) { |
| 274 | torch_npu::distributed::broadcast_coalesced( | 274 | torch_npu::distributed::broadcast_coalesced( |
| 275 | std::move(process_group), tensors, buffer_size, rank); | 275 | std::move(process_group), tensors, buffer_size, rank); |
| 276 | }, | 276 | }, |
| 277 | py::arg("process_group"), | 277 | py::arg("process_group"), |
| 278 | py::arg("tensors"), | 278 | py::arg("tensors"), |
| 279 | py::arg("buffer_size"), | 279 | py::arg("buffer_size"), |
| 280 | // The source of truth rank to broadcast the tensors from. | 280 | // The source of truth rank to broadcast the tensors from. |
| 281 | py::arg("src") = 0, | 281 | py::arg("src") = 0, |
| 282 | py::call_guard<py::gil_scoped_release>()); | 282 | py::call_guard<py::gil_scoped_release>()); |
| 283 | 283 | ||
| 284 | module.def("_is_support_hccl_comm_name", &c10d_npu::isSupportHcclCommName); | 284 | module.def("_is_support_hccl_comm_name", &c10d_npu::isSupportHcclCommName); |
| 285 | 285 | ||
| 286 | shared_ptr_class_<c10d_npu::Reducer>(module, "Reducer") | 286 | shared_ptr_class_<c10d_npu::Reducer>(module, "Reducer") |
| 287 | .def(py::init< | 287 | .def(py::init< |
| 288 | std::vector<at::Tensor>, | 288 | std::vector<at::Tensor>, |
| 289 | std::vector<std::vector<size_t>>, | 289 | std::vector<std::vector<size_t>>, |
| 290 | std::vector<size_t>, | 290 | std::vector<size_t>, |
| 291 | c10::intrusive_ptr<::c10d::ProcessGroup>, | 291 | c10::intrusive_ptr<::c10d::ProcessGroup>, |
| 292 | std::vector<bool>, | 292 | std::vector<bool>, |
| 293 | int64_t, | 293 | int64_t, |
| 294 | bool, | 294 | bool, |
| 295 | bool, | 295 | bool, |
| 296 | std::unordered_map<size_t, std::string>, | 296 | std::unordered_map<size_t, std::string>, |
| 297 | int64_t>(), | 297 | int64_t>(), |
| 298 | py::arg("params"), | 298 | py::arg("params"), |
| 299 | py::arg("bucket_indices"), | 299 | py::arg("bucket_indices"), |
| 300 | py::arg("per_bucket_size_limits"), | 300 | py::arg("per_bucket_size_limits"), |
| 301 | py::arg("process_group"), | 301 | py::arg("process_group"), |
| 302 | py::arg("expect_sparse_gradients") = std::vector<bool>(), | 302 | py::arg("expect_sparse_gradients") = std::vector<bool>(), |
| 303 | py::arg("bucket_bytes_cap") = ::c10d::kDefaultBucketBytesCap, | 303 | py::arg("bucket_bytes_cap") = ::c10d::kDefaultBucketBytesCap, |
| 304 | py::arg("find_unused_parameters") = false, | 304 | py::arg("find_unused_parameters") = false, |
| 305 | py::arg("gradient_as_bucket_view") = false, | 305 | py::arg("gradient_as_bucket_view") = false, |
| 306 | py::arg("param_to_name_mapping") = | 306 | py::arg("param_to_name_mapping") = |
| 307 | std::unordered_map<size_t, std::string>(), | 307 | std::unordered_map<size_t, std::string>(), |
| 308 | py::arg("first_bucket_bytes_cap") = ::c10d::kDefaultFirstBucketBytes, | 308 | py::arg("first_bucket_bytes_cap") = ::c10d::kDefaultFirstBucketBytes, |
| 309 | py::call_guard<py::gil_scoped_release>()) | 309 | py::call_guard<py::gil_scoped_release>()) |
| 310 | .def("prepare_for_forward", | 310 | .def("prepare_for_forward", |
| 311 | &c10d_npu::Reducer::prepare_for_forward, | 311 | &c10d_npu::Reducer::prepare_for_forward, |
| 312 | py::call_guard<py::gil_scoped_release>()) | 312 | py::call_guard<py::gil_scoped_release>()) |
| 313 | .def("prepare_for_backward", | 313 | .def("prepare_for_backward", |
| 314 | &c10d_npu::Reducer::prepare_for_backward, | 314 | &c10d_npu::Reducer::prepare_for_backward, |
| 315 | py::call_guard<py::gil_scoped_release>()) | 315 | py::call_guard<py::gil_scoped_release>()) |
| 316 | .def("prepare_for_backward", | 316 | .def("prepare_for_backward", |
| 317 | [](c10d_npu::Reducer& reducer, const at::Tensor& output) | 317 | [](c10d_npu::Reducer& reducer, const at::Tensor& output) |
| 318 | -> void { reducer.prepare_for_backward({output}); }, | 318 | -> void { reducer.prepare_for_backward({output}); }, |
| 319 | py::call_guard<py::gil_scoped_release>()) | 319 | py::call_guard<py::gil_scoped_release>()) |
| 320 | .def("get_backward_stats", &c10d_npu::Reducer::get_backward_stats) | 320 | .def("get_backward_stats", &c10d_npu::Reducer::get_backward_stats) |
| 321 | .def("_install_post_backward_futures", [](::c10d_npu::Reducer& reducer, const std::vector<std::shared_ptr<torch::jit::PythonFutureWrapper>>& futs) { | 321 | .def("_install_post_backward_futures", [](::c10d_npu::Reducer& reducer, const std::vector<std::shared_ptr<torch::jit::PythonFutureWrapper>>& futs) { |
| 322 | c10::List<c10::intrusive_ptr<c10::ivalue::Future>> futures(c10::FutureType::create(c10::TensorType::get())); | 322 | c10::List<c10::intrusive_ptr<c10::ivalue::Future>> futures(c10::FutureType::create(c10::TensorType::get())); |
| 323 | for (const auto &fut : futs) { | 323 | for (const auto &fut : futs) { |
| 324 | futures.push_back(fut->fut); | 324 | futures.push_back(fut->fut); |
| 325 | } | 325 | } |
| 326 | reducer.install_futures(std::move(futures)); | 326 | reducer.install_futures(std::move(futures)); |
| 327 | }, | 327 | }, |
| 328 | py::call_guard<py::gil_scoped_release>()) | 328 | py::call_guard<py::gil_scoped_release>()) |
| 329 | .def("_rebuild_buckets", | 329 | .def("_rebuild_buckets", |
| 330 | &::c10d_npu::Reducer::rebuild_buckets, | 330 | &::c10d_npu::Reducer::rebuild_buckets, |
| 331 | py::call_guard<py::gil_scoped_release>()) | 331 | py::call_guard<py::gil_scoped_release>()) |
| 332 | .def("_get_zeros_like_grad_buckets", | 332 | .def("_get_zeros_like_grad_buckets", |
| 333 | [](::c10d_npu::Reducer& reducer) { | 333 | [](::c10d_npu::Reducer& reducer) { |
| 334 | return reducer.get_grad_buckets(true); | 334 | return reducer.get_grad_buckets(true); |
| 335 | }, | 335 | }, |
| 336 | py::call_guard<py::gil_scoped_release>()) | 336 | py::call_guard<py::gil_scoped_release>()) |
| 337 | .def("_push_all_rebuilt_params", | 337 | .def("_push_all_rebuilt_params", |
| 338 | &::c10d_npu::Reducer::push_rebuilt_params_for_all_indices, | 338 | &::c10d_npu::Reducer::push_rebuilt_params_for_all_indices, |
| 339 | py::call_guard<py::gil_scoped_release>()) | 339 | py::call_guard<py::gil_scoped_release>()) |
| 340 | .def("_set_forward_pass_work_handle", | 340 | .def("_set_forward_pass_work_handle", |
| 341 | &::c10d_npu::Reducer::set_forward_pass_work_handle, | 341 | &::c10d_npu::Reducer::set_forward_pass_work_handle, |
| 342 | py::call_guard<py::gil_scoped_release>()) | 342 | py::call_guard<py::gil_scoped_release>()) |
| 343 | .def("_get_local_used_map", | 343 | .def("_get_local_used_map", |
| 344 | &::c10d_npu::Reducer::get_local_used_map_on_device) | 344 | &::c10d_npu::Reducer::get_local_used_map_on_device) |
| 345 | .def("_set_ddp_runtime_logging_sample_rate", | 345 | .def("_set_ddp_runtime_logging_sample_rate", |
| 346 | &::c10d_npu::Reducer::set_ddp_runtime_logging_sample_rate, | 346 | &::c10d_npu::Reducer::set_ddp_runtime_logging_sample_rate, |
| 347 | py::arg("sample_rate"), | 347 | py::arg("sample_rate"), |
| 348 | py::call_guard<py::gil_scoped_release>()) | 348 | py::call_guard<py::gil_scoped_release>()) |
| 349 | .def("_set_static_graph", | 349 | .def("_set_static_graph", |
| 350 | &::c10d_npu::Reducer::set_static_graph, | 350 | &::c10d_npu::Reducer::set_static_graph, |
| 351 | py::call_guard<py::gil_scoped_release>()) | 351 | py::call_guard<py::gil_scoped_release>()) |
| 352 | .def("_ddp_graph_static", | 352 | .def("_ddp_graph_static", |
| 353 | &::c10d_npu::Reducer::ddp_graph_static, | 353 | &::c10d_npu::Reducer::ddp_graph_static, |
| 354 | py::call_guard<py::gil_scoped_release>()) | 354 | py::call_guard<py::gil_scoped_release>()) |
| 355 | .def("_delay_all_reduce", | 355 | .def("_delay_all_reduce", |
| 356 | &::c10d_npu::Reducer::delay_all_reduce, | 356 | &::c10d_npu::Reducer::delay_all_reduce, |
| 357 | py::call_guard<py::gil_scoped_release>()) | 357 | py::call_guard<py::gil_scoped_release>()) |
| 358 | .def("_run_comm_hook", | 358 | .def("_run_comm_hook", |
| 359 | [](::c10d_npu::Reducer& reducer, ::c10d::GradBucket& bucket) | 359 | [](::c10d_npu::Reducer& reducer, ::c10d::GradBucket& bucket) |
| 360 | -> std::shared_ptr<torch::jit::PythonFutureWrapper> { | 360 | -> std::shared_ptr<torch::jit::PythonFutureWrapper> { |
| 361 | c10::intrusive_ptr<c10::ivalue::Future> fut = | 361 | c10::intrusive_ptr<c10::ivalue::Future> fut = |
| 362 | reducer.run_comm_hook(bucket); | 362 | reducer.run_comm_hook(bucket); |
| 363 | return std::make_shared<torch::jit::PythonFutureWrapper>(fut); | 363 | return std::make_shared<torch::jit::PythonFutureWrapper>(fut); |
| 364 | }, | 364 | }, |
| 365 | py::call_guard<py::gil_scoped_release>()) | 365 | py::call_guard<py::gil_scoped_release>()) |
| 366 | .def("set_logger", | 366 | .def("set_logger", |
| 367 | [](::c10d_npu::Reducer& reducer, | 367 | [](::c10d_npu::Reducer& reducer, |
| 368 | const std::shared_ptr<::c10d::Logger> logger) { | 368 | const std::shared_ptr<::c10d::Logger> logger) { |
| 369 | std::weak_ptr<::c10d::Logger> logger_weakref = logger; | 369 | std::weak_ptr<::c10d::Logger> logger_weakref = logger; |
| 370 | reducer.set_logger(logger_weakref); | 370 | reducer.set_logger(logger_weakref); |
| 371 | }); | 371 | }); |
| 372 | 372 | ||
| 373 | py::module_ dist = py::module_::import("torch._C._distributed_c10d"); | 373 | py::module_ dist = py::module_::import("torch._C._distributed_c10d"); |
| 374 | auto processGroupHCCL = intrusive_ptr_no_gil_destructor_class_<::c10d_npu::ProcessGroupHCCL>( | 374 | auto processGroupHCCL = intrusive_ptr_no_gil_destructor_class_<::c10d_npu::ProcessGroupHCCL>( |
| 375 | module, "ProcessGroupHCCL", dist.attr("Backend")) | 375 | module, "ProcessGroupHCCL", dist.attr("Backend")) |
| 376 | .def(py::init<const c10::intrusive_ptr<::c10d::Store>&, | 376 | .def(py::init<const c10::intrusive_ptr<::c10d::Store>&, |
| 377 | int, | 377 | int, |
| 378 | int, | 378 | int, |
| 379 | c10::intrusive_ptr<::c10d_npu::ProcessGroupHCCL::Options>>(), | 379 | c10::intrusive_ptr<::c10d_npu::ProcessGroupHCCL::Options>>(), |
| 380 | py::call_guard<py::gil_scoped_release>()) | 380 | py::call_guard<py::gil_scoped_release>()) |
| 381 | .def(py::init([](const c10::intrusive_ptr<::c10d::Store>& store, | 381 | .def(py::init([](const c10::intrusive_ptr<::c10d::Store>& store, |
| 382 | int rank, | 382 | int rank, |
| 383 | int size, | 383 | int size, |
| 384 | const std::chrono::milliseconds& timeout) { | 384 | const std::chrono::milliseconds& timeout) { |
| 385 | auto options = ::c10d_npu::ProcessGroupHCCL::Options::create(); | 385 | auto options = ::c10d_npu::ProcessGroupHCCL::Options::create(); |
| 386 | options->is_high_priority_stream = false; | 386 | options->is_high_priority_stream = false; |
| 387 | options->timeout = timeout; | 387 | options->timeout = timeout; |
| 388 | return c10::make_intrusive<::c10d_npu::ProcessGroupHCCL>( | 388 | return c10::make_intrusive<::c10d_npu::ProcessGroupHCCL>( |
| 389 | store, rank, size, options); | 389 | store, rank, size, options); |
| 390 | }), | 390 | }), |
| 391 | py::arg("store"), | 391 | py::arg("store"), |
| 392 | py::arg("rank"), | 392 | py::arg("rank"), |
| 393 | py::arg("size"), | 393 | py::arg("size"), |
| 394 | py::arg("timeout") = kProcessGroupDefaultTimeout, | 394 | py::arg("timeout") = kProcessGroupDefaultTimeout, |
| 395 | py::call_guard<py::gil_scoped_release>()) | 395 | py::call_guard<py::gil_scoped_release>()) |
| 396 | .def("get_hccl_comm", &::c10d_npu::ProcessGroupHCCL::getHcclComm) | 396 | .def("get_hccl_comm", &::c10d_npu::ProcessGroupHCCL::getHcclComm) |
| 397 | .def("_set_hccl_comm_name", &::c10d_npu::ProcessGroupHCCL::setHcclCommName) | 397 | .def("_set_hccl_comm_name", &::c10d_npu::ProcessGroupHCCL::setHcclCommName) |
| 398 | .def("resume_hccl_comm", &::c10d_npu::ProcessGroupHCCL::resumeHcclComm) | 398 | .def("resume_hccl_comm", &::c10d_npu::ProcessGroupHCCL::resumeHcclComm) |
| 399 | .def("_set_switch_nic_comm", | 399 | .def("_set_switch_nic_comm", |
| 400 | &::c10d_npu::ProcessGroupHCCL::setSwitchNicComm, | 400 | &::c10d_npu::ProcessGroupHCCL::setSwitchNicComm, |
| 401 | py::arg("rankid"), | 401 | py::arg("rankid"), |
| 402 | py::arg("nRanks"), | 402 | py::arg("nRanks"), |
| 403 | py::arg("ranks") = std::vector<uint32_t>{}, | 403 | py::arg("ranks") = std::vector<uint32_t>{}, |
| 404 | py::arg("useBackup") = std::vector<bool>{}) | 404 | py::arg("useBackup") = std::vector<bool>{}) |
| 405 | .def("abort_hccl_comm", &::c10d_npu::ProcessGroupHCCL::abortAndClearHcclComm) | 405 | .def("abort_hccl_comm", &::c10d_npu::ProcessGroupHCCL::abortAndClearHcclComm) |
| 406 | .def("_delete_tcpstore_key", &::c10d_npu::ProcessGroupHCCL::deleteTCPStoreKey) | 406 | .def("_delete_tcpstore_key", &::c10d_npu::ProcessGroupHCCL::deleteTCPStoreKey) |
| 407 | .def("set_watchdog_status", &::c10d_npu::ProcessGroupHCCL::setWatchdogStatus) | 407 | .def("set_watchdog_status", &::c10d_npu::ProcessGroupHCCL::setWatchdogStatus) |
| 408 | .def("clear_workmeta_list", &::c10d_npu::ProcessGroupHCCL::clearWorkMetaList) | 408 | .def("clear_workmeta_list", &::c10d_npu::ProcessGroupHCCL::clearWorkMetaList) |
| 409 | .def("get_hccl_comm_name", | 409 | .def("get_hccl_comm_name", |
| 410 | [](::c10d_npu::ProcessGroupHCCL &pg, int rankid, py::args args, py::kwargs kwargs) | 410 | [](::c10d_npu::ProcessGroupHCCL &pg, int rankid, py::args args, py::kwargs kwargs) |
| 411 | -> std::string { | 411 | -> std::string { |
| 412 | bool init_comm = true; | 412 | bool init_comm = true; |
| 413 | if (kwargs.contains("init_comm")) { | 413 | if (kwargs.contains("init_comm")) { |
| 414 | init_comm = py::cast<bool>(kwargs["init_comm"]); | 414 | init_comm = py::cast<bool>(kwargs["init_comm"]); |
| 415 | } | 415 | } |
| 416 | return pg.getHcclCommName(rankid, init_comm); | 416 | return pg.getHcclCommName(rankid, init_comm); |
| 417 | }) | 417 | }) |
| 418 | .def("_get_stream_id", &::c10d_npu::ProcessGroupHCCL::getStreamId, | 418 | .def("_get_stream_id", &::c10d_npu::ProcessGroupHCCL::getStreamId, |
| 419 | py::arg("p2p") = false, | 419 | py::arg("p2p") = false, |
| 420 | py::arg("peer") = -1) | 420 | py::arg("peer") = -1) |
| 421 | .def("get_coll_stream_id", &::c10d_npu::ProcessGroupHCCL::getCollNpuStreamId, | 421 | .def("get_coll_stream_id", &::c10d_npu::ProcessGroupHCCL::getCollNpuStreamId, |
| 422 | py::arg("device")) | 422 | py::arg("device")) |
| 423 | .def("get_p2p_stream_id", &::c10d_npu::ProcessGroupHCCL::getP2PStreamId, | 423 | .def("get_p2p_stream_id", &::c10d_npu::ProcessGroupHCCL::getP2PStreamId, |
| 424 | py::arg("device"), | 424 | py::arg("device"), |
| 425 | py::arg("peer"), | 425 | py::arg("peer"), |
| 426 | py::arg("is_batched")) | 426 | py::arg("is_batched")) |
| 427 | .def("_window_register_and_exchange", &::c10d_npu::ProcessGroupHCCL::windowRegisterAndExchange, | 427 | .def("_window_register_and_exchange", &::c10d_npu::ProcessGroupHCCL::windowRegisterAndExchange, |
| 428 | py::arg("window_size"), | 428 | py::arg("window_size"), |
| 429 | py::arg("peer_ranks")) | 429 | py::arg("peer_ranks")) |
| 430 | .def("_get_window_mem", &::c10d_npu::ProcessGroupHCCL::getWindowMem) | 430 | .def("_get_window_mem", &::c10d_npu::ProcessGroupHCCL::getWindowMem) |
| 431 | .def_property_readonly("options", &::c10d_npu::ProcessGroupHCCL::getOptions) | 431 | .def_property_readonly("options", &::c10d_npu::ProcessGroupHCCL::getOptions) |
| 432 | .def("batch_isend_irecv", | 432 | .def("batch_isend_irecv", |
| 433 | [](::c10d_npu::ProcessGroupHCCL &pg, std::vector<std::string> &op_type, | 433 | [](::c10d_npu::ProcessGroupHCCL &pg, std::vector<std::string> &op_type, |
| 434 | std::vector<at::Tensor> &tensors, | 434 | std::vector<at::Tensor> &tensors, |
| 435 | std::vector<int64_t> remote_rank_list) | 435 | std::vector<int64_t> remote_rank_list) |
| 436 | -> c10::intrusive_ptr<c10d::Work> { | 436 | -> c10::intrusive_ptr<c10d::Work> { |
| 437 | return pg.batch_isend_irecv(op_type, tensors, remote_rank_list); | 437 | return pg.batch_isend_irecv(op_type, tensors, remote_rank_list); |
| 438 | }, | 438 | }, |
| 439 | py::call_guard<py::gil_scoped_release>()) | 439 | py::call_guard<py::gil_scoped_release>()) |
| 440 | .def("reduce_scatter_tensor_uneven", | 440 | .def("reduce_scatter_tensor_uneven", |
| 441 | &::c10d_npu::ProcessGroupHCCL::_reduce_scatter_base_uneven, | 441 | &::c10d_npu::ProcessGroupHCCL::_reduce_scatter_base_uneven, |
| 442 | py::arg("output"), | 442 | py::arg("output"), |
| 443 | py::arg("input"), | 443 | py::arg("input"), |
| 444 | py::arg("input_split_sizes") = std::vector<int64_t>{}, | 444 | py::arg("input_split_sizes") = std::vector<int64_t>{}, |
| 445 | py::arg("opts") = ::c10d::ReduceScatterOptions(), | 445 | py::arg("opts") = ::c10d::ReduceScatterOptions(), |
| 446 | py::call_guard<py::gil_scoped_release>()) | 446 | py::call_guard<py::gil_scoped_release>()) |
| 447 | .def("all_gather_into_tensor_uneven", | 447 | .def("all_gather_into_tensor_uneven", |
| 448 | &::c10d_npu::ProcessGroupHCCL::_allgather_base_uneven, | 448 | &::c10d_npu::ProcessGroupHCCL::_allgather_base_uneven, |
| 449 | py::arg("output"), | 449 | py::arg("output"), |
| 450 | py::arg("input"), | 450 | py::arg("input"), |
| 451 | py::arg("output_split_sizes") = std::vector<int64_t>{}, | 451 | py::arg("output_split_sizes") = std::vector<int64_t>{}, |
| 452 | py::arg("opts") = ::c10d::AllgatherOptions(), | 452 | py::arg("opts") = ::c10d::AllgatherOptions(), |
| 453 | py::call_guard<py::gil_scoped_release>()) | 453 | py::call_guard<py::gil_scoped_release>()) |
| 454 | .def("_set_default_timeout", | 454 | .def("_set_default_timeout", |
| 455 | &::c10d_npu::ProcessGroupHCCL::setTimeout, | 455 | &::c10d_npu::ProcessGroupHCCL::setTimeout, |
| 456 | py::arg("timeout"), | 456 | py::arg("timeout"), |
| 457 | py::call_guard<py::gil_scoped_release>()) | 457 | py::call_guard<py::gil_scoped_release>()) |
| 458 | .def("_add_ephemeral_timeout", | 458 | .def("_add_ephemeral_timeout", |
| 459 | &::c10d_npu::ProcessGroupHCCL::addEphemeralTimeout, | 459 | &::c10d_npu::ProcessGroupHCCL::addEphemeralTimeout, |
| 460 | py::arg("timeout"), | 460 | py::arg("timeout"), |
| 461 | py::call_guard<py::gil_scoped_release>()); | 461 | py::call_guard<py::gil_scoped_release>()); |
| 462 | 462 | ||
| 463 | intrusive_ptr_class_<::c10d_npu::ProcessGroupHCCL::Options>( | 463 | intrusive_ptr_class_<::c10d_npu::ProcessGroupHCCL::Options>( |
| 464 | processGroupHCCL, | 464 | processGroupHCCL, |
| 465 | "Options", | 465 | "Options", |
| 466 | dist.attr("Backend").attr("Options")) | 466 | dist.attr("Backend").attr("Options")) |
| 467 | .def(py::init<>()) | 467 | .def(py::init<>()) |
| 468 | .def_readwrite("op_timeout", &::c10d_npu::ProcessGroupHCCL::Options::opTimeout) | 468 | .def_readwrite("op_timeout", &::c10d_npu::ProcessGroupHCCL::Options::opTimeout) |
| 469 | .def_readwrite("is_high_priority_stream", | 469 | .def_readwrite("is_high_priority_stream", |
| 470 | &::c10d_npu::ProcessGroupHCCL::Options::is_high_priority_stream) | 470 | &::c10d_npu::ProcessGroupHCCL::Options::is_high_priority_stream) |
| 471 | .def_readwrite("global_ranks_in_group", | 471 | .def_readwrite("global_ranks_in_group", |
| 472 | &::c10d_npu::ProcessGroupHCCL::Options::global_ranks_in_group) | 472 | &::c10d_npu::ProcessGroupHCCL::Options::global_ranks_in_group) |
| 473 | .def_readwrite("hccl_config", &::c10d_npu::ProcessGroupHCCL::Options::hccl_config) | 473 | .def_readwrite("hccl_config", &::c10d_npu::ProcessGroupHCCL::Options::hccl_config) |
| 474 | .def_readwrite("group_id", | 474 | .def_readwrite("group_id", |
| 475 | &::c10d_npu::ProcessGroupHCCL::Options::group_id); | 475 | &::c10d_npu::ProcessGroupHCCL::Options::group_id); |
| 476 | 476 | ||
| 477 | // bind for ProcessGroupLCCL | 477 | // bind for ProcessGroupLCCL |
| 478 | auto processGroupLCCL = intrusive_ptr_no_gil_destructor_class_<::c10d_npu::ProcessGroupLCCL>( | 478 | auto processGroupLCCL = intrusive_ptr_no_gil_destructor_class_<::c10d_npu::ProcessGroupLCCL>( |
| 479 | module, "ProcessGroupLCCL", dist.attr("Backend")) | 479 | module, "ProcessGroupLCCL", dist.attr("Backend")) |
| 480 | .def(py::init<const c10::intrusive_ptr<::c10d::Store>&, int, int>(), | 480 | .def(py::init<const c10::intrusive_ptr<::c10d::Store>&, int, int>(), |
| 481 | py::call_guard<py::gil_scoped_release>()); | 481 | py::call_guard<py::gil_scoped_release>()); |
| 482 | 482 | ||
| 483 | auto cDist = py::module_::import("torch._C._distributed_c10d"); | 483 | auto cDist = py::module_::import("torch._C._distributed_c10d"); |
| 484 | auto parallelStore = intrusive_ptr_no_gil_destructor_class_<::c10d::ParallelTcpStore>( | 484 | auto parallelStore = intrusive_ptr_no_gil_destructor_class_<::c10d::ParallelTcpStore>( |
| 485 | module, "ParallelStore", cDist.attr("Store"), R"( | 485 | module, "ParallelStore", cDist.attr("Store"), R"( |
| 486 | A TCP-Parallel-Epoll-based distributed key-value store implementation. The server store holds | 486 | A TCP-Parallel-Epoll-based distributed key-value store implementation. The server store holds |
| 487 | the data, while the client stores can connect to the server store over TCP and | 487 | the data, while the client stores can connect to the server store over TCP and |
| 488 | perform actions such as :meth:`~torch.distributed.store.set` to insert a key-value | 488 | perform actions such as :meth:`~torch.distributed.store.set` to insert a key-value |
| 489 | pair, :meth:`~torch.distributed.store.get` to retrieve a key-value pair, etc. There | 489 | pair, :meth:`~torch.distributed.store.get` to retrieve a key-value pair, etc. There |
| 490 | should always be one server store initialized because the client store(s) will wait for | 490 | should always be one server store initialized because the client store(s) will wait for |
| 491 | the server to establish a connection. | 491 | the server to establish a connection. |
| 492 | 492 | ||
| 493 | Arguments: | 493 | Arguments: |
| 494 | host_name (str): The hostname or IP Address the server store should run on. | 494 | host_name (str): The hostname or IP Address the server store should run on. |
| 495 | port (int): The port on which the server store should listen for incoming requests. | 495 | port (int): The port on which the server store should listen for incoming requests. |
| 496 | world_size (int, optional): The total number of store users (number of clients + 1 for the server). Default is -1 (a negative value indicates a non-fixed number of store users). | 496 | world_size (int, optional): The total number of store users (number of clients + 1 for the server). Default is -1 (a negative value indicates a non-fixed number of store users). |
| 497 | agentRun(bool): The client(worker), agentRun is False. The agent(proxy), agentRun is True. | 497 | agentRun(bool): The client(worker), agentRun is False. The agent(proxy), agentRun is True. |
| 498 | agentPid(int): Generally, a single `torch_run` is launched on a node. If multiple `torch_run` are launched on a node, the agentPid refers to the process ID (PID) of each `torch_run`. | 498 | agentPid(int): Generally, a single `torch_run` is launched on a node. If multiple `torch_run` are launched on a node, the agentPid refers to the process ID (PID) of each `torch_run`. |
| 499 | The pid transmit to the worker through environment variable and is used for local socket communication. | 499 | The pid transmit to the worker through environment variable and is used for local socket communication. |
| 500 | is_master (bool, optional): True when initializing the server store and False for client stores. Default is False. | 500 | is_master (bool, optional): True when initializing the server store and False for client stores. Default is False. |
| 501 | enableTiered(bool, optional): parallel tcpstore tiered optimization, if True, The agent adds a proxy role, the worker on the node connects to the proxy via Unix Domain Socket. | 501 | enableTiered(bool, optional): parallel tcpstore tiered optimization, if True, The agent adds a proxy role, the worker on the node connects to the proxy via Unix Domain Socket. |
| 502 | and the proxy connects to the server via TCP, completing the establishment and communication of the connection., Default is False. | 502 | and the proxy connects to the server via TCP, completing the establishment and communication of the connection., Default is False. |
| 503 | timeout (timedelta, optional): Timeout used by the store during initialization and for methods such as :meth:`~torch.distributed.store.get` and :meth:`~torch.distributed.store.wait`. Default is timedelta(seconds=300) | 503 | timeout (timedelta, optional): Timeout used by the store during initialization and for methods such as :meth:`~torch.distributed.store.get` and :meth:`~torch.distributed.store.wait`. Default is timedelta(seconds=300) |
| 504 | wait_for_worker (bool, optional): Whether to wait for all the workers to connect with the server store. This is only applicable when world_size is a fixed value. Default is True. | 504 | wait_for_worker (bool, optional): Whether to wait for all the workers to connect with the server store. This is only applicable when world_size is a fixed value. Default is True. |
| 505 | 505 | ||
| 506 | --enable_tiered_parallel_tcpstore = "false": | 506 | --enable_tiered_parallel_tcpstore = "false": |
| 507 | Example:: | 507 | Example:: |
| 508 | >>> import torch_npu.distributed as dist | 508 | >>> import torch_npu.distributed as dist |
| 509 | >>> from datetime import timedelta | 509 | >>> from datetime import timedelta |
| 510 | >>> # Run on process 1 (server) | 510 | >>> # Run on process 1 (server) |
| 511 | >>> server_store = dist.ParallelStore("127.0.0.1", 1234, 2, True, 100, True, timedelta(seconds=30)) | 511 | >>> server_store = dist.ParallelStore("127.0.0.1", 1234, 2, True, 100, True, timedelta(seconds=30)) |
| 512 | >>> # Run on process 2 (client) | 512 | >>> # Run on process 2 (client) |
| 513 | >>> client_store = dist.ParallelStore("127.0.0.1", 1234, 2, False, 100, False) | 513 | >>> client_store = dist.ParallelStore("127.0.0.1", 1234, 2, False, 100, False) |
| 514 | >>> # Use any of the store methods from either the client or server after initialization | 514 | >>> # Use any of the store methods from either the client or server after initialization |
| 515 | >>> server_store.set("first_key", "first_value") | 515 | >>> server_store.set("first_key", "first_value") |
| 516 | >>> client_store.get("first_key") | 516 | >>> client_store.get("first_key") |
| 517 | 517 | ||
| 518 | --enable_tiered_parallel_tcpstore = "true": | 518 | --enable_tiered_parallel_tcpstore = "true": |
| 519 | Example:: | 519 | Example:: |
| 520 | >>> import torch_npu.distributed as dist | 520 | >>> import torch_npu.distributed as dist |
| 521 | >>> from datetime import timedelta | 521 | >>> from datetime import timedelta |
| 522 | >>> # Run on process 1 (server proxy) | 522 | >>> # Run on process 1 (server proxy) |
| 523 | >>> server_store = dist.ParallelStore("127.0.0.1", 1234, 2, True, 100, True, True, timedelta(seconds=30)) | 523 | >>> server_store = dist.ParallelStore("127.0.0.1", 1234, 2, True, 100, True, True, timedelta(seconds=30)) |
| 524 | >>> # Run on process 2 (client) | 524 | >>> # Run on process 2 (client) |
| 525 | >>> client_store = dist.ParallelStore("127.0.0.1", 1234, 2, False, 100, False, True) | 525 | >>> client_store = dist.ParallelStore("127.0.0.1", 1234, 2, False, 100, False, True) |
| 526 | >>> # Use any of the store methods from either the client or server and proxy after initialization | 526 | >>> # Use any of the store methods from either the client or server and proxy after initialization |
| 527 | >>> server_store.set("first_key", "first_value") | 527 | >>> server_store.set("first_key", "first_value") |
| 528 | >>> client_store.get("first_key") | 528 | >>> client_store.get("first_key") |
| 529 | )") | 529 | )") |
| 530 | 530 | ||
| 531 | .def(py::init([](const std::string &host, | 531 | .def(py::init([](const std::string &host, |
| 532 | uint16_t port, | 532 | uint16_t port, |
| 533 | int worldSize, | 533 | int worldSize, |
| 534 | bool agentRun, | 534 | bool agentRun, |
| 535 | uint32_t agentPid, | 535 | uint32_t agentPid, |
| 536 | bool isServer, | 536 | bool isServer, |
| 537 | bool enableTiered, | 537 | bool enableTiered, |
| 538 | std::chrono::milliseconds timeout, | 538 | std::chrono::milliseconds timeout, |
| 539 | bool waitWorkers, | 539 | bool waitWorkers, |
| 540 | bool multiTenant) { | 540 | bool multiTenant) { |
| 541 | c10::optional<std::size_t> numWorkers = c10::nullopt; | 541 | c10::optional<std::size_t> numWorkers = c10::nullopt; |
| 542 | if (worldSize > -1) { | 542 | if (worldSize > -1) { |
| 543 | numWorkers = static_cast<std::size_t>(worldSize); | 543 | numWorkers = static_cast<std::size_t>(worldSize); |
| 544 | } | 544 | } |
| 545 | ::c10d::TCPStoreOptions opts{ port, isServer, numWorkers, waitWorkers, timeout, multiTenant }; | 545 | ::c10d::TCPStoreOptions opts{ port, isServer, numWorkers, waitWorkers, timeout, multiTenant }; |
| 546 | return c10::make_intrusive <::c10d::ParallelTcpStore>(host, agentRun, agentPid, enableTiered, opts); | 546 | return c10::make_intrusive <::c10d::ParallelTcpStore>(host, agentRun, agentPid, enableTiered, opts); |
| 547 | }), | 547 | }), |
| 548 | py::arg("host") = "127.0.0.1", | 548 | py::arg("host") = "127.0.0.1", |
| 549 | py::arg("port") = 29500, | 549 | py::arg("port") = 29500, |
| 550 | py::arg("world_size") = -1, | 550 | py::arg("world_size") = -1, |
| 551 | py::arg("agent_run") = false, | 551 | py::arg("agent_run") = false, |
| 552 | py::arg("agent_pid") = -1, | 552 | py::arg("agent_pid") = -1, |
| 553 | py::arg("is_server") = false, | 553 | py::arg("is_server") = false, |
| 554 | py::arg("enable_tiered") = false, | 554 | py::arg("enable_tiered") = false, |
| 555 | py::arg("timeout") = std::chrono::milliseconds(300000), | 555 | py::arg("timeout") = std::chrono::milliseconds(300000), |
| 556 | py::arg("wait_workers") = true, | 556 | py::arg("wait_workers") = true, |
| 557 | py::arg("multi_tenant") = false); | 557 | py::arg("multi_tenant") = false); |
| 558 | 558 | ||
| 559 | module.def("_dump_hccl_trace_json", | 559 | module.def("_dump_hccl_trace_json", |
| 560 | [](std::optional<bool> includeCollectives, | 560 | [](std::optional<bool> includeCollectives, |
| 561 | std::optional<bool> onlyActive) { | 561 | std::optional<bool> onlyActive) { |
| 562 | return py::bytes(::c10d_npu::dump_hccl_trace_json( | 562 | return py::bytes(::c10d_npu::dump_hccl_trace_json( |
| 563 | includeCollectives.value_or(true), onlyActive.value_or(false))); | 563 | includeCollectives.value_or(true), onlyActive.value_or(false))); |
| 564 | }, | 564 | }, |
| 565 | py::arg("includeCollectives") = std::optional<bool>(), | 565 | py::arg("includeCollectives") = std::optional<bool>(), |
| 566 | py::arg("onlyActive") = std::optional<bool>(), | 566 | py::arg("onlyActive") = std::optional<bool>(), |
| 567 | R"( | 567 | R"( |
| 568 | Arguments: | 568 | Arguments: |
| 569 | includeCollectives(bool, optional): Whether to include collective work traces. Default is True. | 569 | includeCollectives(bool, optional): Whether to include collective work traces. Default is True. |
| 570 | onlyActive (bool, optional): Whether to only include active collective work traces. Default is False. | 570 | onlyActive (bool, optional): Whether to only include active collective work traces. Default is False. |
| 571 | Returns: | 571 | Returns: |
| 572 | Stringified json work traces. | 572 | Stringified json work traces. |
| 573 | Default settings return everything - i.e. contains HCCL comm dumps and collective traces. | 573 | Default settings return everything - i.e. contains HCCL comm dumps and collective traces. |
| 574 | )"); | 574 | )"); |
| 575 | module.def("_dump_hccl_trace", | 575 | module.def("_dump_hccl_trace", |
| 576 | [](std::optional<bool> includeCollectives, | 576 | [](std::optional<bool> includeCollectives, |
| 577 | std::optional<bool> includeStackTraces, | 577 | std::optional<bool> includeStackTraces, |
| 578 | std::optional<bool> onlyActive) { | 578 | std::optional<bool> onlyActive) { |
| 579 | return py::bytes(::c10d_npu::dump_hccl_trace( | 579 | return py::bytes(::c10d_npu::dump_hccl_trace( |
| 580 | includeCollectives.value_or(true), | 580 | includeCollectives.value_or(true), |
| 581 | includeStackTraces.value_or(true), | 581 | includeStackTraces.value_or(true), |
| 582 | onlyActive.value_or(false))); | 582 | onlyActive.value_or(false))); |
| 583 | }, | 583 | }, |
| 584 | py::arg("includeCollectives") = std::optional<bool>(), | 584 | py::arg("includeCollectives") = std::optional<bool>(), |
| 585 | py::arg("includeStackTraces") = std::optional<bool>(), | 585 | py::arg("includeStackTraces") = std::optional<bool>(), |
| 586 | py::arg("onlyActive") = std::optional<bool>(), | 586 | py::arg("onlyActive") = std::optional<bool>(), |
| 587 | R"( | 587 | R"( |
| 588 | Arguments: | 588 | Arguments: |
| 589 | includeCollectives(bool, optional): Whether to include collective work traces. Default is True. | 589 | includeCollectives(bool, optional): Whether to include collective work traces. Default is True. |
| 590 | includeStackTraces(bool, optional): Whether to include stacktraces in the collective work traces. Default is True. | 590 | includeStackTraces(bool, optional): Whether to include stacktraces in the collective work traces. Default is True. |
| 591 | onlyActive (bool, optional): Whether to only include active collective work traces. Default is False. | 591 | onlyActive (bool, optional): Whether to only include active collective work traces. Default is False. |
| 592 | Returns: | 592 | Returns: |
| 593 | Stringified pickle work traces. | 593 | Stringified pickle work traces. |
| 594 | Default settings return everything - i.e. contains HCCL comm dumps and collective traces. | 594 | Default settings return everything - i.e. contains HCCL comm dumps and collective traces. |
| 595 | )"); | 595 | )"); |
| 596 | 596 | ||
| 597 | Py_RETURN_TRUE; | 597 | Py_RETURN_TRUE; |
| 598 | } | 598 | } |
| 599 | 599 | ||
| 600 | // c10d methods on torch._C | 600 | // c10d methods on torch._C |
| 601 | static PyMethodDef methods[] = { // NOLINT | 601 | static PyMethodDef methods[] = { // NOLINT |
| 602 | {"_c10d_npu_init", c10d_npu_init, METH_NOARGS, nullptr}, | 602 | {"_c10d_npu_init", c10d_npu_init, METH_NOARGS, nullptr}, |
| 603 | #ifdef USE_RPC_FRAMEWORK | 603 | #ifdef USE_RPC_FRAMEWORK |
| 604 | {"_rpc_npu_init", rpc::rpc_npu_init, METH_NOARGS, nullptr}, | 604 | {"_rpc_npu_init", rpc::rpc_npu_init, METH_NOARGS, nullptr}, |
| 605 | #endif | 605 | #endif |
| 606 | {nullptr, nullptr, 0, nullptr}}; | 606 | {nullptr, nullptr, 0, nullptr}}; |
| 607 | 607 | ||
| 608 | PyMethodDef* python_functions() | 608 | PyMethodDef* python_functions() |
| 609 | { | 609 | { |
| 610 | return methods; | 610 | return methods; |
| 611 | } | 611 | } |
| 612 | 612 | ||
| 613 | } // namespace distributed | 613 | } // namespace distributed |
| 614 | } // namespace torch_npu | 614 | } // namespace torch_npu |
| @@ -1,12 +1,12 @@ | |||
| 1 | #pragma once | 1 | #pragma once |
| 2 | 2 | ||
| 3 | #include <torch/csrc/python_headers.h> | 3 | #include <torch/csrc/python_headers.h> |
| 4 | #include "torch_npu/csrc/core/npu/NPUMacros.h" | 4 | #include "torch_npu/csrc/core/npu/NPUMacros.h" |
| 5 | 5 | ||
| 6 | namespace torch_npu { | 6 | namespace torch_npu { |
| 7 | namespace distributed { | 7 | namespace distributed { |
| 8 | 8 | ||
| 9 | TORCH_NPU_API PyMethodDef* python_functions(); | 9 | TORCH_NPU_API PyMethodDef* python_functions(); |
| 10 | 10 | ||
| 11 | } // namespace distributed | 11 | } // namespace distributed |
| 12 | } // namespace torch_npu | 12 | } // namespace torch_npu |
| @@ -1,57 +1,57 @@ | |||
| 1 | #include <c10d/default_comm_hooks.hpp> | 1 | #include <c10d/default_comm_hooks.hpp> |
| 2 | #include <c10/core/ScalarType.h> | 2 | #include <c10/core/ScalarType.h> |
| 3 | #include <c10/util/Exception.h> | 3 | #include <c10/util/Exception.h> |
| 4 | 4 | ||
| 5 | #include <c10d/ProcessGroup.hpp> | 5 | #include <c10d/ProcessGroup.hpp> |
| 6 | #include <c10d/comm.hpp> | 6 | #include <c10d/comm.hpp> |
| 7 | #include <torch/torch.h> | 7 | #include <torch/torch.h> |
| 8 | 8 | ||
| 9 | #include "torch_npu/csrc/core/npu/NPUException.h" | 9 | #include "torch_npu/csrc/core/npu/NPUException.h" |
| 10 | 10 | ||
| 11 | namespace c10d { | 11 | namespace c10d { |
| 12 | 12 | ||
| 13 | c10::intrusive_ptr<c10::ivalue::Future> AllReduceCommHook::runHook( | 13 | c10::intrusive_ptr<c10::ivalue::Future> AllReduceCommHook::runHook( |
| 14 | GradBucket& bucket) | 14 | GradBucket& bucket) |
| 15 | { | 15 | { |
| 16 | std::vector<at::Tensor> tensors = {bucket.getBufferRef()}; | 16 | std::vector<at::Tensor> tensors = {bucket.getBufferRef()}; |
| 17 | // Apply the division first to avoid overflow, especially for FP16. | 17 | // Apply the division first to avoid overflow, especially for FP16. |
| 18 | tensors[0] /= state_->getSize(); | 18 | tensors[0] /= state_->getSize(); |
| 19 | return state_->allreduce(tensors)->getFuture(); | 19 | return state_->allreduce(tensors)->getFuture(); |
| 20 | } | 20 | } |
| 21 | 21 | ||
| 22 | c10::intrusive_ptr<c10::ivalue::Future> FP16CompressCommHook::runHook( | 22 | c10::intrusive_ptr<c10::ivalue::Future> FP16CompressCommHook::runHook( |
| 23 | GradBucket& bucket) | 23 | GradBucket& bucket) |
| 24 | { | 24 | { |
| 25 | auto compressed_tensor = bucket.getBufferRef().to(torch::kFloat16); | 25 | auto compressed_tensor = bucket.getBufferRef().to(torch::kFloat16); |
| 26 | // Apply the division first to avoid overflow. | 26 | // Apply the division first to avoid overflow. |
| 27 | compressed_tensor /= state_->getSize(); | 27 | compressed_tensor /= state_->getSize(); |
| 28 | std::vector<at::Tensor> tensors = {compressed_tensor}; | 28 | std::vector<at::Tensor> tensors = {compressed_tensor}; |
| 29 | 29 | ||
| 30 | auto allreduce_fut = state_->allreduce(tensors)->getFuture(); | 30 | auto allreduce_fut = state_->allreduce(tensors)->getFuture(); |
| 31 | auto decompressed_tensor = bucket.getBufferRef(); | 31 | auto decompressed_tensor = bucket.getBufferRef(); |
| 32 | auto decompress = [decompressed_tensor](c10::ivalue::Future& allreduce_fut) { | 32 | auto decompress = [decompressed_tensor](c10::ivalue::Future& allreduce_fut) { |
| 33 | auto result = allreduce_fut.value(); | 33 | auto result = allreduce_fut.value(); |
| 34 | TORCH_INTERNAL_ASSERT( | 34 | TORCH_INTERNAL_ASSERT( |
| 35 | result.isTensorList(), | 35 | result.isTensorList(), |
| 36 | "ProcessGroup::allreduce should return TensorList", DIST_ERROR(ErrCode::INTERNAL)); | 36 | "ProcessGroup::allreduce should return TensorList", DIST_ERROR(ErrCode::INTERNAL)); |
| 37 | 37 | ||
| 38 | auto reduce_tensor = result.toTensorVector()[0]; | 38 | auto reduce_tensor = result.toTensorVector()[0]; |
| 39 | TORCH_INTERNAL_ASSERT_DEBUG_ONLY( | 39 | TORCH_INTERNAL_ASSERT_DEBUG_ONLY( |
| 40 | reduce_tensor.scalar_type() == at::ScalarType::Half, | 40 | reduce_tensor.scalar_type() == at::ScalarType::Half, |
| 41 | "Expected reduced tensor to be fp16 in FP16CompressHook, but got type ", | 41 | "Expected reduced tensor to be fp16 in FP16CompressHook, but got type ", |
| 42 | reduce_tensor.scalar_type(), DIST_ERROR(ErrCode::TYPE) | 42 | reduce_tensor.scalar_type(), DIST_ERROR(ErrCode::TYPE) |
| 43 | ); | 43 | ); |
| 44 | decompressed_tensor.copy_(reduce_tensor); | 44 | decompressed_tensor.copy_(reduce_tensor); |
| 45 | return c10::IValue(decompressed_tensor); | 45 | return c10::IValue(decompressed_tensor); |
| 46 | }; | 46 | }; |
| 47 | 47 | ||
| 48 | return allreduce_fut->then(decompress, allreduce_fut->elementType()); | 48 | return allreduce_fut->then(decompress, allreduce_fut->elementType()); |
| 49 | } | 49 | } |
| 50 | 50 | ||
| 51 | c10::intrusive_ptr<c10::ivalue::Future> _AllReduceBySumCommHook::runHook(GradBucket& bucket) | 51 | c10::intrusive_ptr<c10::ivalue::Future> _AllReduceBySumCommHook::runHook(GradBucket& bucket) |
| 52 | { | 52 | { |
| 53 | std::vector<at::Tensor> tensors = {bucket.getBufferRef()}; | 53 | std::vector<at::Tensor> tensors = {bucket.getBufferRef()}; |
| 54 | return state_->allreduce(tensors)->getFuture(); | 54 | return state_->allreduce(tensors)->getFuture(); |
| 55 | } | 55 | } |
| 56 | 56 | ||
| 57 | } // namespace c10d | 57 | } // namespace c10d |
| @@ -1,48 +1,48 @@ | |||
| 1 | #pragma once | 1 | #pragma once |
| 2 | 2 | ||
| 3 | #include <c10d/ProcessGroup.hpp> | 3 | #include <c10d/ProcessGroup.hpp> |
| 4 | #include <c10d/comm.hpp> | 4 | #include <c10d/comm.hpp> |
| 5 | 5 | ||
| 6 | namespace c10d { | 6 | namespace c10d { |
| 7 | 7 | ||
| 8 | enum class BuiltinCommHookType { | 8 | enum class BuiltinCommHookType { |
| 9 | ALLREDUCE = 1, | 9 | ALLREDUCE = 1, |
| 10 | FP16_COMPRESS = 2, | 10 | FP16_COMPRESS = 2, |
| 11 | }; | 11 | }; |
| 12 | 12 | ||
| 13 | class AllReduceCommHook : public CppCommHookInterface<c10::intrusive_ptr<ProcessGroup>> { | 13 | class AllReduceCommHook : public CppCommHookInterface<c10::intrusive_ptr<ProcessGroup>> { |
| 14 | public: | 14 | public: |
| 15 | explicit AllReduceCommHook(c10::intrusive_ptr<ProcessGroup> state) | 15 | explicit AllReduceCommHook(c10::intrusive_ptr<ProcessGroup> state) |
| 16 | : CppCommHookInterface<c10::intrusive_ptr<ProcessGroup>>(state) {} | 16 | : CppCommHookInterface<c10::intrusive_ptr<ProcessGroup>>(state) {} |
| 17 | 17 | ||
| 18 | ~AllReduceCommHook() override = default; | 18 | ~AllReduceCommHook() override = default; |
| 19 | 19 | ||
| 20 | c10::intrusive_ptr<c10::ivalue::Future> runHook(GradBucket& bucket) override; | 20 | c10::intrusive_ptr<c10::ivalue::Future> runHook(GradBucket& bucket) override; |
| 21 | }; | 21 | }; |
| 22 | 22 | ||
| 23 | class FP16CompressCommHook : public CppCommHookInterface<c10::intrusive_ptr<ProcessGroup>> { | 23 | class FP16CompressCommHook : public CppCommHookInterface<c10::intrusive_ptr<ProcessGroup>> { |
| 24 | public: | 24 | public: |
| 25 | explicit FP16CompressCommHook(c10::intrusive_ptr<ProcessGroup> state) | 25 | explicit FP16CompressCommHook(c10::intrusive_ptr<ProcessGroup> state) |
| 26 | : CppCommHookInterface<c10::intrusive_ptr<ProcessGroup>>(state) {} | 26 | : CppCommHookInterface<c10::intrusive_ptr<ProcessGroup>>(state) {} |
| 27 | 27 | ||
| 28 | ~FP16CompressCommHook() override = default; | 28 | ~FP16CompressCommHook() override = default; |
| 29 | 29 | ||
| 30 | c10::intrusive_ptr<c10::ivalue::Future> runHook(GradBucket& bucket) override; | 30 | c10::intrusive_ptr<c10::ivalue::Future> runHook(GradBucket& bucket) override; |
| 31 | }; | 31 | }; |
| 32 | 32 | ||
| 33 | // Almost same as AllReduceCommHook, but without division inside the hook. | 33 | // Almost same as AllReduceCommHook, but without division inside the hook. |
| 34 | // This enables the optimization of fusing copy and division and saves one scan | 34 | // This enables the optimization of fusing copy and division and saves one scan |
| 35 | // over all the input parameters, when no communication hook is provided by the user. | 35 | // over all the input parameters, when no communication hook is provided by the user. |
| 36 | // Only used internally and not released as a public built-in communication hook. | 36 | // Only used internally and not released as a public built-in communication hook. |
| 37 | class _AllReduceBySumCommHook | 37 | class _AllReduceBySumCommHook |
| 38 | : public CppCommHookInterface<c10::intrusive_ptr<ProcessGroup>> { | 38 | : public CppCommHookInterface<c10::intrusive_ptr<ProcessGroup>> { |
| 39 | public: | 39 | public: |
| 40 | explicit _AllReduceBySumCommHook(c10::intrusive_ptr<ProcessGroup> state) | 40 | explicit _AllReduceBySumCommHook(c10::intrusive_ptr<ProcessGroup> state) |
| 41 | : CppCommHookInterface<c10::intrusive_ptr<ProcessGroup>>(state) {} | 41 | : CppCommHookInterface<c10::intrusive_ptr<ProcessGroup>>(state) {} |
| 42 | 42 | ||
| 43 | ~_AllReduceBySumCommHook() override = default; | 43 | ~_AllReduceBySumCommHook() override = default; |
| 44 | 44 | ||
| 45 | c10::intrusive_ptr<c10::ivalue::Future> runHook(GradBucket& bucket) override; | 45 | c10::intrusive_ptr<c10::ivalue::Future> runHook(GradBucket& bucket) override; |
| 46 | }; | 46 | }; |
| 47 | 47 | ||
| 48 | } // namespace c10d | 48 | } // namespace c10d |
| @@ -1,622 +1,622 @@ | |||
| 1 | #pragma once | 1 | #pragma once |
| 2 | 2 | ||
| 3 | #include <atomic> | 3 | #include <atomic> |
| 4 | #include <memory> | 4 | #include <memory> |
| 5 | #include <mutex> | 5 | #include <mutex> |
| 6 | #include <tuple> | 6 | #include <tuple> |
| 7 | #include <unordered_map> | 7 | #include <unordered_map> |
| 8 | #include <vector> | 8 | #include <vector> |
| 9 | 9 | ||
| 10 | #include <ATen/core/ivalue_inl.h> | 10 | #include <ATen/core/ivalue_inl.h> |
| 11 | #include <c10/macros/Macros.h> | 11 | #include <c10/macros/Macros.h> |
| 12 | #include <c10/util/ApproximateClock.h> | 12 | #include <c10/util/ApproximateClock.h> |
| 13 | #include <c10/util/intrusive_ptr.h> | 13 | #include <c10/util/intrusive_ptr.h> |
| 14 | #include <c10d/ProcessGroup.hpp> | 14 | #include <c10d/ProcessGroup.hpp> |
| 15 | #include <c10d/Utils.hpp> | 15 | #include <c10d/Utils.hpp> |
| 16 | #include <c10d/Work.hpp> | 16 | #include <c10d/Work.hpp> |
| 17 | #include <c10d/comm.hpp> | 17 | #include <c10d/comm.hpp> |
| 18 | #include <c10d/default_comm_hooks.hpp> | 18 | #include <c10d/default_comm_hooks.hpp> |
| 19 | #include <torch/csrc/autograd/function.h> | 19 | #include <torch/csrc/autograd/function.h> |
| 20 | #include <torch/csrc/autograd/profiler.h> | 20 | #include <torch/csrc/autograd/profiler.h> |
| 21 | #include <torch/csrc/autograd/variable.h> | 21 | #include <torch/csrc/autograd/variable.h> |
| 22 | #ifndef _WIN32 | 22 | #ifndef _WIN32 |
| 23 | #include <torch/csrc/distributed/autograd/context/context.h> | 23 | #include <torch/csrc/distributed/autograd/context/context.h> |
| 24 | #endif | 24 | #endif |
| 25 | #include <c10d/logger.hpp> | 25 | #include <c10d/logger.hpp> |
| 26 | #include <c10d/debug.h> | 26 | #include <c10d/debug.h> |
| 27 | 27 | ||
| 28 | namespace c10d_npu { | 28 | namespace c10d_npu { |
| 29 | 29 | ||
| 30 | constexpr int kDefaultFirstBucketBytes = int(1024 * 1024); | 30 | constexpr int kDefaultFirstBucketBytes = int(1024 * 1024); |
| 31 | constexpr int kDefaultBucketBytesCap = int(25 * 1024 * 1024); | 31 | constexpr int kDefaultBucketBytesCap = int(25 * 1024 * 1024); |
| 32 | // Collect runtime stats once for every kDDPRuntimeLoggingSampleRate iterations. | 32 | // Collect runtime stats once for every kDDPRuntimeLoggingSampleRate iterations. |
| 33 | constexpr int kDDPRuntimeLoggingSampleRate = 100; | 33 | constexpr int kDDPRuntimeLoggingSampleRate = 100; |
| 34 | constexpr int kUnsetTime = -1; | 34 | constexpr int kUnsetTime = -1; |
| 35 | 35 | ||
| 36 | inline int64_t current_time_in_nanos() | 36 | inline int64_t current_time_in_nanos() |
| 37 | { | 37 | { |
| 38 | return c10::getTime(); | 38 | return c10::getTime(); |
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | // Forward declaration | 41 | // Forward declaration |
| 42 | class Logger; | 42 | class Logger; |
| 43 | 43 | ||
| 44 | class TORCH_API Timer { | 44 | class TORCH_API Timer { |
| 45 | private: | 45 | private: |
| 46 | // The timestamp of forward call start time in each iteration. | 46 | // The timestamp of forward call start time in each iteration. |
| 47 | int64_t forward_start_time = kUnsetTime; | 47 | int64_t forward_start_time = kUnsetTime; |
| 48 | // The timestamp of backward computation start and end time in each | 48 | // The timestamp of backward computation start and end time in each |
| 49 | // iteration. | 49 | // iteration. |
| 50 | int64_t backward_compute_start_time = kUnsetTime; | 50 | int64_t backward_compute_start_time = kUnsetTime; |
| 51 | int64_t backward_compute_end_time = kUnsetTime; | 51 | int64_t backward_compute_end_time = kUnsetTime; |
| 52 | // The timestamp of first communication call start time in each iteration. | 52 | // The timestamp of first communication call start time in each iteration. |
| 53 | int64_t backward_comm_start_time = kUnsetTime; | 53 | int64_t backward_comm_start_time = kUnsetTime; |
| 54 | // The timestamp of last communication call end time in each iteration. | 54 | // The timestamp of last communication call end time in each iteration. |
| 55 | int64_t backward_comm_end_time = kUnsetTime; | 55 | int64_t backward_comm_end_time = kUnsetTime; |
| 56 | public: | 56 | public: |
| 57 | enum class Event { | 57 | enum class Event { |
| 58 | kForwardStart, | 58 | kForwardStart, |
| 59 | kBackwardComputeStart, | 59 | kBackwardComputeStart, |
| 60 | kBackwardComputeEnd, | 60 | kBackwardComputeEnd, |
| 61 | kBackwardCommStart, | 61 | kBackwardCommStart, |
| 62 | kBackwardCommEnd, | 62 | kBackwardCommEnd, |
| 63 | }; | 63 | }; |
| 64 | 64 | ||
| 65 | // Record the current event, i.e., mark it as having occurred now. Default | 65 | // Record the current event, i.e., mark it as having occurred now. Default |
| 66 | // CPU implementation. | 66 | // CPU implementation. |
| 67 | virtual void record(Event event) { | 67 | virtual void record(Event event) { |
| 68 | getTimeRef(event) = current_time_in_nanos(); | 68 | getTimeRef(event) = current_time_in_nanos(); |
| 69 | } | 69 | } |
| 70 | 70 | ||
| 71 | // Return the difference between when two events occurred, in nanoseconds. | 71 | // Return the difference between when two events occurred, in nanoseconds. |
| 72 | // Or nullopt if one of them hasn't been recorded. | 72 | // Or nullopt if one of them hasn't been recorded. |
| 73 | virtual c10::optional<int64_t> measureDifference(Event start, Event end) = 0; | 73 | virtual c10::optional<int64_t> measureDifference(Event start, Event end) = 0; |
| 74 | 74 | ||
| 75 | virtual ~Timer() = default; | 75 | virtual ~Timer() = default; |
| 76 | 76 | ||
| 77 | // Return host-side timestamp, or nullopt if it has not yet been recorded. | 77 | // Return host-side timestamp, or nullopt if it has not yet been recorded. |
| 78 | c10::optional<int64_t> getTimestamp(Event event) { | 78 | c10::optional<int64_t> getTimestamp(Event event) { |
| 79 | auto time = getTimeRef(event); | 79 | auto time = getTimeRef(event); |
| 80 | if (time == kUnsetTime) { | 80 | if (time == kUnsetTime) { |
| 81 | return c10::nullopt; | 81 | return c10::nullopt; |
| 82 | } else { | 82 | } else { |
| 83 | return time; | 83 | return time; |
| 84 | } | 84 | } |
| 85 | } | 85 | } |
| 86 | 86 | ||
| 87 | // Return host-side time member variable corresponding to the given event. | 87 | // Return host-side time member variable corresponding to the given event. |
| 88 | int64_t& getTimeRef(Event event) { | 88 | int64_t& getTimeRef(Event event) { |
| 89 | switch (event) { | 89 | switch (event) { |
| 90 | case Event::kForwardStart: | 90 | case Event::kForwardStart: |
| 91 | return forward_start_time; | 91 | return forward_start_time; |
| 92 | case Event::kBackwardComputeStart: | 92 | case Event::kBackwardComputeStart: |
| 93 | return backward_compute_start_time; | 93 | return backward_compute_start_time; |
| 94 | case Event::kBackwardComputeEnd: | 94 | case Event::kBackwardComputeEnd: |
| 95 | return backward_compute_end_time; | 95 | return backward_compute_end_time; |
| 96 | case Event::kBackwardCommStart: | 96 | case Event::kBackwardCommStart: |
| 97 | return backward_comm_start_time; | 97 | return backward_comm_start_time; |
| 98 | case Event::kBackwardCommEnd: | 98 | case Event::kBackwardCommEnd: |
| 99 | return backward_comm_end_time; | 99 | return backward_comm_end_time; |
| 100 | default: | 100 | default: |
| 101 | TORCH_INTERNAL_ASSERT(false); | 101 | TORCH_INTERNAL_ASSERT(false); |
| 102 | } | 102 | } |
| 103 | } | 103 | } |
| 104 | }; | 104 | }; |
| 105 | 105 | ||
| 106 | // Local accumulator type for a single bucket. | 106 | // Local accumulator type for a single bucket. |
| 107 | struct BucketAccumulator { | 107 | struct BucketAccumulator { |
| 108 | std::vector<size_t> indices; | 108 | std::vector<size_t> indices; |
| 109 | size_t size = 0; | 109 | size_t size = 0; |
| 110 | size_t size_limit = 0; | 110 | size_t size_limit = 0; |
| 111 | }; | 111 | }; |
| 112 | 112 | ||
| 113 | C10_DECLARE_TYPED_REGISTRY(TimerRegistry, c10::DeviceType, Timer, std::unique_ptr, c10::Device); | 113 | C10_DECLARE_TYPED_REGISTRY(TimerRegistry, c10::DeviceType, Timer, std::unique_ptr, c10::Device); |
| 114 | 114 | ||
| 115 | class Reducer { | 115 | class Reducer { |
| 116 | public: | 116 | public: |
| 117 | // The constructor takes a list of variables for every model replica. | 117 | // The constructor takes a list of variables for every model replica. |
| 118 | // The bucket assignment for this reducer is specified as a list of | 118 | // The bucket assignment for this reducer is specified as a list of |
| 119 | // buckets, each of which is specified as a list of indices into the | 119 | // buckets, each of which is specified as a list of indices into the |
| 120 | // variables list for **a single replica** (i.e. `variables[0]`). | 120 | // variables list for **a single replica** (i.e. `variables[0]`). |
| 121 | explicit Reducer( | 121 | explicit Reducer( |
| 122 | std::vector<at::Tensor> params, | 122 | std::vector<at::Tensor> params, |
| 123 | std::vector<std::vector<size_t>> bucket_indices, | 123 | std::vector<std::vector<size_t>> bucket_indices, |
| 124 | std::vector<size_t> per_bucket_size_limits, | 124 | std::vector<size_t> per_bucket_size_limits, |
| 125 | c10::intrusive_ptr<c10d::ProcessGroup> process_group, | 125 | c10::intrusive_ptr<c10d::ProcessGroup> process_group, |
| 126 | std::vector<bool> expect_sparse_gradients, | 126 | std::vector<bool> expect_sparse_gradients, |
| 127 | int64_t bucket_bytes_cap, | 127 | int64_t bucket_bytes_cap, |
| 128 | bool find_unused_parameters, | 128 | bool find_unused_parameters, |
| 129 | bool gradient_as_bucket_view, | 129 | bool gradient_as_bucket_view, |
| 130 | std::unordered_map<size_t, std::string> paramNames, | 130 | std::unordered_map<size_t, std::string> paramNames, |
| 131 | int64_t first_bucket_bytes_cap); | 131 | int64_t first_bucket_bytes_cap); |
| 132 | 132 | ||
| 133 | ~Reducer() noexcept(false); | 133 | ~Reducer() noexcept(false); |
| 134 | 134 | ||
| 135 | // To (re-)initialize bucket assignment, pass a list of buckets, each | 135 | // To (re-)initialize bucket assignment, pass a list of buckets, each |
| 136 | // of which is specified by a list of indices in the variables list. | 136 | // of which is specified by a list of indices in the variables list. |
| 137 | // This function performs validation that the variables within a bucket | 137 | // This function performs validation that the variables within a bucket |
| 138 | // all live on the same device and have the same dimensionality. | 138 | // all live on the same device and have the same dimensionality. |
| 139 | void initialize_buckets( | 139 | void initialize_buckets( |
| 140 | std::vector<std::vector<size_t>> bucket_indices, | 140 | std::vector<std::vector<size_t>> bucket_indices, |
| 141 | std::vector<size_t> per_bucket_sizes); | 141 | std::vector<size_t> per_bucket_sizes); |
| 142 | 142 | ||
| 143 | // This function is called when the forward function has produced an output, | 143 | // This function is called when the forward function has produced an output, |
| 144 | // and the user wishes to reduce gradients in the backwards pass. | 144 | // and the user wishes to reduce gradients in the backwards pass. |
| 145 | // If they don't, and wish to accumulate gradients before reducing them, | 145 | // If they don't, and wish to accumulate gradients before reducing them, |
| 146 | // a call to this function can simply be omitted. | 146 | // a call to this function can simply be omitted. |
| 147 | void prepare_for_backward(const std::vector<at::Tensor>& outputs); | 147 | void prepare_for_backward(const std::vector<at::Tensor>& outputs); |
| 148 | 148 | ||
| 149 | // Called at the begginning of forward() inside DistributedDataParallel, | 149 | // Called at the begginning of forward() inside DistributedDataParallel, |
| 150 | // right now it caputures the starting time of forward in each iteration. | 150 | // right now it caputures the starting time of forward in each iteration. |
| 151 | void prepare_for_forward(); | 151 | void prepare_for_forward(); |
| 152 | 152 | ||
| 153 | // Returns the relative time in nanoseconds when gradients were ready, | 153 | // Returns the relative time in nanoseconds when gradients were ready, |
| 154 | // with respect to the time `prepare_for_backward` was called. The | 154 | // with respect to the time `prepare_for_backward` was called. The |
| 155 | // vector is for parameters for a single model replica. | 155 | // vector is for parameters for a single model replica. |
| 156 | std::vector<int64_t> get_backward_stats() const | 156 | std::vector<int64_t> get_backward_stats() const |
| 157 | { | 157 | { |
| 158 | return backward_stats_; | 158 | return backward_stats_; |
| 159 | } | 159 | } |
| 160 | 160 | ||
| 161 | // Registers a hook to the reducer. The hook is `CommHookInterface` | 161 | // Registers a hook to the reducer. The hook is `CommHookInterface` |
| 162 | // type to allow both Python and CPP hooks. This function can only | 162 | // type to allow both Python and CPP hooks. This function can only |
| 163 | // be called once before calling backward. | 163 | // be called once before calling backward. |
| 164 | // Cannot combine with the call of `register_builtin_comm_hook`. | 164 | // Cannot combine with the call of `register_builtin_comm_hook`. |
| 165 | void register_comm_hook(std::unique_ptr<c10d::CommHookInterface> iface); | 165 | void register_comm_hook(std::unique_ptr<c10d::CommHookInterface> iface); |
| 166 | 166 | ||
| 167 | // Registers a built-in C++ comm hook to the reducer. This function can only | 167 | // Registers a built-in C++ comm hook to the reducer. This function can only |
| 168 | // be called once before calling backward. | 168 | // be called once before calling backward. |
| 169 | // Cannot combine with the call of `register_comm_hook`. | 169 | // Cannot combine with the call of `register_comm_hook`. |
| 170 | void register_builtin_comm_hook(c10d::BuiltinCommHookType comm_hook_type); | 170 | void register_builtin_comm_hook(c10d::BuiltinCommHookType comm_hook_type); |
| 171 | 171 | ||
| 172 | // Runs allreduce or installed communication hook given GradBucket instance. | 172 | // Runs allreduce or installed communication hook given GradBucket instance. |
| 173 | c10::intrusive_ptr<c10::ivalue::Future> run_comm_hook( | 173 | c10::intrusive_ptr<c10::ivalue::Future> run_comm_hook( |
| 174 | c10d::GradBucket& grad_bucket); | 174 | c10d::GradBucket& grad_bucket); |
| 175 | 175 | ||
| 176 | // Runs default allreduce hook. | 176 | // Runs default allreduce hook. |
| 177 | c10::intrusive_ptr<c10::ivalue::Future> run_allreduce_hook( | 177 | c10::intrusive_ptr<c10::ivalue::Future> run_allreduce_hook( |
| 178 | c10d::GradBucket& grad_bucket); | 178 | c10d::GradBucket& grad_bucket); |
| 179 | 179 | ||
| 180 | // Returns gradient buckets in sequential order of buckets_. This is the order | 180 | // Returns gradient buckets in sequential order of buckets_. This is the order |
| 181 | // in which buckets are reduced across processes. If return_zero_tensors=true, | 181 | // in which buckets are reduced across processes. If return_zero_tensors=true, |
| 182 | // will return zero tensors of the same shape instead of the true tensors. | 182 | // will return zero tensors of the same shape instead of the true tensors. |
| 183 | std::vector<c10d::GradBucket> get_grad_buckets( | 183 | std::vector<c10d::GradBucket> get_grad_buckets( |
| 184 | bool return_zero_tensors = true) const; | 184 | bool return_zero_tensors = true) const; |
| 185 | 185 | ||
| 186 | // Rebuild buckets based on rebuilt_params_ and rebuilt_param_indices_ | 186 | // Rebuild buckets based on rebuilt_params_ and rebuilt_param_indices_ |
| 187 | // according to when tensors received grads in the backward pass. | 187 | // according to when tensors received grads in the backward pass. |
| 188 | bool rebuild_buckets(); | 188 | bool rebuild_buckets(); |
| 189 | 189 | ||
| 190 | // Install futures that should be awaited at end of backwards. Currently these | 190 | // Install futures that should be awaited at end of backwards. Currently these |
| 191 | // are only used by user-defined custom buffer reduction hooks, but can be generalized | 191 | // are only used by user-defined custom buffer reduction hooks, but can be generalized |
| 192 | // to any user-originating futures that need to be awaited. | 192 | // to any user-originating futures that need to be awaited. |
| 193 | void install_futures(c10::List<c10::intrusive_ptr<c10::ivalue::Future>> futs); | 193 | void install_futures(c10::List<c10::intrusive_ptr<c10::ivalue::Future>> futs); |
| 194 | 194 | ||
| 195 | // Returns true if we should rebuild buckets, else false. We only rebuild | 195 | // Returns true if we should rebuild buckets, else false. We only rebuild |
| 196 | // buckets once after the first iteration and never rebuild them if | 196 | // buckets once after the first iteration and never rebuild them if |
| 197 | // find_unused_parameters_. | 197 | // find_unused_parameters_. |
| 198 | inline bool should_rebuild_buckets() const | 198 | inline bool should_rebuild_buckets() const |
| 199 | { | 199 | { |
| 200 | return (static_graph_ || !find_unused_parameters_) && !has_rebuilt_bucket_; | 200 | return (static_graph_ || !find_unused_parameters_) && !has_rebuilt_bucket_; |
| 201 | } | 201 | } |
| 202 | 202 | ||
| 203 | // Pushes all parameters to be rebuilt. | 203 | // Pushes all parameters to be rebuilt. |
| 204 | void push_rebuilt_params_for_all_indices(); | 204 | void push_rebuilt_params_for_all_indices(); |
| 205 | 205 | ||
| 206 | // Creates and sets ForwardPassWorkHandle given a ProcessGroup::Work and the | 206 | // Creates and sets ForwardPassWorkHandle given a ProcessGroup::Work and the |
| 207 | // corresponding tensor being reduced. | 207 | // corresponding tensor being reduced. |
| 208 | void set_forward_pass_work_handle( | 208 | void set_forward_pass_work_handle( |
| 209 | c10::intrusive_ptr<c10d::Work> forwardPassWorkHandle, | 209 | c10::intrusive_ptr<c10d::Work> forwardPassWorkHandle, |
| 210 | bool useStaticWorldSize); | 210 | bool useStaticWorldSize); |
| 211 | 211 | ||
| 212 | // Retrieve on-device tensors used to track locally unused parameters. It is | 212 | // Retrieve on-device tensors used to track locally unused parameters. It is |
| 213 | // a tensor where index i = 1 if the Variable with that index has been used. | 213 | // a tensor where index i = 1 if the Variable with that index has been used. |
| 214 | at::Tensor get_local_used_map_on_device() const; | 214 | at::Tensor get_local_used_map_on_device() const; |
| 215 | 215 | ||
| 216 | // An function for users to set sample_rate of collecting | 216 | // An function for users to set sample_rate of collecting |
| 217 | // runtime stats. The time stats will be recorded for the | 217 | // runtime stats. The time stats will be recorded for the |
| 218 | // first 10 iterations, after 10 iteratons time stats will be | 218 | // first 10 iterations, after 10 iteratons time stats will be |
| 219 | // recorded once every "sample_rate" training iterations. | 219 | // recorded once every "sample_rate" training iterations. |
| 220 | void set_ddp_runtime_logging_sample_rate(int sample_rate); | 220 | void set_ddp_runtime_logging_sample_rate(int sample_rate); |
| 221 | 221 | ||
| 222 | // Specify the training graph is static. | 222 | // Specify the training graph is static. |
| 223 | void set_static_graph(); | 223 | void set_static_graph(); |
| 224 | 224 | ||
| 225 | // Delay all reduce to be after all gradients' calculation is complete. | 225 | // Delay all reduce to be after all gradients' calculation is complete. |
| 226 | void delay_all_reduce(); | 226 | void delay_all_reduce(); |
| 227 | 227 | ||
| 228 | // Weak reference to associated DDP logger. The reference is weak to avoid | 228 | // Weak reference to associated DDP logger. The reference is weak to avoid |
| 229 | // refcycle between reducer and logger. | 229 | // refcycle between reducer and logger. |
| 230 | void set_logger(std::weak_ptr<c10d::Logger> logger); | 230 | void set_logger(std::weak_ptr<c10d::Logger> logger); |
| 231 | 231 | ||
| 232 | // When graph is not explicitly set by user as static and has unused | 232 | // When graph is not explicitly set by user as static and has unused |
| 233 | // parameters, this will return whether the graph has been static until the | 233 | // parameters, this will return whether the graph has been static until the |
| 234 | // current iteration, which means unused params set has not changed. | 234 | // current iteration, which means unused params set has not changed. |
| 235 | bool ddp_graph_static(); | 235 | bool ddp_graph_static(); |
| 236 | 236 | ||
| 237 | protected: | 237 | protected: |
| 238 | // Forward declaration. | 238 | // Forward declaration. |
| 239 | struct Bucket; | 239 | struct Bucket; |
| 240 | 240 | ||
| 241 | void push_rebuilt_params(const size_t& index); | 241 | void push_rebuilt_params(const size_t& index); |
| 242 | 242 | ||
| 243 | // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) | 243 | // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) |
| 244 | mutable std::mutex mutex_; | 244 | mutable std::mutex mutex_; |
| 245 | // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) | 245 | // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) |
| 246 | const std::vector<at::Tensor> params_; | 246 | const std::vector<at::Tensor> params_; |
| 247 | // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) | 247 | // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) |
| 248 | const c10::intrusive_ptr<::c10d::ProcessGroup> process_group_; | 248 | const c10::intrusive_ptr<::c10d::ProcessGroup> process_group_; |
| 249 | // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) | 249 | // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) |
| 250 | std::vector<bool> expect_sparse_gradients_; | 250 | std::vector<bool> expect_sparse_gradients_; |
| 251 | 251 | ||
| 252 | std::vector<std::shared_ptr<torch::autograd::Node>> | 252 | std::vector<std::shared_ptr<torch::autograd::Node>> |
| 253 | grad_accumulators_; // NOLINT(cppcoreguidelines-non-private-member-variables-in-classes) | 253 | grad_accumulators_; // NOLINT(cppcoreguidelines-non-private-member-variables-in-classes) |
| 254 | // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) | 254 | // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) |
| 255 | std::unordered_map<torch::autograd::Node*, size_t> gradAccToVariableMap_; | 255 | std::unordered_map<torch::autograd::Node*, size_t> gradAccToVariableMap_; |
| 256 | std::vector<std::pair<uintptr_t, std::shared_ptr<torch::autograd::Node>>> | 256 | std::vector<std::pair<uintptr_t, std::shared_ptr<torch::autograd::Node>>> |
| 257 | hooks_; // NOLINT(cppcoreguidelines-non-private-member-variables-in-classes) | 257 | hooks_; // NOLINT(cppcoreguidelines-non-private-member-variables-in-classes) |
| 258 | 258 | ||
| 259 | // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) | 259 | // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) |
| 260 | bool expect_autograd_hooks_; | 260 | bool expect_autograd_hooks_; |
| 261 | // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) | 261 | // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) |
| 262 | bool require_finalize_; | 262 | bool require_finalize_; |
| 263 | // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) | 263 | // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) |
| 264 | size_t next_bucket_; | 264 | size_t next_bucket_; |
| 265 | 265 | ||
| 266 | // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) | 266 | // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) |
| 267 | bool has_marked_unused_parameters_; | 267 | bool has_marked_unused_parameters_; |
| 268 | // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) | 268 | // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) |
| 269 | const bool find_unused_parameters_; | 269 | const bool find_unused_parameters_; |
| 270 | // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) | 270 | // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) |
| 271 | const bool gradient_as_bucket_view_; | 271 | const bool gradient_as_bucket_view_; |
| 272 | // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) | 272 | // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) |
| 273 | std::vector<size_t> unused_parameters_; | 273 | std::vector<size_t> unused_parameters_; |
| 274 | // Previous iteration's unused params, used for checking if unused parameters | 274 | // Previous iteration's unused params, used for checking if unused parameters |
| 275 | // change between iterations. Only filled during the first backwards call. | 275 | // change between iterations. Only filled during the first backwards call. |
| 276 | // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) | 276 | // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) |
| 277 | std::vector<size_t> prev_iteration_unused_parameters_; | 277 | std::vector<size_t> prev_iteration_unused_parameters_; |
| 278 | // Whether graph is static or not. When user does not explicitly set static | 278 | // Whether graph is static or not. When user does not explicitly set static |
| 279 | // graph, the only possible dynamism is set of unused parameters changing | 279 | // graph, the only possible dynamism is set of unused parameters changing |
| 280 | // between iterations which is tracked by this flag. | 280 | // between iterations which is tracked by this flag. |
| 281 | // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) | 281 | // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) |
| 282 | bool ddp_graph_static_{true}; | 282 | bool ddp_graph_static_{true}; |
| 283 | // Locally used parameter maps indicating if parameters are used locally | 283 | // Locally used parameter maps indicating if parameters are used locally |
| 284 | // during the current iteration or no_sync session if no_sync is on. | 284 | // during the current iteration or no_sync session if no_sync is on. |
| 285 | // Each map is a one-dim int32 tensor of number of parameters. These tensors | 285 | // Each map is a one-dim int32 tensor of number of parameters. These tensors |
| 286 | // are marked in autograd_hook to indicate the corresponding param has been | 286 | // are marked in autograd_hook to indicate the corresponding param has been |
| 287 | // used, and get allreduced in the end of backward step of current iteration | 287 | // used, and get allreduced in the end of backward step of current iteration |
| 288 | // or no_sync session for figuring out the globally unused parameters. | 288 | // or no_sync session for figuring out the globally unused parameters. |
| 289 | // | 289 | // |
| 290 | // local_used_map_: CPU tensor for bookkeeping locally used params | 290 | // local_used_map_: CPU tensor for bookkeeping locally used params |
| 291 | // local_used_map_dev_: dev tensor for reducing globally unused params | 291 | // local_used_map_dev_: dev tensor for reducing globally unused params |
| 292 | at::Tensor local_used_map_; | 292 | at::Tensor local_used_map_; |
| 293 | at::Tensor local_used_map_dev_; | 293 | at::Tensor local_used_map_dev_; |
| 294 | // Indicate that reduction is done and D2H copy is done as well. | 294 | // Indicate that reduction is done and D2H copy is done as well. |
| 295 | bool local_used_map_reduced_; | 295 | bool local_used_map_reduced_; |
| 296 | 296 | ||
| 297 | // Weak pointer to associated DDP logger. | 297 | // Weak pointer to associated DDP logger. |
| 298 | std::weak_ptr<c10d::Logger> logger_; | 298 | std::weak_ptr<c10d::Logger> logger_; |
| 299 | // List of futures installed by Reducer::install_futures that should be awaited | 299 | // List of futures installed by Reducer::install_futures that should be awaited |
| 300 | // at the end of backwards pass. | 300 | // at the end of backwards pass. |
| 301 | c10::optional<c10::List<c10::intrusive_ptr<c10::ivalue::Future>>> installed_futures_{c10::nullopt}; | 301 | c10::optional<c10::List<c10::intrusive_ptr<c10::ivalue::Future>>> installed_futures_{c10::nullopt}; |
| 302 | 302 | ||
| 303 | // Work handle for allreduce on local_used_map_ | 303 | // Work handle for allreduce on local_used_map_ |
| 304 | c10::intrusive_ptr<c10d::Work> local_used_work_; | 304 | c10::intrusive_ptr<c10d::Work> local_used_work_; |
| 305 | 305 | ||
| 306 | void mark_variable_ready_dense(size_t variable_index); | 306 | void mark_variable_ready_dense(size_t variable_index); |
| 307 | 307 | ||
| 308 | void mark_variable_ready_sparse(size_t variable_index); | 308 | void mark_variable_ready_sparse(size_t variable_index); |
| 309 | 309 | ||
| 310 | void mark_variable_ready(size_t variable_index); | 310 | void mark_variable_ready(size_t variable_index); |
| 311 | 311 | ||
| 312 | void autograd_hook(size_t index); | 312 | void autograd_hook(size_t index); |
| 313 | 313 | ||
| 314 | void mark_bucket_ready(size_t bucket_index); | 314 | void mark_bucket_ready(size_t bucket_index); |
| 315 | 315 | ||
| 316 | void finalize_bucket_dense(Bucket& replica); | 316 | void finalize_bucket_dense(Bucket& replica); |
| 317 | 317 | ||
| 318 | void finalize_backward(); | 318 | void finalize_backward(); |
| 319 | 319 | ||
| 320 | // Returns list of model parameters corresponding to the given bucket. | 320 | // Returns list of model parameters corresponding to the given bucket. |
| 321 | // bucket_index is a key to cache after buckets are rebuilt, after which this | 321 | // bucket_index is a key to cache after buckets are rebuilt, after which this |
| 322 | // mapping never changes. | 322 | // mapping never changes. |
| 323 | std::vector<at::Tensor> get_variables_for_bucket( | 323 | std::vector<at::Tensor> get_variables_for_bucket( |
| 324 | size_t bucket_index, const Bucket& bucket) const; | 324 | size_t bucket_index, const Bucket& bucket) const; |
| 325 | 325 | ||
| 326 | // Asserts that the reduction for the previous iteration has finished before | 326 | // Asserts that the reduction for the previous iteration has finished before |
| 327 | // rebuilding buckets or kicking off the next one. | 327 | // rebuilding buckets or kicking off the next one. |
| 328 | void ensure_prior_reduction_finished(); | 328 | void ensure_prior_reduction_finished(); |
| 329 | 329 | ||
| 330 | // Broadcast rebuilt buckets from rank 0 to other ranks before initializing | 330 | // Broadcast rebuilt buckets from rank 0 to other ranks before initializing |
| 331 | // the buckets | 331 | // the buckets |
| 332 | void sync_bucket_indices(std::vector<std::vector<size_t>>& bucket_indices); | 332 | void sync_bucket_indices(std::vector<std::vector<size_t>>& bucket_indices); |
| 333 | 333 | ||
| 334 | // We'd like to use DistAutogradContext::GradCallback here but dist autograd | 334 | // We'd like to use DistAutogradContext::GradCallback here but dist autograd |
| 335 | // doesn't exist under Windows. So we just directly use the concrete type but | 335 | // doesn't exist under Windows. So we just directly use the concrete type but |
| 336 | // to preserve and enforce our original intent we do a static assert when dist | 336 | // to preserve and enforce our original intent we do a static assert when dist |
| 337 | // autograd is available. | 337 | // autograd is available. |
| 338 | using GradCallback = std::function<bool(at::Tensor&)>; | 338 | using GradCallback = std::function<bool(at::Tensor&)>; |
| 339 | #ifndef _WIN32 | 339 | #ifndef _WIN32 |
| 340 | static_assert( | 340 | static_assert( |
| 341 | std::is_same< | 341 | std::is_same< |
| 342 | GradCallback, | 342 | GradCallback, |
| 343 | torch::distributed::autograd::DistAutogradContext::GradCallback>:: | 343 | torch::distributed::autograd::DistAutogradContext::GradCallback>:: |
| 344 | value, | 344 | value, |
| 345 | ""); | 345 | ""); |
| 346 | #endif | 346 | #endif |
| 347 | void runGradCallbackForVariable(at::Tensor& variable, GradCallback&& cb); | 347 | void runGradCallbackForVariable(at::Tensor& variable, GradCallback&& cb); |
| 348 | 348 | ||
| 349 | // A bucket replica represents [1..N] gradients to be reduced, | 349 | // A bucket replica represents [1..N] gradients to be reduced, |
| 350 | // with the same dtype, on the same device. | 350 | // with the same dtype, on the same device. |
| 351 | // | 351 | // |
| 352 | // Batching gradients together before reducing them can result in lower | 352 | // Batching gradients together before reducing them can result in lower |
| 353 | // overhead and/or faster time to completion. Only gradients of the same type | 353 | // overhead and/or faster time to completion. Only gradients of the same type |
| 354 | // and on the same device can be batched. The tensor that represents the | 354 | // and on the same device can be batched. The tensor that represents the |
| 355 | // flattened gradient uses the same type and is placed on the same device. | 355 | // flattened gradient uses the same type and is placed on the same device. |
| 356 | // Buckets are filled as the gradients they hold are computed (triggered by | 356 | // Buckets are filled as the gradients they hold are computed (triggered by |
| 357 | // autograd hooks). Buckets are reduced in a predetermined order that is | 357 | // autograd hooks). Buckets are reduced in a predetermined order that is |
| 358 | // identical across processes. | 358 | // identical across processes. |
| 359 | struct BucketReplica { | 359 | struct BucketReplica { |
| 360 | // Flattened (1 dimensional) contents of bucket. | 360 | // Flattened (1 dimensional) contents of bucket. |
| 361 | at::Tensor contents; | 361 | at::Tensor contents; |
| 362 | 362 | ||
| 363 | // Views into contents for each grad. Each view will be created with | 363 | // Views into contents for each grad. Each view will be created with |
| 364 | // layout (sizes + strides) matching the grad's expected layout | 364 | // layout (sizes + strides) matching the grad's expected layout |
| 365 | // ("Gradient Layout Contract" in torch/csrc/autograd/AccumulateGrad.h). | 365 | // ("Gradient Layout Contract" in torch/csrc/autograd/AccumulateGrad.h). |
| 366 | // `bucket_views_in[i].copy_(grad)` and | 366 | // `bucket_views_in[i].copy_(grad)` and |
| 367 | // `grad.copy_(bucket_views_out[i])` | 367 | // `grad.copy_(bucket_views_out[i])` |
| 368 | // provide convenient ways to move grad data in/out of contents. | 368 | // provide convenient ways to move grad data in/out of contents. |
| 369 | // The reason we keep two states for bucket_views is that if DDP | 369 | // The reason we keep two states for bucket_views is that if DDP |
| 370 | // communication hook was registered, `bucket_views_out` could be | 370 | // communication hook was registered, `bucket_views_out` could be |
| 371 | // re-initialized with the value of hook's `future_work`. We still need to | 371 | // re-initialized with the value of hook's `future_work`. We still need to |
| 372 | // keep a separate view reference to replica's original contents for | 372 | // keep a separate view reference to replica's original contents for |
| 373 | // `bucket_views_in[i].copy_(grad)` call. | 373 | // `bucket_views_in[i].copy_(grad)` call. |
| 374 | std::vector<at::Tensor> bucket_views_in; | 374 | std::vector<at::Tensor> bucket_views_in; |
| 375 | std::vector<at::Tensor> bucket_views_out; | 375 | std::vector<at::Tensor> bucket_views_out; |
| 376 | 376 | ||
| 377 | // Variables that contribute to this bucket replica. Use refcounted value | 377 | // Variables that contribute to this bucket replica. Use refcounted value |
| 378 | // here so that we can easily unflatten the bucket contents into the | 378 | // here so that we can easily unflatten the bucket contents into the |
| 379 | // participating variables after reduction has completed. | 379 | // participating variables after reduction has completed. |
| 380 | std::vector<at::Tensor> variables; | 380 | std::vector<at::Tensor> variables; |
| 381 | 381 | ||
| 382 | // Per-variable offset/length into the flat bucket contents tensor and grad | 382 | // Per-variable offset/length into the flat bucket contents tensor and grad |
| 383 | // bucket. | 383 | // bucket. |
| 384 | std::vector<size_t> offsets; | 384 | std::vector<size_t> offsets; |
| 385 | std::vector<size_t> lengths; | 385 | std::vector<size_t> lengths; |
| 386 | 386 | ||
| 387 | // Per-variable sizes into the grad bucekt. | 387 | // Per-variable sizes into the grad bucekt. |
| 388 | std::vector<c10::IntArrayRef> sizes_vec; | 388 | std::vector<c10::IntArrayRef> sizes_vec; |
| 389 | 389 | ||
| 390 | // Number of tensors to be added before this bucket is complete. | 390 | // Number of tensors to be added before this bucket is complete. |
| 391 | // This is reset to `variables.size()` every iteration. | 391 | // This is reset to `variables.size()` every iteration. |
| 392 | size_t pending; | 392 | size_t pending; |
| 393 | }; | 393 | }; |
| 394 | 394 | ||
| 395 | // This function is called inside `initialize_buckets`, it initializes both | 395 | // This function is called inside `initialize_buckets`, it initializes both |
| 396 | // bucket_views_in and bucket_views_out into the contents tensor for each | 396 | // bucket_views_in and bucket_views_out into the contents tensor for each |
| 397 | // variable's grad. Views serve as entry points to copy_ each grad's data | 397 | // variable's grad. Views serve as entry points to copy_ each grad's data |
| 398 | // in/out of the flat contents tensor. | 398 | // in/out of the flat contents tensor. |
| 399 | void initialize_bucket_views(BucketReplica& replica, at::Tensor& contents); | 399 | void initialize_bucket_views(BucketReplica& replica, at::Tensor& contents); |
| 400 | 400 | ||
| 401 | // This function is called inside `finalize_backward`, it happens only if | 401 | // This function is called inside `finalize_backward`, it happens only if |
| 402 | // DDP communication hook was registered to recreate just bucket_views_out | 402 | // DDP communication hook was registered to recreate just bucket_views_out |
| 403 | // with the result of `future_work`. | 403 | // with the result of `future_work`. |
| 404 | void populate_bucket_views_out(BucketReplica& replica, at::Tensor& tensor) const; | 404 | void populate_bucket_views_out(BucketReplica& replica, at::Tensor& tensor) const; |
| 405 | 405 | ||
| 406 | // If gradient_as_bucket_view_ is false, after allreduce buckets, | 406 | // If gradient_as_bucket_view_ is false, after allreduce buckets, |
| 407 | // copy bucket results back to grads. | 407 | // copy bucket results back to grads. |
| 408 | void copy_bucket_to_grad( | 408 | void copy_bucket_to_grad( |
| 409 | at::Tensor& variable, | 409 | at::Tensor& variable, |
| 410 | Reducer::BucketReplica& replica, | 410 | Reducer::BucketReplica& replica, |
| 411 | size_t intra_bucket_index, | 411 | size_t intra_bucket_index, |
| 412 | bool global_unused); | 412 | bool global_unused); |
| 413 | // Check layout of grad and bucket_view before copying the grad to bucket. | 413 | // Check layout of grad and bucket_view before copying the grad to bucket. |
| 414 | void check_grad_layout(const at::Tensor& grad, const at::Tensor& bucket_view); | 414 | void check_grad_layout(const at::Tensor& grad, const at::Tensor& bucket_view); |
| 415 | // If gradient_as_bucket_view_ is false, before allreduce buckets, | 415 | // If gradient_as_bucket_view_ is false, before allreduce buckets, |
| 416 | // copy grads to buckets. | 416 | // copy grads to buckets. |
| 417 | void copy_grad_to_bucket(const at::Tensor& grad, at::Tensor& bucket_view); | 417 | void copy_grad_to_bucket(const at::Tensor& grad, at::Tensor& bucket_view); |
| 418 | // A bucket holds N bucket replicas (1 per model replica). | 418 | // A bucket holds N bucket replicas (1 per model replica). |
| 419 | // | 419 | // |
| 420 | // If every bucket in this struct is ready, the reduction can be kicked off. | 420 | // If every bucket in this struct is ready, the reduction can be kicked off. |
| 421 | // One bucket per replica. Reduction is kicked off when every bucket is ready. | 421 | // One bucket per replica. Reduction is kicked off when every bucket is ready. |
| 422 | // | 422 | // |
| 423 | struct Bucket { | 423 | struct Bucket { |
| 424 | std::vector<BucketReplica> replicas; | 424 | std::vector<BucketReplica> replicas; |
| 425 | 425 | ||
| 426 | // Global indices of participating variables in the bucket | 426 | // Global indices of participating variables in the bucket |
| 427 | std::vector<size_t> variable_indices; | 427 | std::vector<size_t> variable_indices; |
| 428 | 428 | ||
| 429 | // Number of replicas to be marked done before this bucket is ready. | 429 | // Number of replicas to be marked done before this bucket is ready. |
| 430 | size_t pending; | 430 | size_t pending; |
| 431 | 431 | ||
| 432 | // Keep work handle around when this set of buckets is being reduced. | 432 | // Keep work handle around when this set of buckets is being reduced. |
| 433 | c10::intrusive_ptr<c10d::Work> work; | 433 | c10::intrusive_ptr<c10d::Work> work; |
| 434 | 434 | ||
| 435 | // Keep future work handle around DDP comm hook. | 435 | // Keep future work handle around DDP comm hook. |
| 436 | // If no hook is registered, a temporary vanilla allreduce hook will be | 436 | // If no hook is registered, a temporary vanilla allreduce hook will be |
| 437 | // used. | 437 | // used. |
| 438 | c10::intrusive_ptr<at::ivalue::Future> future_work; | 438 | c10::intrusive_ptr<at::ivalue::Future> future_work; |
| 439 | 439 | ||
| 440 | // If this bucket should expect a single sparse gradient. | 440 | // If this bucket should expect a single sparse gradient. |
| 441 | // Implies: replicas[i].variables.size() == 1. | 441 | // Implies: replicas[i].variables.size() == 1. |
| 442 | bool expect_sparse_gradient = false; | 442 | bool expect_sparse_gradient = false; |
| 443 | // "Limit" of cumulative parameter sizes that this bucket manages. It is | 443 | // "Limit" of cumulative parameter sizes that this bucket manages. It is |
| 444 | // actually a soft limit because we don't shard parameters across buckets | 444 | // actually a soft limit because we don't shard parameters across buckets |
| 445 | // so a single parameter may push it over the cap. | 445 | // so a single parameter may push it over the cap. |
| 446 | size_t bucket_size_limit; | 446 | size_t bucket_size_limit; |
| 447 | }; | 447 | }; |
| 448 | 448 | ||
| 449 | std::vector<Bucket> buckets_; | 449 | std::vector<Bucket> buckets_; |
| 450 | 450 | ||
| 451 | // A variable locator locates a particular variable in the bucket | 451 | // A variable locator locates a particular variable in the bucket |
| 452 | // structure. The `bucket_index` field points to the bucket in the `buckets_` | 452 | // structure. The `bucket_index` field points to the bucket in the `buckets_` |
| 453 | // vector. The `intra_bucket_index` field points to the index of the variable | 453 | // vector. The `intra_bucket_index` field points to the index of the variable |
| 454 | // in any of the vector fields in the bucket replica. | 454 | // in any of the vector fields in the bucket replica. |
| 455 | struct VariableLocator { | 455 | struct VariableLocator { |
| 456 | // Index into the `buckets_` variable. | 456 | // Index into the `buckets_` variable. |
| 457 | size_t bucket_index; | 457 | size_t bucket_index; |
| 458 | // Index of parameter in single bucket replica. | 458 | // Index of parameter in single bucket replica. |
| 459 | size_t intra_bucket_index; | 459 | size_t intra_bucket_index; |
| 460 | 460 | ||
| 461 | VariableLocator() = default; | 461 | VariableLocator() = default; |
| 462 | 462 | ||
| 463 | VariableLocator(size_t bucket_index_, size_t intra_bucket_index_) | 463 | VariableLocator(size_t bucket_index_, size_t intra_bucket_index_) |
| 464 | { | 464 | { |
| 465 | bucket_index = bucket_index_; | 465 | bucket_index = bucket_index_; |
| 466 | intra_bucket_index = intra_bucket_index_; | 466 | intra_bucket_index = intra_bucket_index_; |
| 467 | } | 467 | } |
| 468 | }; | 468 | }; |
| 469 | 469 | ||
| 470 | // Map the index of a variable to its location in the bucket structure. | 470 | // Map the index of a variable to its location in the bucket structure. |
| 471 | std::vector<VariableLocator> variable_locators_; | 471 | std::vector<VariableLocator> variable_locators_; |
| 472 | 472 | ||
| 473 | // track the number of iterations to synchronize grads in training so far. | 473 | // track the number of iterations to synchronize grads in training so far. |
| 474 | long num_iterations_; | 474 | long num_iterations_; |
| 475 | // track the number of buckets that have been ready for | 475 | // track the number of buckets that have been ready for |
| 476 | // communication calls like allReduce or communication hooks. | 476 | // communication calls like allReduce or communication hooks. |
| 477 | int num_buckets_ready_; | 477 | int num_buckets_ready_; |
| 478 | 478 | ||
| 479 | // Timing information. | 479 | // Timing information. |
| 480 | int64_t backward_compute_start_time_ = -1; | 480 | int64_t backward_compute_start_time_ = -1; |
| 481 | std::unique_ptr<Timer> timer_; | 481 | std::unique_ptr<Timer> timer_; |
| 482 | 482 | ||
| 483 | // We collect the relative timestamp of every gradient being ready | 483 | // We collect the relative timestamp of every gradient being ready |
| 484 | // when executing autograd. This can be used to derive a timeline of | 484 | // when executing autograd. This can be used to derive a timeline of |
| 485 | // the point in time buckets were ready, or ideal bucket assignment/ordering. | 485 | // the point in time buckets were ready, or ideal bucket assignment/ordering. |
| 486 | std::vector<int64_t> backward_stats_; | 486 | std::vector<int64_t> backward_stats_; |
| 487 | 487 | ||
| 488 | bool should_collect_runtime_stats(); | 488 | bool should_collect_runtime_stats(); |
| 489 | void record_forward_compute_start_time(); | 489 | void record_forward_compute_start_time(); |
| 490 | void record_backward_compute_start_time(); | 490 | void record_backward_compute_start_time(); |
| 491 | void record_backward_compute_end_time(); | 491 | void record_backward_compute_end_time(); |
| 492 | void record_backward_comm_start_time(); | 492 | void record_backward_comm_start_time(); |
| 493 | void record_backward_comm_end_time(); | 493 | void record_backward_comm_end_time(); |
| 494 | 494 | ||
| 495 | int get_ddp_runtime_logging_sample_rate() const; | 495 | int get_ddp_runtime_logging_sample_rate() const; |
| 496 | int ddp_runtime_logging_sample_rate_ = kDDPRuntimeLoggingSampleRate; | 496 | int ddp_runtime_logging_sample_rate_ = kDDPRuntimeLoggingSampleRate; |
| 497 | 497 | ||
| 498 | bool is_multi_device_module_ = false; | 498 | bool is_multi_device_module_ = false; |
| 499 | 499 | ||
| 500 | // Following variables are to help build dynamic bucket order | 500 | // Following variables are to help build dynamic bucket order |
| 501 | bool has_rebuilt_bucket_; | 501 | bool has_rebuilt_bucket_; |
| 502 | std::vector<at::Tensor> rebuilt_params_; | 502 | std::vector<at::Tensor> rebuilt_params_; |
| 503 | std::vector<int64_t> rebuilt_param_indices_; | 503 | std::vector<int64_t> rebuilt_param_indices_; |
| 504 | const int64_t bucket_bytes_cap_; | 504 | const int64_t bucket_bytes_cap_; |
| 505 | 505 | ||
| 506 | #ifndef _WIN32 | 506 | #ifndef _WIN32 |
| 507 | struct RpcContext { | 507 | struct RpcContext { |
| 508 | using ContextPtr = torch::distributed::autograd::ContextPtr; | 508 | using ContextPtr = torch::distributed::autograd::ContextPtr; |
| 509 | // The shared_ptr is to hold the context instance. | 509 | // The shared_ptr is to hold the context instance. |
| 510 | ContextPtr context_ptr_holder; | 510 | ContextPtr context_ptr_holder; |
| 511 | std::atomic<ContextPtr::element_type*> context_ptr{nullptr}; | 511 | std::atomic<ContextPtr::element_type*> context_ptr{nullptr}; |
| 512 | 512 | ||
| 513 | void set(ContextPtr&& new_context_ptr); | 513 | void set(ContextPtr&& new_context_ptr); |
| 514 | }; | 514 | }; |
| 515 | RpcContext rpc_context_; | 515 | RpcContext rpc_context_; |
| 516 | #endif | 516 | #endif |
| 517 | 517 | ||
| 518 | // A struct containing work handle and tensor for allreduce scheduled in | 518 | // A struct containing work handle and tensor for allreduce scheduled in |
| 519 | // forward pass, if applicable. | 519 | // forward pass, if applicable. |
| 520 | struct ForwardPassAllreduceWork { | 520 | struct ForwardPassAllreduceWork { |
| 521 | c10::intrusive_ptr<c10d::Work> workHandle; | 521 | c10::intrusive_ptr<c10d::Work> workHandle; |
| 522 | at::Tensor resultTensor; | 522 | at::Tensor resultTensor; |
| 523 | // whether we should divide by the initial world_size or the no. of | 523 | // whether we should divide by the initial world_size or the no. of |
| 524 | // remaining DDP ranks. | 524 | // remaining DDP ranks. |
| 525 | bool useStaticWorldSize = false; | 525 | bool useStaticWorldSize = false; |
| 526 | }; | 526 | }; |
| 527 | 527 | ||
| 528 | // Handle for the currently scheduled allreduce in the forward pass, if | 528 | // Handle for the currently scheduled allreduce in the forward pass, if |
| 529 | // applicable. | 529 | // applicable. |
| 530 | ForwardPassAllreduceWork forwardPassWorkHandle_; | 530 | ForwardPassAllreduceWork forwardPassWorkHandle_; |
| 531 | 531 | ||
| 532 | // Division factor for reduction of gradients. | 532 | // Division factor for reduction of gradients. |
| 533 | // Equal to the process group size, with an exception of handling uneven | 533 | // Equal to the process group size, with an exception of handling uneven |
| 534 | // input. | 534 | // input. |
| 535 | int div_factor_; | 535 | int div_factor_; |
| 536 | 536 | ||
| 537 | bool static_graph_; | 537 | bool static_graph_; |
| 538 | 538 | ||
| 539 | // Key: size_t (index), Value: the number of times that a variable's | 539 | // Key: size_t (index), Value: the number of times that a variable's |
| 540 | // autograd_hook() should be triggered before marking this variable's grad as | 540 | // autograd_hook() should be triggered before marking this variable's grad as |
| 541 | // ready for communication. Map will not change after 1st iteration. | 541 | // ready for communication. Map will not change after 1st iteration. |
| 542 | std::unordered_map<size_t, int> numGradHooksTriggeredMap_; | 542 | std::unordered_map<size_t, int> numGradHooksTriggeredMap_; |
| 543 | // Key: size_t (index), Value: the number of times that a variable's | 543 | // Key: size_t (index), Value: the number of times that a variable's |
| 544 | // autograd_hook() are left to be triggered before marking this variable's | 544 | // autograd_hook() are left to be triggered before marking this variable's |
| 545 | // grad as ready for communication. Map will change after 1st iteration to | 545 | // grad as ready for communication. Map will change after 1st iteration to |
| 546 | // track a grad is ready for communication or not. | 546 | // track a grad is ready for communication or not. |
| 547 | std::unordered_map<size_t, int> numGradHooksTriggeredMapPerIteration_; | 547 | std::unordered_map<size_t, int> numGradHooksTriggeredMapPerIteration_; |
| 548 | 548 | ||
| 549 | private: | 549 | private: |
| 550 | // reset counting for buckets before backward starts | 550 | // reset counting for buckets before backward starts |
| 551 | void reset_bucket_counting(); | 551 | void reset_bucket_counting(); |
| 552 | // search unused parameters beore backward starts | 552 | // search unused parameters beore backward starts |
| 553 | void search_unused_parameters( | 553 | void search_unused_parameters( |
| 554 | const std::vector<torch::autograd::Variable>& outputs); | 554 | const std::vector<torch::autograd::Variable>& outputs); |
| 555 | void set_divide_factor(); | 555 | void set_divide_factor(); |
| 556 | // kick off all reduce for the ready bucket | 556 | // kick off all reduce for the ready bucket |
| 557 | void all_reduce_bucket(Bucket& bucket); | 557 | void all_reduce_bucket(Bucket& bucket); |
| 558 | // kick off all reduce to local used map, it can help find global unused | 558 | // kick off all reduce to local used map, it can help find global unused |
| 559 | // parameters | 559 | // parameters |
| 560 | void all_reduce_local_used_map(); | 560 | void all_reduce_local_used_map(); |
| 561 | // initialize locally used parameter maps | 561 | // initialize locally used parameter maps |
| 562 | void initialize_local_used_map(); | 562 | void initialize_local_used_map(); |
| 563 | // get current cuda stream | 563 | // get current cuda stream |
| 564 | const c10::Stream get_current_stream(); | 564 | const c10::Stream get_current_stream(); |
| 565 | bool dynamic_graph_find_unused() const; | 565 | bool dynamic_graph_find_unused() const; |
| 566 | bool static_graph_first_iteration() const; | 566 | bool static_graph_first_iteration() const; |
| 567 | bool static_graph_after_first_iteration() const; | 567 | bool static_graph_after_first_iteration() const; |
| 568 | 568 | ||
| 569 | // comm_hook_ is used to access the DDP communication hook if registered. | 569 | // comm_hook_ is used to access the DDP communication hook if registered. |
| 570 | std::unique_ptr<c10d::CommHookInterface> comm_hook_; | 570 | std::unique_ptr<c10d::CommHookInterface> comm_hook_; |
| 571 | // Debug level setting. It is parsed once when Reducer is constructed, and | 571 | // Debug level setting. It is parsed once when Reducer is constructed, and |
| 572 | // remains the same across a single invocation of DDP training. | 572 | // remains the same across a single invocation of DDP training. |
| 573 | c10d::DebugLevel ddp_debug_level_; | 573 | c10d::DebugLevel ddp_debug_level_; |
| 574 | // Mapping of variable index to fully qualified name of model to notify users | 574 | // Mapping of variable index to fully qualified name of model to notify users |
| 575 | // about errors when certain parameters do not get gradient. | 575 | // about errors when certain parameters do not get gradient. |
| 576 | std::unordered_map<size_t, std::string> param_names_; | 576 | std::unordered_map<size_t, std::string> param_names_; |
| 577 | // Variable indices stored sequentially in order of when the gradient is ready | 577 | // Variable indices stored sequentially in order of when the gradient is ready |
| 578 | // for the current backwards pass. | 578 | // for the current backwards pass. |
| 579 | std::vector<int> grad_ready_order_indices_; | 579 | std::vector<int> grad_ready_order_indices_; |
| 580 | // Bytes capacity of first bucket, can be configured by user | 580 | // Bytes capacity of first bucket, can be configured by user |
| 581 | int64_t first_bucket_bytes_cap_; | 581 | int64_t first_bucket_bytes_cap_; |
| 582 | // Per iteration set of parameter indices that have been marked ready. | 582 | // Per iteration set of parameter indices that have been marked ready. |
| 583 | std::unordered_set<size_t> perIterationReadyParams_; | 583 | std::unordered_set<size_t> perIterationReadyParams_; |
| 584 | // Retrieves parameter names that have not been marked as ready as part of | 584 | // Retrieves parameter names that have not been marked as ready as part of |
| 585 | // previous iteration. | 585 | // previous iteration. |
| 586 | std::vector<std::string> getUnmarkedParamsForIteration(); | 586 | std::vector<std::string> getUnmarkedParamsForIteration(); |
| 587 | // Retrives parameter indices that have not been marked as ready as part of | 587 | // Retrives parameter indices that have not been marked as ready as part of |
| 588 | // previous iteration. | 588 | // previous iteration. |
| 589 | std::vector<size_t> getUnmarkedParamIndicesForIteration(); | 589 | std::vector<size_t> getUnmarkedParamIndicesForIteration(); |
| 590 | // Raises appropriate error if mark_variable_ready is called on the same | 590 | // Raises appropriate error if mark_variable_ready is called on the same |
| 591 | // variable twice, which is unexpected. | 591 | // variable twice, which is unexpected. |
| 592 | void checkAndRaiseMarkedTwiceError(size_t curVariableIndex); | 592 | void checkAndRaiseMarkedTwiceError(size_t curVariableIndex); |
| 593 | // Retrieves parameter corresponding to the given VariableIndex. | 593 | // Retrieves parameter corresponding to the given VariableIndex. |
| 594 | at::Tensor& get_param_from_index(size_t index); | 594 | at::Tensor& get_param_from_index(size_t index); |
| 595 | 595 | ||
| 596 | // Cached bucket index to model parameter mapping. Populated after buckets | 596 | // Cached bucket index to model parameter mapping. Populated after buckets |
| 597 | // are rebuilt after which this mapping is static. | 597 | // are rebuilt after which this mapping is static. |
| 598 | mutable std::unordered_map<size_t, std::vector<at::Tensor>> cached_variables_for_bucket_; | 598 | mutable std::unordered_map<size_t, std::vector<at::Tensor>> cached_variables_for_bucket_; |
| 599 | 599 | ||
| 600 | friend class Logger; | 600 | friend class Logger; |
| 601 | }; | 601 | }; |
| 602 | 602 | ||
| 603 | // This is equivalent to take_tensors but returns indices into the | 603 | // This is equivalent to take_tensors but returns indices into the |
| 604 | // tensor list argument for bucket assignment. Also, it is aware | 604 | // tensor list argument for bucket assignment. Also, it is aware |
| 605 | // of device placement and will not allow buckets to span devices. | 605 | // of device placement and will not allow buckets to span devices. |
| 606 | // The index of tensors[i] assigned to bucket is tensor_indices[i], | 606 | // The index of tensors[i] assigned to bucket is tensor_indices[i], |
| 607 | // when tensor_indices is empty, the index of tensors[i] assigned to | 607 | // when tensor_indices is empty, the index of tensors[i] assigned to |
| 608 | // bucket is i. | 608 | // bucket is i. |
| 609 | std::tuple<std::vector<std::vector<size_t>>, std::vector<size_t>> compute_bucket_assignment_by_size( | 609 | std::tuple<std::vector<std::vector<size_t>>, std::vector<size_t>> compute_bucket_assignment_by_size( |
| 610 | const std::vector<at::Tensor>& tensors, | 610 | const std::vector<at::Tensor>& tensors, |
| 611 | const std::vector<size_t>& bucket_size, | 611 | const std::vector<size_t>& bucket_size, |
| 612 | const std::vector<bool>& expect_sparse_gradient = {}, | 612 | const std::vector<bool>& expect_sparse_gradient = {}, |
| 613 | const std::vector<int64_t>& tensor_indices = {}, | 613 | const std::vector<int64_t>& tensor_indices = {}, |
| 614 | const c10::optional<std::weak_ptr<c10d::Logger>>& logger = {}); | 614 | const c10::optional<std::weak_ptr<c10d::Logger>>& logger = {}); |
| 615 | 615 | ||
| 616 | // Verify models across all processes are the same as model on rank 0 with | 616 | // Verify models across all processes are the same as model on rank 0 with |
| 617 | // respect to no. of params and matching dtype/size/layout. | 617 | // respect to no. of params and matching dtype/size/layout. |
| 618 | void verify_params_across_processes( | 618 | void verify_params_across_processes( |
| 619 | const c10::intrusive_ptr<c10d::ProcessGroup>& process_group, | 619 | const c10::intrusive_ptr<c10d::ProcessGroup>& process_group, |
| 620 | const std::vector<at::Tensor>& params, | 620 | const std::vector<at::Tensor>& params, |
| 621 | const c10::optional<std::weak_ptr<c10d::Logger>>& logger); | 621 | const c10::optional<std::weak_ptr<c10d::Logger>>& logger); |
| 622 | } // namespace c10d_npu | 622 | } // namespace c10d_npu |
| @@ -1,12 +1,12 @@ | |||
| 1 | FILE(GLOB _FRAMEWORK_SRCS | 1 | FILE(GLOB _FRAMEWORK_SRCS |
| 2 | *.cpp | 2 | *.cpp |
| 3 | aoe/*.cpp | 3 | aoe/*.cpp |
| 4 | autograd/*.cpp | 4 | autograd/*.cpp |
| 5 | contiguous/*.cpp | 5 | contiguous/*.cpp |
| 6 | interface/*.cpp | 6 | interface/*.cpp |
| 7 | utils/*.cpp) | 7 | utils/*.cpp) |
| 8 | 8 | ||
| 9 | LIST(APPEND FRAMEWORK_SRCS ${_FRAMEWORK_SRCS}) | 9 | LIST(APPEND FRAMEWORK_SRCS ${_FRAMEWORK_SRCS}) |
| 10 | 10 | ||
| 11 | # Pass to parent | 11 | # Pass to parent |
| 12 | set(FRAMEWORK_SRCS ${FRAMEWORK_SRCS} PARENT_SCOPE) | 12 | set(FRAMEWORK_SRCS ${FRAMEWORK_SRCS} PARENT_SCOPE) |
| @@ -311,4 +311,3 @@ int64_t StorageDescHelper::GetValidMemorySize(const at::Tensor &tensor) | |||
| 311 | 311 | ||
| 312 | } // namespace native | 312 | } // namespace native |
| 313 | } // namespace at_npu | 313 | } // namespace at_npu |
| 314 | |||
| @@ -87,4 +87,3 @@ bool CanUseMemcpyForOtherFormat(const at::Tensor &tensor) | |||
| 87 | 87 | ||
| 88 | } // namespace native | 88 | } // namespace native |
| 89 | } // namespace at_npu | 89 | } // namespace at_npu |
| 90 | |||
| @@ -8,4 +8,3 @@ bool isSyscntEnable(); | |||
| 8 | } // namespace native | 8 | } // namespace native |
| 9 | } // namespace torchat_npu_npu | 9 | } // namespace torchat_npu_npu |
| 10 | 10 | ||
| 11 | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | FILE(GLOB _NPU_SRCS *.cpp) | 1 | FILE(GLOB _NPU_SRCS *.cpp) |
| 2 | 2 | ||
| 3 | LIST(APPEND NPU_SRCS ${_NPU_SRCS}) | 3 | LIST(APPEND NPU_SRCS ${_NPU_SRCS}) |
| 4 | 4 | ||
| 5 | # Pass to parent | 5 | # Pass to parent |
| 6 | set(NPU_SRCS ${NPU_SRCS} PARENT_SCOPE) | 6 | set(NPU_SRCS ${NPU_SRCS} PARENT_SCOPE) |
| @@ -1,6 +1,6 @@ | |||
| 1 | FILE(GLOB _PROF_SRCS *.cpp unwind/*.cpp python/*.cpp) | 1 | FILE(GLOB _PROF_SRCS *.cpp unwind/*.cpp python/*.cpp) |
| 2 | 2 | ||
| 3 | LIST(APPEND PROF_SRCS ${_PROF_SRCS}) | 3 | LIST(APPEND PROF_SRCS ${_PROF_SRCS}) |
| 4 | 4 | ||
| 5 | # Pass to parent | 5 | # Pass to parent |
| 6 | set(PROF_SRCS ${PROF_SRCS} PARENT_SCOPE) | 6 | set(PROF_SRCS ${PROF_SRCS} PARENT_SCOPE) |
| @@ -1,347 +1,347 @@ | |||
| 1 | #include <c10/core/DeviceType.h> | 1 | #include <c10/core/DeviceType.h> |
| 2 | #include <torch/csrc/autograd/utils/wrap_outputs.h> | 2 | #include <torch/csrc/autograd/utils/wrap_outputs.h> |
| 3 | 3 | ||
| 4 | #include "torch_npu/csrc/utils/TensorType.h" | 4 | #include "torch_npu/csrc/utils/TensorType.h" |
| 5 | #include "torch_npu/csrc/utils/LazyInit.h" | 5 | #include "torch_npu/csrc/utils/LazyInit.h" |
| 6 | 6 | ||
| 7 | namespace torch_npu { | 7 | namespace torch_npu { |
| 8 | namespace utils { | 8 | namespace utils { |
| 9 | using namespace at; | 9 | using namespace at; |
| 10 | using namespace torch::autograd; | 10 | using namespace torch::autograd; |
| 11 | 11 | ||
| 12 | std::vector<std::pair<Backend, ScalarType>> all_declared_types_npu() | 12 | std::vector<std::pair<Backend, ScalarType>> all_declared_types_npu() |
| 13 | { | 13 | { |
| 14 | std::vector<std::pair<Backend, ScalarType>> ret; | 14 | std::vector<std::pair<Backend, ScalarType>> ret; |
| 15 | // can't easily iterate over enum classes, does not support BFloat16 now | 15 | // can't easily iterate over enum classes, does not support BFloat16 now |
| 16 | std::vector<Backend> backends = { c10::Backend::PrivateUse1 }; | 16 | std::vector<Backend> backends = { c10::Backend::PrivateUse1 }; |
| 17 | std::vector<ScalarType> scalar_types = { ScalarType::Byte, ScalarType::Char, ScalarType::Double, | 17 | std::vector<ScalarType> scalar_types = { ScalarType::Byte, ScalarType::Char, ScalarType::Double, |
| 18 | ScalarType::Float, ScalarType::Int, ScalarType::Long, | 18 | ScalarType::Float, ScalarType::Int, ScalarType::Long, |
| 19 | ScalarType::Short, ScalarType::Half, ScalarType::Bool, | 19 | ScalarType::Short, ScalarType::Half, ScalarType::Bool, |
| 20 | ScalarType::BFloat16 }; | 20 | ScalarType::BFloat16 }; |
| 21 | 21 | ||
| 22 | for (auto &backend : backends) { | 22 | for (auto &backend : backends) { |
| 23 | for (auto &scalar_type : scalar_types) { | 23 | for (auto &scalar_type : scalar_types) { |
| 24 | ret.emplace_back(std::make_pair(backend, scalar_type)); | 24 | ret.emplace_back(std::make_pair(backend, scalar_type)); |
| 25 | } | 25 | } |
| 26 | } | 26 | } |
| 27 | 27 | ||
| 28 | return ret; | 28 | return ret; |
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | struct PyTensorType { | 31 | struct PyTensorType { |
| 32 | PyTypeObject py_type; | 32 | PyTypeObject py_type; |
| 33 | THPDtype *dtype; | 33 | THPDtype *dtype; |
| 34 | THPLayout *layout; | 34 | THPLayout *layout; |
| 35 | bool is_npu; | 35 | bool is_npu; |
| 36 | char name[64]; | 36 | char name[64]; |
| 37 | int backend; | 37 | int backend; |
| 38 | int scalar_type; | 38 | int scalar_type; |
| 39 | 39 | ||
| 40 | Backend get_backend() const | 40 | Backend get_backend() const |
| 41 | { | 41 | { |
| 42 | return static_cast<Backend>(backend); | 42 | return static_cast<Backend>(backend); |
| 43 | } | 43 | } |
| 44 | 44 | ||
| 45 | DispatchKey get_dispatch_key() const | 45 | DispatchKey get_dispatch_key() const |
| 46 | { | 46 | { |
| 47 | return backendToDispatchKey(static_cast<Backend>(backend)); | 47 | return backendToDispatchKey(static_cast<Backend>(backend)); |
| 48 | } | 48 | } |
| 49 | 49 | ||
| 50 | ScalarType get_scalar_type() const | 50 | ScalarType get_scalar_type() const |
| 51 | { | 51 | { |
| 52 | return static_cast<ScalarType>(scalar_type); | 52 | return static_cast<ScalarType>(scalar_type); |
| 53 | } | 53 | } |
| 54 | }; | 54 | }; |
| 55 | 55 | ||
| 56 | static_assert(std::is_standard_layout<PyTensorType>::value, "PyTensorType must be standard layout"); | 56 | static_assert(std::is_standard_layout<PyTensorType>::value, "PyTensorType must be standard layout"); |
| 57 | 57 | ||
| 58 | static void py_bind_tensor_types(const std::vector<PyTensorType> &tensor_types); | 58 | static void py_bind_tensor_types(const std::vector<PyTensorType> &tensor_types); |
| 59 | 59 | ||
| 60 | static PyObject *Tensor_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) | 60 | static PyObject *Tensor_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) |
| 61 | { | 61 | { |
| 62 | HANDLE_TH_ERRORS | 62 | HANDLE_TH_ERRORS |
| 63 | auto &tensor_type = *((PyTensorType *)type); | 63 | auto &tensor_type = *((PyTensorType *)type); |
| 64 | if (tensor_type.is_npu) { | 64 | if (tensor_type.is_npu) { |
| 65 | TORCH_NPU_WARN_ONCE("Warning: The torch.npu.*DtypeTensor constructors are no longer recommended. " | 65 | TORCH_NPU_WARN_ONCE("Warning: The torch.npu.*DtypeTensor constructors are no longer recommended. " |
| 66 | "It's best to use methods such as torch.tensor(data, dtype=*, device='npu') " | 66 | "It's best to use methods such as torch.tensor(data, dtype=*, device='npu') " |
| 67 | "to create tensors."); | 67 | "to create tensors."); |
| 68 | } | 68 | } |
| 69 | TORCH_CHECK_TYPE(!tensor_type.is_npu || c10_npu::device_count() != 0, "type ", tensor_type.name, | 69 | TORCH_CHECK_TYPE(!tensor_type.is_npu || c10_npu::device_count() != 0, "type ", tensor_type.name, |
| 70 | " not available. Torch not compiled with npu enabled.", PTA_ERROR(ErrCode::TYPE)) | 70 | " not available. Torch not compiled with npu enabled.", PTA_ERROR(ErrCode::TYPE)) |
| 71 | torch_npu::utils::npu_lazy_init(); | 71 | torch_npu::utils::npu_lazy_init(); |
| 72 | return THPVariable_Wrap( | 72 | return THPVariable_Wrap( |
| 73 | torch::utils::legacy_tensor_ctor(tensor_type.get_dispatch_key(), tensor_type.get_scalar_type(), args, kwargs)); | 73 | torch::utils::legacy_tensor_ctor(tensor_type.get_dispatch_key(), tensor_type.get_scalar_type(), args, kwargs)); |
| 74 | END_HANDLE_TH_ERRORS | 74 | END_HANDLE_TH_ERRORS |
| 75 | } | 75 | } |
| 76 | 76 | ||
| 77 | static PyObject *Tensor_instancecheck(PyObject *_self, PyObject *arg) | 77 | static PyObject *Tensor_instancecheck(PyObject *_self, PyObject *arg) |
| 78 | { | 78 | { |
| 79 | HANDLE_TH_ERRORS | 79 | HANDLE_TH_ERRORS |
| 80 | auto self = (PyTensorType *)_self; | 80 | auto self = (PyTensorType *)_self; |
| 81 | if (THPVariable_Check(arg)) { | 81 | if (THPVariable_Check(arg)) { |
| 82 | const auto &var = THPVariable_Unpack(arg); | 82 | const auto &var = THPVariable_Unpack(arg); |
| 83 | 83 | ||
| 84 | if (legacyExtractDispatchKey(var.key_set()) == self->get_dispatch_key() && | 84 | if (legacyExtractDispatchKey(var.key_set()) == self->get_dispatch_key() && |
| 85 | var.scalar_type() == static_cast<ScalarType>(self->scalar_type)) { | 85 | var.scalar_type() == static_cast<ScalarType>(self->scalar_type)) { |
| 86 | Py_RETURN_TRUE; | 86 | Py_RETURN_TRUE; |
| 87 | } | 87 | } |
| 88 | } | 88 | } |
| 89 | Py_RETURN_FALSE; | 89 | Py_RETURN_FALSE; |
| 90 | END_HANDLE_TH_ERRORS | 90 | END_HANDLE_TH_ERRORS |
| 91 | } | 91 | } |
| 92 | 92 | ||
| 93 | PyObject *Tensor_dtype(PyTensorType *self, void *unused) | 93 | PyObject *Tensor_dtype(PyTensorType *self, void *unused) |
| 94 | { | 94 | { |
| 95 | return torch::autograd::utils::wrap(self->dtype); | 95 | return torch::autograd::utils::wrap(self->dtype); |
| 96 | } | 96 | } |
| 97 | 97 | ||
| 98 | PyObject *Tensor_layout(PyTensorType *self, void *unused) | 98 | PyObject *Tensor_layout(PyTensorType *self, void *unused) |
| 99 | { | 99 | { |
| 100 | return torch::autograd::utils::wrap(self->layout); | 100 | return torch::autograd::utils::wrap(self->layout); |
| 101 | } | 101 | } |
| 102 | 102 | ||
| 103 | PyObject *Tensor_is_npu(PyTensorType *self, void *unused) | 103 | PyObject *Tensor_is_npu(PyTensorType *self, void *unused) |
| 104 | { | 104 | { |
| 105 | if (self->is_npu) { | 105 | if (self->is_npu) { |
| 106 | Py_RETURN_TRUE; | 106 | Py_RETURN_TRUE; |
| 107 | } else { | 107 | } else { |
| 108 | Py_RETURN_FALSE; | 108 | Py_RETURN_FALSE; |
| 109 | } | 109 | } |
| 110 | } | 110 | } |
| 111 | 111 | ||
| 112 | PyObject *Tensor_is_sparse(PyTensorType *self, void *unused) | 112 | PyObject *Tensor_is_sparse(PyTensorType *self, void *unused) |
| 113 | { | 113 | { |
| 114 | if (self->layout->layout == at::Layout::Strided) { | 114 | if (self->layout->layout == at::Layout::Strided) { |
| 115 | Py_RETURN_FALSE; | 115 | Py_RETURN_FALSE; |
| 116 | } else { | 116 | } else { |
| 117 | Py_RETURN_TRUE; | 117 | Py_RETURN_TRUE; |
| 118 | } | 118 | } |
| 119 | } | 119 | } |
| 120 | 120 | ||
| 121 | static struct PyMethodDef metaclass_methods[] = { | 121 | static struct PyMethodDef metaclass_methods[] = { |
| 122 | {"__instancecheck__", Tensor_instancecheck, METH_O, nullptr}, | 122 | {"__instancecheck__", Tensor_instancecheck, METH_O, nullptr}, |
| 123 | {nullptr} | 123 | {nullptr} |
| 124 | }; | 124 | }; |
| 125 | 125 | ||
| 126 | using getter = PyObject *(*)(PyObject *, void *); | 126 | using getter = PyObject *(*)(PyObject *, void *); |
| 127 | 127 | ||
| 128 | static struct PyGetSetDef metaclass_properties[] = { | 128 | static struct PyGetSetDef metaclass_properties[] = { |
| 129 | {"dtype", (getter)Tensor_dtype, nullptr, nullptr, nullptr}, | 129 | {"dtype", (getter)Tensor_dtype, nullptr, nullptr, nullptr}, |
| 130 | {"layout", (getter)Tensor_layout, nullptr, nullptr, nullptr}, | 130 | {"layout", (getter)Tensor_layout, nullptr, nullptr, nullptr}, |
| 131 | {"is_npu", (getter)Tensor_is_npu, nullptr, nullptr, nullptr}, | 131 | {"is_npu", (getter)Tensor_is_npu, nullptr, nullptr, nullptr}, |
| 132 | {"is_sparse", (getter)Tensor_is_sparse, nullptr, nullptr, nullptr}, | 132 | {"is_sparse", (getter)Tensor_is_sparse, nullptr, nullptr, nullptr}, |
| 133 | {nullptr} | 133 | {nullptr} |
| 134 | }; | 134 | }; |
| 135 | 135 | ||
| 136 | static PyTypeObject metaclass = { | 136 | static PyTypeObject metaclass = { |
| 137 | PyVarObject_HEAD_INIT(nullptr, 0) "torch.tensortype", /* tp_name */ | 137 | PyVarObject_HEAD_INIT(nullptr, 0) "torch.tensortype", /* tp_name */ |
| 138 | sizeof(PyTypeObject) /* tp_basicsize */ | 138 | sizeof(PyTypeObject) /* tp_basicsize */ |
| 139 | }; | 139 | }; |
| 140 | 140 | ||
| 141 | static void py_initialize_metaclass(PyTypeObject &metaclass) | 141 | static void py_initialize_metaclass(PyTypeObject &metaclass) |
| 142 | { | 142 | { |
| 143 | metaclass.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE; | 143 | metaclass.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE; |
| 144 | metaclass.tp_methods = metaclass_methods; | 144 | metaclass.tp_methods = metaclass_methods; |
| 145 | metaclass.tp_getset = metaclass_properties; | 145 | metaclass.tp_getset = metaclass_properties; |
| 146 | metaclass.tp_base = &PyType_Type; | 146 | metaclass.tp_base = &PyType_Type; |
| 147 | if (PyType_Ready(&metaclass) < 0) { | 147 | if (PyType_Ready(&metaclass) < 0) { |
| 148 | throw python_error(); | 148 | throw python_error(); |
| 149 | } | 149 | } |
| 150 | } | 150 | } |
| 151 | 151 | ||
| 152 | static PyTypeObject tensor_type_prototype = { | 152 | static PyTypeObject tensor_type_prototype = { |
| 153 | PyVarObject_HEAD_INIT(&metaclass, 0) nullptr, /* tp_name */ | 153 | PyVarObject_HEAD_INIT(&metaclass, 0) nullptr, /* tp_name */ |
| 154 | sizeof(PyTensorType) /* tp_basicsize */ | 154 | sizeof(PyTensorType) /* tp_basicsize */ |
| 155 | }; | 155 | }; |
| 156 | 156 | ||
| 157 | static void py_initialize_tensor_type(PyTypeObject &type, const char *name, PyObject *tp_dict) | 157 | static void py_initialize_tensor_type(PyTypeObject &type, const char *name, PyObject *tp_dict) |
| 158 | { | 158 | { |
| 159 | // NOTE: we don't use the typical static declaration of PyTypeObject because | 159 | // NOTE: we don't use the typical static declaration of PyTypeObject because |
| 160 | // we need to initialize as many types as there are VariableType instances. | 160 | // we need to initialize as many types as there are VariableType instances. |
| 161 | // We copy the basic object fields from a prototype definition and initialize | 161 | // We copy the basic object fields from a prototype definition and initialize |
| 162 | // the remaining fields below. | 162 | // the remaining fields below. |
| 163 | memcpy(&type, &tensor_type_prototype, sizeof(PyTypeObject)); | 163 | memcpy(&type, &tensor_type_prototype, sizeof(PyTypeObject)); |
| 164 | // Subclassing from torch.<ScalarType>Tensor isn't supported. | 164 | // Subclassing from torch.<ScalarType>Tensor isn't supported. |
| 165 | // (Py_TPFLAGS_BASETYPE omitted). Subclassing torch.Tensor still allowed. | 165 | // (Py_TPFLAGS_BASETYPE omitted). Subclassing torch.Tensor still allowed. |
| 166 | type.tp_flags = Py_TPFLAGS_DEFAULT; | 166 | type.tp_flags = Py_TPFLAGS_DEFAULT; |
| 167 | type.tp_name = name; | 167 | type.tp_name = name; |
| 168 | type.tp_new = Tensor_new; | 168 | type.tp_new = Tensor_new; |
| 169 | if (PyType_Ready(&type) < 0) { | 169 | if (PyType_Ready(&type) < 0) { |
| 170 | throw python_error(); | 170 | throw python_error(); |
| 171 | } | 171 | } |
| 172 | if (PyDict_Merge(type.tp_dict, tp_dict, 0) < 0) { | 172 | if (PyDict_Merge(type.tp_dict, tp_dict, 0) < 0) { |
| 173 | throw python_error(); | 173 | throw python_error(); |
| 174 | } | 174 | } |
| 175 | } | 175 | } |
| 176 | 176 | ||
| 177 | static std::string get_module(Backend backend) | 177 | static std::string get_module(Backend backend) |
| 178 | { | 178 | { |
| 179 | switch (backend) { | 179 | switch (backend) { |
| 180 | case Backend::CPU: | 180 | case Backend::CPU: |
| 181 | return "torch"; | 181 | return "torch"; |
| 182 | case Backend::CUDA: | 182 | case Backend::CUDA: |
| 183 | return "torch.cuda"; | 183 | return "torch.cuda"; |
| 184 | case Backend::SparseCPU: | 184 | case Backend::SparseCPU: |
| 185 | return "torch.sparse"; | 185 | return "torch.sparse"; |
| 186 | case Backend::SparseCUDA: | 186 | case Backend::SparseCUDA: |
| 187 | return "torch.cuda.sparse"; | 187 | return "torch.cuda.sparse"; |
| 188 | case Backend::PrivateUse1: | 188 | case Backend::PrivateUse1: |
| 189 | return "torch." + c10::get_privateuse1_backend(); | 189 | return "torch." + c10::get_privateuse1_backend(); |
| 190 | default: | 190 | default: |
| 191 | AT_ERROR("invalid backend: ", c10::toString(backend)); | 191 | AT_ERROR("invalid backend: ", c10::toString(backend)); |
| 192 | } | 192 | } |
| 193 | } | 193 | } |
| 194 | 194 | ||
| 195 | static std::string get_name(Backend backend, ScalarType scalarType) | 195 | static std::string get_name(Backend backend, ScalarType scalarType) |
| 196 | { | 196 | { |
| 197 | std::ostringstream ss; | 197 | std::ostringstream ss; |
| 198 | ss << get_module(backend) << "." << toString(scalarType) << "Tensor"; | 198 | ss << get_module(backend) << "." << toString(scalarType) << "Tensor"; |
| 199 | return ss.str(); | 199 | return ss.str(); |
| 200 | } | 200 | } |
| 201 | 201 | ||
| 202 | static void set_type(PyTensorType &type_obj, Backend backend, ScalarType scalarType) | 202 | static void set_type(PyTensorType &type_obj, Backend backend, ScalarType scalarType) |
| 203 | { | 203 | { |
| 204 | // This field is lazily initialized from backend and scalar_type | 204 | // This field is lazily initialized from backend and scalar_type |
| 205 | type_obj.backend = static_cast<int>(backend); | 205 | type_obj.backend = static_cast<int>(backend); |
| 206 | type_obj.scalar_type = static_cast<int>(scalarType); | 206 | type_obj.scalar_type = static_cast<int>(scalarType); |
| 207 | type_obj.layout = torch::getTHPLayout(c10::layout_from_backend(backend)); | 207 | type_obj.layout = torch::getTHPLayout(c10::layout_from_backend(backend)); |
| 208 | type_obj.dtype = torch::getTHPDtype(scalarType); | 208 | type_obj.dtype = torch::getTHPDtype(scalarType); |
| 209 | type_obj.is_npu = (backend == c10::Backend::PrivateUse1); | 209 | type_obj.is_npu = (backend == c10::Backend::PrivateUse1); |
| 210 | } | 210 | } |
| 211 | 211 | ||
| 212 | static void set_name(PyTensorType &type_obj, const std::string &name) | 212 | static void set_name(PyTensorType &type_obj, const std::string &name) |
| 213 | { | 213 | { |
| 214 | size_t n = sizeof(type_obj.name); | 214 | size_t n = sizeof(type_obj.name); |
| 215 | strncpy(type_obj.name, name.c_str(), n); | 215 | strncpy(type_obj.name, name.c_str(), n); |
| 216 | type_obj.name[n - 1] = '\0'; | 216 | type_obj.name[n - 1] = '\0'; |
| 217 | } | 217 | } |
| 218 | 218 | ||
| 219 | static THPObjectPtr get_tensor_dict() | 219 | static THPObjectPtr get_tensor_dict() |
| 220 | { | 220 | { |
| 221 | auto torch = THPObjectPtr(PyImport_ImportModule("torch")); | 221 | auto torch = THPObjectPtr(PyImport_ImportModule("torch")); |
| 222 | if (!torch) { | 222 | if (!torch) { |
| 223 | throw python_error(); | 223 | throw python_error(); |
| 224 | } | 224 | } |
| 225 | 225 | ||
| 226 | auto tensor_class = THPObjectPtr(PyObject_GetAttrString(torch, "Tensor")); | 226 | auto tensor_class = THPObjectPtr(PyObject_GetAttrString(torch, "Tensor")); |
| 227 | if (!tensor_class) { | 227 | if (!tensor_class) { |
| 228 | throw python_error(); | 228 | throw python_error(); |
| 229 | } | 229 | } |
| 230 | 230 | ||
| 231 | auto tensor_type = (PyTypeObject *)tensor_class.get(); | 231 | auto tensor_type = (PyTypeObject *)tensor_class.get(); |
| 232 | TORCH_CHECK(tensor_type->tp_base, "missing base type for Tensor", PTA_ERROR(ErrCode::TYPE)); | 232 | TORCH_CHECK(tensor_type->tp_base, "missing base type for Tensor", PTA_ERROR(ErrCode::TYPE)); |
| 233 | 233 | ||
| 234 | auto res = THPObjectPtr(PyDict_New()); | 234 | auto res = THPObjectPtr(PyDict_New()); |
| 235 | if (!res) { | 235 | if (!res) { |
| 236 | throw python_error(); | 236 | throw python_error(); |
| 237 | } | 237 | } |
| 238 | 238 | ||
| 239 | if (PyDict_Merge(res.get(), tensor_type->tp_dict, 0) < 0) { | 239 | if (PyDict_Merge(res.get(), tensor_type->tp_dict, 0) < 0) { |
| 240 | throw python_error(); | 240 | throw python_error(); |
| 241 | } | 241 | } |
| 242 | if (PyDict_Merge(res.get(), tensor_type->tp_base->tp_dict, 0) < 0) { | 242 | if (PyDict_Merge(res.get(), tensor_type->tp_base->tp_dict, 0) < 0) { |
| 243 | throw python_error(); | 243 | throw python_error(); |
| 244 | } | 244 | } |
| 245 | 245 | ||
| 246 | return res; | 246 | return res; |
| 247 | } | 247 | } |
| 248 | 248 | ||
| 249 | static std::vector<PyTensorType> tensor_types; | 249 | static std::vector<PyTensorType> tensor_types; |
| 250 | 250 | ||
| 251 | static void initialize_npu_aten_types(std::vector<PyTensorType> &tensor_types) | 251 | static void initialize_npu_aten_types(std::vector<PyTensorType> &tensor_types) |
| 252 | { | 252 | { |
| 253 | // only initialize npu types | 253 | // only initialize npu types |
| 254 | auto declared_types = all_declared_types_npu(); | 254 | auto declared_types = all_declared_types_npu(); |
| 255 | tensor_types.resize(declared_types.size()); | 255 | tensor_types.resize(declared_types.size()); |
| 256 | 256 | ||
| 257 | for (size_t i = 0, end = declared_types.size(); i != end; i++) { | 257 | for (size_t i = 0, end = declared_types.size(); i != end; i++) { |
| 258 | auto &tensor_type = tensor_types[i]; | 258 | auto &tensor_type = tensor_types[i]; |
| 259 | Backend backend = declared_types[i].first; | 259 | Backend backend = declared_types[i].first; |
| 260 | ScalarType scalar_type = declared_types[i].second; | 260 | ScalarType scalar_type = declared_types[i].second; |
| 261 | set_type(tensor_type, backend, scalar_type); | 261 | set_type(tensor_type, backend, scalar_type); |
| 262 | set_name(tensor_type, get_name(backend, scalar_type)); | 262 | set_name(tensor_type, get_name(backend, scalar_type)); |
| 263 | } | 263 | } |
| 264 | } | 264 | } |
| 265 | 265 | ||
| 266 | void _initialize_python_bindings() | 266 | void _initialize_python_bindings() |
| 267 | { | 267 | { |
| 268 | // Initialize the at::Type* pointers, name, and properties of the PyTensorType | 268 | // Initialize the at::Type* pointers, name, and properties of the PyTensorType |
| 269 | // vector. After this call, the vector must not be resized. | 269 | // vector. After this call, the vector must not be resized. |
| 270 | initialize_npu_aten_types(tensor_types); | 270 | initialize_npu_aten_types(tensor_types); |
| 271 | 271 | ||
| 272 | // Initialize the Python metaclass for the torch.FloatTensor, etc. types. | 272 | // Initialize the Python metaclass for the torch.FloatTensor, etc. types. |
| 273 | // The metaclass handles __instancecheck__ checks and binds the dtype property | 273 | // The metaclass handles __instancecheck__ checks and binds the dtype property |
| 274 | // on the type objects. | 274 | // on the type objects. |
| 275 | py_initialize_metaclass(metaclass); | 275 | py_initialize_metaclass(metaclass); |
| 276 | 276 | ||
| 277 | // Get the tp_dict of the Variable class. We copy function definitions | 277 | // Get the tp_dict of the Variable class. We copy function definitions |
| 278 | // onto each Tensor type object so that they can be accessed via e.g. | 278 | // onto each Tensor type object so that they can be accessed via e.g. |
| 279 | // `torch.npu.FloatTensor.add`. | 279 | // `torch.npu.FloatTensor.add`. |
| 280 | auto tensor_dict = get_tensor_dict(); | 280 | auto tensor_dict = get_tensor_dict(); |
| 281 | 281 | ||
| 282 | // Initialize each Python type object torch.npu.FloatTensor, torch.npu.DoubleTensor, etc. | 282 | // Initialize each Python type object torch.npu.FloatTensor, torch.npu.DoubleTensor, etc. |
| 283 | for (auto &tensor_type : tensor_types) { | 283 | for (auto &tensor_type : tensor_types) { |
| 284 | py_initialize_tensor_type(tensor_type.py_type, tensor_type.name, tensor_dict.get()); | 284 | py_initialize_tensor_type(tensor_type.py_type, tensor_type.name, tensor_dict.get()); |
| 285 | } | 285 | } |
| 286 | 286 | ||
| 287 | // Add the type objects to their corresponding modules. e.g. torch.npu.FloatTensor | 287 | // Add the type objects to their corresponding modules. e.g. torch.npu.FloatTensor |
| 288 | // is added to the `torch_npu` module as `FloatTensor`. Also add all the type | 288 | // is added to the `torch_npu` module as `FloatTensor`. Also add all the type |
| 289 | // objects to the set torch_npu._tensor_classes. | 289 | // objects to the set torch_npu._tensor_classes. |
| 290 | py_bind_tensor_types(tensor_types); | 290 | py_bind_tensor_types(tensor_types); |
| 291 | } | 291 | } |
| 292 | 292 | ||
| 293 | static void py_bind_tensor_types(const std::vector<PyTensorType> &tensor_types) | 293 | static void py_bind_tensor_types(const std::vector<PyTensorType> &tensor_types) |
| 294 | { | 294 | { |
| 295 | auto torch_module = THPObjectPtr(PyImport_ImportModule("torch")); | 295 | auto torch_module = THPObjectPtr(PyImport_ImportModule("torch")); |
| 296 | if (!torch_module) { | 296 | if (!torch_module) { |
| 297 | throw python_error(); | 297 | throw python_error(); |
| 298 | } | 298 | } |
| 299 | 299 | ||
| 300 | auto tensor_classes = THPObjectPtr(PyObject_GetAttrString(torch_module.get(), "_tensor_classes")); | 300 | auto tensor_classes = THPObjectPtr(PyObject_GetAttrString(torch_module.get(), "_tensor_classes")); |
| 301 | if (!tensor_classes) { | 301 | if (!tensor_classes) { |
| 302 | throw python_error(); | 302 | throw python_error(); |
| 303 | } | 303 | } |
| 304 | 304 | ||
| 305 | for (auto &tensor_type : tensor_types) { | 305 | for (auto &tensor_type : tensor_types) { |
| 306 | auto name = std::string(tensor_type.name); | 306 | auto name = std::string(tensor_type.name); |
| 307 | auto idx = name.rfind('.'); | 307 | auto idx = name.rfind('.'); |
| 308 | auto type_name = name.substr(idx + 1); | 308 | auto type_name = name.substr(idx + 1); |
| 309 | auto module_name = name.substr(0, idx); | 309 | auto module_name = name.substr(0, idx); |
| 310 | 310 | ||
| 311 | auto module_obj = THPObjectPtr(PyImport_ImportModule(module_name.c_str())); | 311 | auto module_obj = THPObjectPtr(PyImport_ImportModule(module_name.c_str())); |
| 312 | if (!module_obj) { | 312 | if (!module_obj) { |
| 313 | throw python_error(); | 313 | throw python_error(); |
| 314 | } | 314 | } |
| 315 | 315 | ||
| 316 | PyObject *type_obj = (PyObject *)&tensor_type; | 316 | PyObject *type_obj = (PyObject *)&tensor_type; |
| 317 | Py_INCREF(type_obj); | 317 | Py_INCREF(type_obj); |
| 318 | if (PyModule_AddObject(module_obj.get(), type_name.c_str(), type_obj) < 0) { | 318 | if (PyModule_AddObject(module_obj.get(), type_name.c_str(), type_obj) < 0) { |
| 319 | throw python_error(); | 319 | throw python_error(); |
| 320 | } | 320 | } |
| 321 | if (PySet_Add(tensor_classes.get(), type_obj) < 0) { | 321 | if (PySet_Add(tensor_classes.get(), type_obj) < 0) { |
| 322 | throw python_error(); | 322 | throw python_error(); |
| 323 | } | 323 | } |
| 324 | } | 324 | } |
| 325 | } | 325 | } |
| 326 | 326 | ||
| 327 | // Callback for python part. Used for additional initialization of python classes | 327 | // Callback for python part. Used for additional initialization of python classes |
| 328 | static PyObject *THPModule_initExtension(PyObject *_unused, PyObject *noargs) | 328 | static PyObject *THPModule_initExtension(PyObject *_unused, PyObject *noargs) |
| 329 | { | 329 | { |
| 330 | HANDLE_TH_ERRORS | 330 | HANDLE_TH_ERRORS |
| 331 | _initialize_python_bindings(); | 331 | _initialize_python_bindings(); |
| 332 | Py_RETURN_NONE; | 332 | Py_RETURN_NONE; |
| 333 | END_HANDLE_TH_ERRORS | 333 | END_HANDLE_TH_ERRORS |
| 334 | } | 334 | } |
| 335 | 335 | ||
| 336 | // autograd methods on torch._C | 336 | // autograd methods on torch._C |
| 337 | static PyMethodDef TorchNpuExtensionMethods[] = { | 337 | static PyMethodDef TorchNpuExtensionMethods[] = { |
| 338 | {"_initExtension", (PyCFunction)THPModule_initExtension, METH_NOARGS, nullptr}, | 338 | {"_initExtension", (PyCFunction)THPModule_initExtension, METH_NOARGS, nullptr}, |
| 339 | {nullptr, nullptr, 0, nullptr} | 339 | {nullptr, nullptr, 0, nullptr} |
| 340 | }; | 340 | }; |
| 341 | 341 | ||
| 342 | PyMethodDef *npu_extension_functions() | 342 | PyMethodDef *npu_extension_functions() |
| 343 | { | 343 | { |
| 344 | return TorchNpuExtensionMethods; | 344 | return TorchNpuExtensionMethods; |
| 345 | } | 345 | } |
| 346 | } | 346 | } |
| 347 | } | 347 | } |
| @@ -1,16 +1,16 @@ | |||
| 1 | #include <torch/csrc/utils/tensor_new.h> | 1 | #include <torch/csrc/utils/tensor_new.h> |
| 2 | 2 | ||
| 3 | #include "torch_npu/csrc/core/npu/NPUMacros.h" | 3 | #include "torch_npu/csrc/core/npu/NPUMacros.h" |
| 4 | #include "torch_npu/csrc/core/npu/NPUFunctions.h" | 4 | #include "torch_npu/csrc/core/npu/NPUFunctions.h" |
| 5 | 5 | ||
| 6 | namespace torch_npu { | 6 | namespace torch_npu { |
| 7 | namespace utils { | 7 | namespace utils { |
| 8 | 8 | ||
| 9 | // Initializes the Python tensor type objects: torch.npu.FloatTensor, | 9 | // Initializes the Python tensor type objects: torch.npu.FloatTensor, |
| 10 | // torch.npu.DoubleTensor, etc. and binds them in their containing modules. | 10 | // torch.npu.DoubleTensor, etc. and binds them in their containing modules. |
| 11 | void _initialize_python_bindings(); | 11 | void _initialize_python_bindings(); |
| 12 | 12 | ||
| 13 | TORCH_NPU_API PyMethodDef* npu_extension_functions(); | 13 | TORCH_NPU_API PyMethodDef* npu_extension_functions(); |
| 14 | 14 | ||
| 15 | } | 15 | } |
| 16 | } | 16 | } |
| @@ -1,30 +1,30 @@ | |||
| 1 | __all__ = [ | 1 | __all__ = [ |
| 2 | "is_hccl_available", "reinit_process_group", "reduce_scatter_tensor_uneven", "all_gather_into_tensor_uneven" | 2 | "is_hccl_available", "reinit_process_group", "reduce_scatter_tensor_uneven", "all_gather_into_tensor_uneven" |
| 3 | ] | 3 | ] |
| 4 | 4 | ||
| 5 | from torch.distributed import _make_nccl_premul_sum as _make_hccl_premul_sum | 5 | from torch.distributed import _make_nccl_premul_sum as _make_hccl_premul_sum |
| 6 | 6 | ||
| 7 | import torch_npu | 7 | import torch_npu |
| 8 | 8 | ||
| 9 | 9 | ||
| 10 | def is_available(): | 10 | def is_available(): |
| 11 | """ | 11 | """ |
| 12 | Returns ``True`` if the distributed package is available. Otherwise, | 12 | Returns ``True`` if the distributed package is available. Otherwise, |
| 13 | ``torch.distributed`` does not expose any other APIs. Currently, | 13 | ``torch.distributed`` does not expose any other APIs. Currently, |
| 14 | ``torch.distributed`` is available on Linux, MacOS and Windows. Set | 14 | ``torch.distributed`` is available on Linux, MacOS and Windows. Set |
| 15 | ``USE_DISTRIBUTED=1`` to enable it when building PyTorch from source. | 15 | ``USE_DISTRIBUTED=1`` to enable it when building PyTorch from source. |
| 16 | Currently, the default value is ``USE_DISTRIBUTED=1`` for Linux and Windows, | 16 | Currently, the default value is ``USE_DISTRIBUTED=1`` for Linux and Windows, |
| 17 | ``USE_DISTRIBUTED=0`` for MacOS. | 17 | ``USE_DISTRIBUTED=0`` for MacOS. |
| 18 | """ | 18 | """ |
| 19 | return hasattr(torch_npu._C, "_c10d_npu_init") | 19 | return hasattr(torch_npu._C, "_c10d_npu_init") |
| 20 | 20 | ||
| 21 | 21 | ||
| 22 | from torch_npu._C._distributed_c10d import ( | 22 | from torch_npu._C._distributed_c10d import ( |
| 23 | ParallelStore, | 23 | ParallelStore, |
| 24 | _verify_params_across_processes, | 24 | _verify_params_across_processes, |
| 25 | _is_support_hccl_comm_name, | 25 | _is_support_hccl_comm_name, |
| 26 | ) | 26 | ) |
| 27 | 27 | ||
| 28 | 28 | ||
| 29 | from torch_npu.distributed import fsdp, tensor, nn | 29 | from torch_npu.distributed import fsdp, tensor, nn |
| 30 | from .distributed_c10d import is_hccl_available, reinit_process_group, _reduce_scatter_tensor_uneven as reduce_scatter_tensor_uneven, _all_gather_into_tensor_uneven as all_gather_into_tensor_uneven | 30 | from .distributed_c10d import is_hccl_available, reinit_process_group, _reduce_scatter_tensor_uneven as reduce_scatter_tensor_uneven, _all_gather_into_tensor_uneven as all_gather_into_tensor_uneven |
| @@ -1,100 +1,99 @@ | |||
| 1 | import torch | 1 | import torch |
| 2 | from torch._dynamo.variables import TorchInGraphFunctionVariable | 2 | from torch._dynamo.variables import TorchInGraphFunctionVariable |
| 3 | from torch._dynamo.trace_rules import manual_torch_name_rule_map, SkipFunctionVariable | 3 | from torch._dynamo.trace_rules import manual_torch_name_rule_map, SkipFunctionVariable |
| 4 | import torch._dynamo.variables.torch as torch_module | 4 | import torch._dynamo.variables.torch as torch_module |
| 5 | from torch._dynamo.utils import common_constant_types | 5 | from torch._dynamo.utils import common_constant_types |
| 6 | import torch_npu | 6 | import torch_npu |
| 7 | 7 | ||
| 8 | __all__ = [] | 8 | __all__ = [] |
| 9 | 9 | ||
| 10 | torch_non_c_binding_in_graph_functions_npu = dict.fromkeys( | 10 | torch_non_c_binding_in_graph_functions_npu = dict.fromkeys( |
| 11 | [ | 11 | [ |
| 12 | "torch.npu.current_stream", | 12 | "torch.npu.current_stream", |
| 13 | "torch.npu.default_stream", | 13 | "torch.npu.default_stream", |
| 14 | "torch.npu.stream", | 14 | "torch.npu.stream", |
| 15 | "torch.npu.set_stream", | 15 | "torch.npu.set_stream", |
| 16 | "torch_npu.npu.utils.synchronize", | 16 | "torch_npu.npu.utils.synchronize", |
| 17 | "torch.npu.current_device", | 17 | "torch.npu.current_device", |
| 18 | "torch.npu.get_device_capability", | 18 | "torch.npu.get_device_capability", |
| 19 | "torch.npu.get_device_properties", | 19 | "torch.npu.get_device_properties", |
| 20 | "torch.npu.graphs.graph_pool_handle", | 20 | "torch.npu.graphs.graph_pool_handle", |
| 21 | "torch.npu.ipc_collect", | 21 | "torch.npu.ipc_collect", |
| 22 | "torch.npu.is_available", | 22 | "torch.npu.is_available", |
| 23 | "torch.npu.memory._dump_snapshot", | 23 | "torch.npu.memory._dump_snapshot", |
| 24 | "torch.npu.memory._free_mutex", | 24 | "torch.npu.memory._free_mutex", |
| 25 | "torch.npu.memory._record_memory_history_impl", | 25 | "torch.npu.memory._record_memory_history_impl", |
| 26 | "torch.npu.memory._set_allocator_settings", | 26 | "torch.npu.memory._set_allocator_settings", |
| 27 | "torch.npu.memory.empty_cache", | 27 | "torch.npu.memory.empty_cache", |
| 28 | "torch.npu.mem_get_info", | 28 | "torch.npu.mem_get_info", |
| 29 | "torch.npu.memory.reset_accumulated_host_memory_stats", | 29 | "torch.npu.memory.reset_accumulated_host_memory_stats", |
| 30 | "torch.npu.memory.reset_accumulated_memory_stats", | 30 | "torch.npu.memory.reset_accumulated_memory_stats", |
| 31 | "torch.npu.memory.reset_max_memory_allocated", | 31 | "torch.npu.memory.reset_max_memory_allocated", |
| 32 | "torch.npu.memory.reset_max_memory_cached", | 32 | "torch.npu.memory.reset_max_memory_cached", |
| 33 | "torch.npu.memory.reset_peak_host_memory_stats", | 33 | "torch.npu.memory.reset_peak_host_memory_stats", |
| 34 | "torch.npu.memory.reset_peak_memory_stats", | 34 | "torch.npu.memory.reset_peak_memory_stats", |
| 35 | "torch.npu.memory.get_per_process_memory_fraction", | 35 | "torch.npu.memory.get_per_process_memory_fraction", |
| 36 | "torch.npu.memory.set_per_process_memory_fraction", | 36 | "torch.npu.memory.set_per_process_memory_fraction", |
| 37 | "torch.npu.random.manual_seed_all", | 37 | "torch.npu.random.manual_seed_all", |
| 38 | "torch.npu.random.manual_seed", | 38 | "torch.npu.random.manual_seed", |
| 39 | "torch.npu.random.seed_all", | 39 | "torch.npu.random.seed_all", |
| 40 | "torch.npu.random.seed", | 40 | "torch.npu.random.seed", |
| 41 | "torch.npu.set_sync_debug_mode", | 41 | "torch.npu.set_sync_debug_mode", |
| 42 | "torch.npu._set_rng_state_offset", | 42 | "torch.npu._set_rng_state_offset", |
| 43 | "torch.npu._get_generator", | 43 | "torch.npu._get_generator", |
| 44 | "torch.npu._memory_viz._frames_fmt", | 44 | "torch.npu._memory_viz._frames_fmt", |
| 45 | "torch.npu._memory_viz._frame_fmt", | 45 | "torch.npu._memory_viz._frame_fmt", |
| 46 | "torch.npu.amp.autocast_mode.custom_bwd", | 46 | "torch.npu.amp.autocast_mode.custom_bwd", |
| 47 | "torch.npu.amp.autocast_mode.custom_fwd", | 47 | "torch.npu.amp.autocast_mode.custom_fwd", |
| 48 | "torch.npu.is_initialized", | 48 | "torch.npu.is_initialized", |
| 49 | "torch.npu._get_current_allocator", | 49 | "torch.npu._get_current_allocator", |
| 50 | "torch.npu.is_bf16_supported", | 50 | "torch.npu.is_bf16_supported", |
| 51 | "torch.npu.memory._get_current_allocator", | 51 | "torch.npu.memory._get_current_allocator", |
| 52 | ], | 52 | ], |
| 53 | TorchInGraphFunctionVariable, | 53 | TorchInGraphFunctionVariable, |
| 54 | ) | 54 | ) |
| 55 | 55 | ||
| 56 | torch_c_binding_in_graph_functions_npu = dict.fromkeys( | 56 | torch_c_binding_in_graph_functions_npu = dict.fromkeys( |
| 57 | [ | 57 | [ |
| 58 | "torch_npu._C._npu_changeCurrentAllocator", | 58 | "torch_npu._C._npu_changeCurrentAllocator", |
| 59 | "torch_npu._C._npu_npuCachingAllocator_set_allocator_settings", | 59 | "torch_npu._C._npu_npuCachingAllocator_set_allocator_settings", |
| 60 | "torch_npu._C._npu_emptyCache", | 60 | "torch_npu._C._npu_emptyCache", |
| 61 | "torch_npu._C._npu_getAllocator", | 61 | "torch_npu._C._npu_getAllocator", |
| 62 | "torch_npu._C._npu_getCheckpointState", | 62 | "torch_npu._C._npu_getCheckpointState", |
| 63 | "torch_npu._C._npu_getCurrentStream", | 63 | "torch_npu._C._npu_getCurrentStream", |
| 64 | "torch_npu._C._npu_getDefaultStream", | 64 | "torch_npu._C._npu_getDefaultStream", |
| 65 | "torch_npu._C._npu_init", | 65 | "torch_npu._C._npu_init", |
| 66 | "torch_npu._C._npu_ipc_collect", | 66 | "torch_npu._C._npu_ipc_collect", |
| 67 | "torch_npu._C._npu_resetAccumulatedHostMemoryStats", | 67 | "torch_npu._C._npu_resetAccumulatedHostMemoryStats", |
| 68 | "torch_npu._C._npu_resetPeakHostMemoryStats", | 68 | "torch_npu._C._npu_resetPeakHostMemoryStats", |
| 69 | "torch_npu._C._npu_resetPeakMemoryStats", | 69 | "torch_npu._C._npu_resetPeakMemoryStats", |
| 70 | "torch_npu._C._npu_set_sync_debug_mode", | 70 | "torch_npu._C._npu_set_sync_debug_mode", |
| 71 | "torch_npu._C._npu_setDevice", | 71 | "torch_npu._C._npu_setDevice", |
| 72 | "torch_npu._C._npu_getMemoryFraction", | 72 | "torch_npu._C._npu_getMemoryFraction", |
| 73 | "torch_npu._C._npu_setMemoryFraction", | 73 | "torch_npu._C._npu_setMemoryFraction", |
| 74 | "torch_npu._C._npu_synchronize", | 74 | "torch_npu._C._npu_synchronize", |
| 75 | "torch_npu._C._npu_resetAccumulatedMemoryStats", | 75 | "torch_npu._C._npu_resetAccumulatedMemoryStats", |
| 76 | "torch_npu._C._npu_hasPrimaryContext", | 76 | "torch_npu._C._npu_hasPrimaryContext", |
| 77 | "torch_npu._C._npu_setStream", | 77 | "torch_npu._C._npu_setStream", |
| 78 | ], | 78 | ], |
| 79 | TorchInGraphFunctionVariable, | 79 | TorchInGraphFunctionVariable, |
| 80 | ) | 80 | ) |
| 81 | 81 | ||
| 82 | skip_functions_npu = dict.fromkeys( | 82 | skip_functions_npu = dict.fromkeys( |
| 83 | [ | 83 | [ |
| 84 | "torch.npu.set_device", | 84 | "torch.npu.set_device", |
| 85 | ], | 85 | ], |
| 86 | SkipFunctionVariable | 86 | SkipFunctionVariable |
| 87 | ) | 87 | ) |
| 88 | 88 | ||
| 89 | 89 | ||
| 90 | def _patch_npu_trace_rules(): | 90 | def _patch_npu_trace_rules(): |
| 91 | torch._dynamo.trace_rules.clear_lru_cache() | 91 | torch._dynamo.trace_rules.clear_lru_cache() |
| 92 | torch._dynamo.trace_rules.torch_name_rule_map.append(torch_non_c_binding_in_graph_functions_npu) | 92 | torch._dynamo.trace_rules.torch_name_rule_map.append(torch_non_c_binding_in_graph_functions_npu) |
| 93 | torch._dynamo.trace_rules.torch_name_rule_map.append(torch_c_binding_in_graph_functions_npu) | 93 | torch._dynamo.trace_rules.torch_name_rule_map.append(torch_c_binding_in_graph_functions_npu) |
| 94 | torch._dynamo.trace_rules.torch_name_rule_map.append(skip_functions_npu) | 94 | torch._dynamo.trace_rules.torch_name_rule_map.append(skip_functions_npu) |
| 95 | torch_module.constant_fold_functions[torch.npu.current_device] = True | 95 | torch_module.constant_fold_functions[torch.npu.current_device] = True |
| 96 | torch_module.constant_fold_functions[torch.npu.get_device_properties] = True | 96 | torch_module.constant_fold_functions[torch.npu.get_device_properties] = True |
| 97 | torch_module.constant_fold_functions_need_guards[torch.npu.current_device] = True | 97 | torch_module.constant_fold_functions_need_guards[torch.npu.current_device] = True |
| 98 | torch_module.constant_fold_functions[torch.npu.is_available] = True | 98 | torch_module.constant_fold_functions[torch.npu.is_available] = True |
| 99 | common_constant_types.add(torch_npu._C._NPUDeviceProperties) | 99 | common_constant_types.add(torch_npu._C._NPUDeviceProperties) |
| 100 | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | __all__ = [ | 1 | __all__ = [ |
| 2 | "autocast", "GradScaler", "custom_fwd", "custom_bwd" | 2 | "autocast", "GradScaler", "custom_fwd", "custom_bwd" |
| 3 | ] | 3 | ] |
| 4 | 4 | ||
| 5 | from .autocast_mode import autocast, custom_fwd, custom_bwd # noqa: F401 | 5 | from .autocast_mode import autocast, custom_fwd, custom_bwd # noqa: F401 |
O | |||
| 6 | from .grad_scaler import GradScaler # noqa: F401 | 6 | from .grad_scaler import GradScaler # noqa: F401 |
| @@ -1,98 +1,98 @@ | |||
| 1 | __all__ = ["autocast", "custom_fwd", "custom_bwd"] | 1 | __all__ = ["autocast", "custom_fwd", "custom_bwd"] |
| 2 | 2 | ||
| 3 | 3 | ||
| 4 | import functools | 4 | import functools |
| 5 | import collections | 5 | import collections |
| 6 | from typing import Any | 6 | from typing import Any |
| 7 | from typing_extensions import deprecated | 7 | from typing_extensions import deprecated |
| 8 | 8 | ||
| 9 | try: | 9 | try: |
| 10 | import numpy as np | 10 | import numpy as np |
| 11 | 11 | ||
| 12 | HAS_NUMPY = True | 12 | HAS_NUMPY = True |
| 13 | except ModuleNotFoundError: | 13 | except ModuleNotFoundError: |
| 14 | np = None # type: ignore[assignment] | 14 | np = None # type: ignore[assignment] |
| 15 | 15 | ||
| 16 | import torch | 16 | import torch |
| 17 | import torch_npu | 17 | import torch_npu |
O 【openlibing.ci】识别到代码检查告警抑制注释,匹配工具:mypy,请Committer检视其合理性。 ![]() ![]() | |||
| 18 | from torch_npu.utils._error_code import ErrCode, pta_error | 18 | from torch_npu.utils._error_code import ErrCode, pta_error |
| 19 | 19 | ||
| 20 | 20 | ||
| 21 | class autocast(torch.amp.autocast_mode.autocast): | 21 | class autocast(torch.amp.autocast_mode.autocast): |
| 22 | r""" | 22 | r""" |
| 23 | See :class:`torch.autocast`. | 23 | See :class:`torch.autocast`. |
| 24 | ``torch.npu.amp.autocast(args...)`` is equivalent to ``torch.autocast("npu", args...)`` | 24 | ``torch.npu.amp.autocast(args...)`` is equivalent to ``torch.autocast("npu", args...)`` |
| 25 | """ | 25 | """ |
| 26 | 26 | ||
| 27 | def __init__(self, enabled: bool = True, dtype: torch.dtype = torch.float16, cache_enabled: bool = True): | 27 | def __init__(self, enabled: bool = True, dtype: torch.dtype = torch.float16, cache_enabled: bool = True): |
| 28 | if torch._jit_internal.is_scripting(): | 28 | if torch._jit_internal.is_scripting(): |
| 29 | self._enabled = enabled | 29 | self._enabled = enabled |
| 30 | self.device = "npu" | 30 | self.device = "npu" |
| 31 | self.fast_dtype = dtype | 31 | self.fast_dtype = dtype |
| 32 | return | 32 | return |
| 33 | super().__init__("npu", enabled=enabled, dtype=dtype, cache_enabled=cache_enabled) | 33 | super().__init__("npu", enabled=enabled, dtype=dtype, cache_enabled=cache_enabled) |
| 34 | 34 | ||
| 35 | def __enter__(self): | 35 | def __enter__(self): |
| 36 | if torch._jit_internal.is_scripting(): | 36 | if torch._jit_internal.is_scripting(): |
| 37 | return self | 37 | return self |
| 38 | return super().__enter__() | 38 | return super().__enter__() |
| 39 | 39 | ||
| 40 | def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any): # type: ignore[override] | 40 | def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any): # type: ignore[override] |
| 41 | if torch._jit_internal.is_scripting(): | 41 | if torch._jit_internal.is_scripting(): |
| 42 | return None | 42 | return None |
O 【openlibing.ci】识别到代码检查告警抑制注释,匹配工具:mypy,请Committer检视其合理性。 ![]() ![]() | |||
| 43 | return super().__exit__(exc_type, exc_val, exc_tb) | 43 | return super().__exit__(exc_type, exc_val, exc_tb) |
| 44 | 44 | ||
| 45 | def __call__(self, func): | 45 | def __call__(self, func): |
| 46 | if torch._jit_internal.is_scripting(): | 46 | if torch._jit_internal.is_scripting(): |
| 47 | return func | 47 | return func |
| 48 | return super().__call__(func) | 48 | return super().__call__(func) |
| 49 | 49 | ||
| 50 | 50 | ||
| 51 | # Casts Tensors and containers of Tensors. Special-cases passthroughs for strings and np.ndarrays, which | 51 | # Casts Tensors and containers of Tensors. Special-cases passthroughs for strings and np.ndarrays, which |
| 52 | # may be falsely detected as "Iterables." | 52 | # may be falsely detected as "Iterables." |
| 53 | def _cast(value, dtype): | 53 | def _cast(value, dtype): |
| 54 | if isinstance(value, torch.Tensor): | 54 | if isinstance(value, torch.Tensor): |
| 55 | is_eligible = (value.is_floating_point() and value.device.type == 'npu' and (value.dtype is not torch.float64)) | 55 | is_eligible = (value.is_floating_point() and value.device.type == 'npu' and (value.dtype is not torch.float64)) |
| 56 | return value.to(dtype) if is_eligible else value | 56 | return value.to(dtype) if is_eligible else value |
| 57 | elif isinstance(value, (str, bytes)): | 57 | elif isinstance(value, (str, bytes)): |
| 58 | return value | 58 | return value |
| 59 | elif HAS_NUMPY and isinstance(value, np.ndarray): | 59 | elif HAS_NUMPY and isinstance(value, np.ndarray): |
| 60 | return value | 60 | return value |
| 61 | elif isinstance(value, collections.abc.Mapping): | 61 | elif isinstance(value, collections.abc.Mapping): |
| 62 | return {_cast(k, dtype): _cast(v, dtype) for k, v in value.items()} | 62 | return {_cast(k, dtype): _cast(v, dtype) for k, v in value.items()} |
| 63 | elif isinstance(value, collections.abc.Iterable): | 63 | elif isinstance(value, collections.abc.Iterable): |
| 64 | iterable = map(lambda v: _cast(v, dtype), value) | 64 | iterable = map(lambda v: _cast(v, dtype), value) |
| 65 | if isinstance(value, list) or isinstance(value, tuple): | 65 | if isinstance(value, list) or isinstance(value, tuple): |
| 66 | return type(value)(iterable) | 66 | return type(value)(iterable) |
| 67 | else: | 67 | else: |
| 68 | return iterable | 68 | return iterable |
| 69 | else: | 69 | else: |
| 70 | return value | 70 | return value |
| 71 | 71 | ||
| 72 | 72 | ||
| 73 | @deprecated( | 73 | @deprecated( |
| 74 | "`torch_npu.npu.amp.custom_fwd(args...)` is deprecated. " | 74 | "`torch_npu.npu.amp.custom_fwd(args...)` is deprecated. " |
| 75 | "Please use `torch.amp.custom_fwd(args..., device_type='npu')` instead.", | 75 | "Please use `torch.amp.custom_fwd(args..., device_type='npu')` instead.", |
| 76 | category=FutureWarning, | 76 | category=FutureWarning, |
| 77 | ) | 77 | ) |
| 78 | def custom_fwd(fwd=None, *, cast_inputs=None): | 78 | def custom_fwd(fwd=None, *, cast_inputs=None): |
| 79 | """ | 79 | """ |
| 80 | ``torch_npu.npu.amp.custom_fwd(args...)`` is deprecated. Please use | 80 | ``torch_npu.npu.amp.custom_fwd(args...)`` is deprecated. Please use |
| 81 | ``torch.amp.custom_fwd(args..., device_type='npu')`` instead. | 81 | ``torch.amp.custom_fwd(args..., device_type='npu')`` instead. |
| 82 | """ | 82 | """ |
| 83 | return functools.partial(torch.amp.custom_fwd, device_type="npu")( | 83 | return functools.partial(torch.amp.custom_fwd, device_type="npu")( |
| 84 | fwd=fwd, cast_inputs=cast_inputs | 84 | fwd=fwd, cast_inputs=cast_inputs |
| 85 | ) | 85 | ) |
| 86 | 86 | ||
| 87 | 87 | ||
| 88 | @deprecated( | 88 | @deprecated( |
| 89 | "`torch_npu.npu.amp.custom_bwd(args...)` is deprecated. " | 89 | "`torch_npu.npu.amp.custom_bwd(args...)` is deprecated. " |
| 90 | "Please use `torch.amp.custom_bwd(args..., device_type='npu')` instead.", | 90 | "Please use `torch.amp.custom_bwd(args..., device_type='npu')` instead.", |
| 91 | category=FutureWarning, | 91 | category=FutureWarning, |
| 92 | ) | 92 | ) |
| 93 | def custom_bwd(bwd): | 93 | def custom_bwd(bwd): |
| 94 | """ | 94 | """ |
| 95 | ``torch_npu.npu.amp.custom_bwd(args...)`` is deprecated. Please use | 95 | ``torch_npu.npu.amp.custom_bwd(args...)`` is deprecated. Please use |
| 96 | ``torch.amp.custom_bwd(args..., device_type='npu')`` instead. | 96 | ``torch.amp.custom_bwd(args..., device_type='npu')`` instead. |
| 97 | """ | 97 | """ |
| 98 | return functools.partial(torch.amp.custom_bwd, device_type="npu")(bwd) | 98 | return functools.partial(torch.amp.custom_bwd, device_type="npu")(bwd) |
| @@ -1,5 +1,5 @@ | |||
| 1 | import torch_npu | 1 | import torch_npu |
| 2 | 2 | ||
| 3 | 3 | ||
| 4 | def amp_definitely_not_available(): | 4 | def amp_definitely_not_available(): |
| 5 | return not torch_npu.npu.is_available() | 5 | return not torch_npu.npu.is_available() |
| @@ -1,505 +1,505 @@ | |||
| 1 | import warnings | 1 | import warnings |
| 2 | from collections import defaultdict | 2 | from collections import defaultdict |
| 3 | import collections.abc as container_abcs | 3 | import collections.abc as container_abcs |
| 4 | from typing import List | 4 | from typing import List |
| 5 | 5 | ||
| 6 | import torch | 6 | import torch |
| 7 | import torch.distributed as dist | 7 | import torch.distributed as dist |
| 8 | from torch.amp.grad_scaler import _MultiDeviceReplicator, OptState, _refresh_per_optimizer_state | 8 | from torch.amp.grad_scaler import _MultiDeviceReplicator, OptState, _refresh_per_optimizer_state |
| 9 | from torch.amp.grad_scaler import GradScaler as BaseGradScaler | 9 | from torch.amp.grad_scaler import GradScaler as BaseGradScaler |
| 10 | import torch_npu | 10 | import torch_npu |
| 11 | from torch_npu.utils._error_code import ErrCode, pta_error | 11 | from torch_npu.utils._error_code import ErrCode, pta_error |
| 12 | from .common import amp_definitely_not_available | 12 | from .common import amp_definitely_not_available |
| 13 | 13 | ||
| 14 | 14 | ||
| 15 | class _NpuMultiDeviceReplicator(_MultiDeviceReplicator): | 15 | class _NpuMultiDeviceReplicator(_MultiDeviceReplicator): |
| 16 | """ | 16 | """ |
| 17 | Lazily serves copies of a tensor to requested devices. Copies are cached per-device. | 17 | Lazily serves copies of a tensor to requested devices. Copies are cached per-device. |
| 18 | """ | 18 | """ |
| 19 | 19 | ||
| 20 | def __init__(self, master_tensor: torch.Tensor) -> None: | 20 | def __init__(self, master_tensor: torch.Tensor) -> None: |
| 21 | if not master_tensor.is_npu: | 21 | if not master_tensor.is_npu: |
| 22 | raise ValueError("Device type of master_tensor should be npu." + pta_error(ErrCode.VALUE)) | 22 | raise ValueError("Device type of master_tensor should be npu." + pta_error(ErrCode.VALUE)) |
| 23 | self.master = master_tensor | 23 | self.master = master_tensor |
| 24 | self._per_device_tensors = {} | 24 | self._per_device_tensors = {} |
| 25 | 25 | ||
| 26 | 26 | ||
| 27 | class GradScaler(BaseGradScaler): | 27 | class GradScaler(BaseGradScaler): |
| 28 | """ | 28 | """ |
| 29 | An instance ``scaler`` of :class:`GradScaler` helps perform the steps of gradient scaling | 29 | An instance ``scaler`` of :class:`GradScaler` helps perform the steps of gradient scaling |
| 30 | conveniently. | 30 | conveniently. |
| 31 | 31 | ||
| 32 | * ``scaler.scale(loss)`` multiplies a given loss by ``scaler``'s current scale factor. | 32 | * ``scaler.scale(loss)`` multiplies a given loss by ``scaler``'s current scale factor. |
| 33 | * ``scaler.step(optimizer)`` safely unscales gradients and calls ``optimizer.step()``. | 33 | * ``scaler.step(optimizer)`` safely unscales gradients and calls ``optimizer.step()``. |
| 34 | * ``scaler.update()`` updates ``scaler``'s scale factor. | 34 | * ``scaler.update()`` updates ``scaler``'s scale factor. |
| 35 | 35 | ||
| 36 | Example:: | 36 | Example:: |
| 37 | 37 | ||
| 38 | # Creates a GradScaler once at the beginning of training. | 38 | # Creates a GradScaler once at the beginning of training. |
| 39 | scaler = GradScaler() | 39 | scaler = GradScaler() |
| 40 | 40 | ||
| 41 | for epoch in epochs: | 41 | for epoch in epochs: |
| 42 | for input, target in data: | 42 | for input, target in data: |
| 43 | optimizer.zero_grad() | 43 | optimizer.zero_grad() |
| 44 | output = model(input) | 44 | output = model(input) |
| 45 | loss = loss_fn(output, target) | 45 | loss = loss_fn(output, target) |
| 46 | 46 | ||
| 47 | # Scales loss. Calls backward() on scaled loss to create scaled gradients. | 47 | # Scales loss. Calls backward() on scaled loss to create scaled gradients. |
| 48 | scaler.scale(loss).backward() | 48 | scaler.scale(loss).backward() |
| 49 | 49 | ||
| 50 | # scaler.step() first unscales gradients of the optimizer's params. | 50 | # scaler.step() first unscales gradients of the optimizer's params. |
| 51 | # If gradients don't contain infs/NaNs, optimizer.step() is then called, | 51 | # If gradients don't contain infs/NaNs, optimizer.step() is then called, |
| 52 | # otherwise, optimizer.step() is skipped. | 52 | # otherwise, optimizer.step() is skipped. |
| 53 | scaler.step(optimizer) | 53 | scaler.step(optimizer) |
| 54 | 54 | ||
| 55 | # Updates the scale for next iteration. | 55 | # Updates the scale for next iteration. |
| 56 | scaler.update() | 56 | scaler.update() |
| 57 | 57 | ||
| 58 | See the :ref:`Automatic Mixed Precision examples<amp-examples>` for usage | 58 | See the :ref:`Automatic Mixed Precision examples<amp-examples>` for usage |
| 59 | (along with autocasting) in more complex cases like gradient clipping, gradient accumulation, gradient penalty, | 59 | (along with autocasting) in more complex cases like gradient clipping, gradient accumulation, gradient penalty, |
| 60 | and multiple losses/optimizers. | 60 | and multiple losses/optimizers. |
| 61 | 61 | ||
| 62 | ``scaler`` dynamically estimates the scale factor each iteration. To minimize gradient underflow, | 62 | ``scaler`` dynamically estimates the scale factor each iteration. To minimize gradient underflow, |
| 63 | a large scale factor should be used. However, ``float16`` values can "overflow" (become inf or NaN) if | 63 | a large scale factor should be used. However, ``float16`` values can "overflow" (become inf or NaN) if |
| 64 | the scale factor is too large. Therefore, the optimal scale factor is the largest factor that can be used | 64 | the scale factor is too large. Therefore, the optimal scale factor is the largest factor that can be used |
| 65 | without incurring inf or NaN gradient values. | 65 | without incurring inf or NaN gradient values. |
| 66 | ``scaler`` approximates the optimal scale factor over time by checking the gradients for infs and NaNs during every | 66 | ``scaler`` approximates the optimal scale factor over time by checking the gradients for infs and NaNs during every |
| 67 | ``scaler.step(optimizer)`` (or optional separate ``scaler.unscale_(optimizer)``, see :meth:`unscale_`). | 67 | ``scaler.step(optimizer)`` (or optional separate ``scaler.unscale_(optimizer)``, see :meth:`unscale_`). |
| 68 | 68 | ||
| 69 | * If infs/NaNs are found, ``scaler.step(optimizer)`` skips the underlying ``optimizer.step()`` (so the params | 69 | * If infs/NaNs are found, ``scaler.step(optimizer)`` skips the underlying ``optimizer.step()`` (so the params |
| 70 | themselves remain uncorrupted) and ``update()`` multiplies the scale by ``backoff_factor``. | 70 | themselves remain uncorrupted) and ``update()`` multiplies the scale by ``backoff_factor``. |
| 71 | 71 | ||
| 72 | * If no infs/NaNs are found, ``scaler.step(optimizer)`` runs the underlying ``optimizer.step()`` as usual. | 72 | * If no infs/NaNs are found, ``scaler.step(optimizer)`` runs the underlying ``optimizer.step()`` as usual. |
| 73 | If ``growth_interval`` unskipped iterations occur consecutively, ``update()`` multiplies the scale by | 73 | If ``growth_interval`` unskipped iterations occur consecutively, ``update()`` multiplies the scale by |
| 74 | ``growth_factor``. | 74 | ``growth_factor``. |
| 75 | 75 | ||
| 76 | The scale factor often causes infs/NaNs to appear in gradients for the first few iterations as its | 76 | The scale factor often causes infs/NaNs to appear in gradients for the first few iterations as its |
| 77 | value calibrates. ``scaler.step`` will skip the underlying ``optimizer.step()`` for these | 77 | value calibrates. ``scaler.step`` will skip the underlying ``optimizer.step()`` for these |
| 78 | iterations. After that, step skipping should occur rarely (once every few hundred or thousand iterations). | 78 | iterations. After that, step skipping should occur rarely (once every few hundred or thousand iterations). |
| 79 | 79 | ||
| 80 | Args: | 80 | Args: |
| 81 | init_scale (float, optional, default=2.**16): Initial scale factor. | 81 | init_scale (float, optional, default=2.**16): Initial scale factor. |
| 82 | growth_factor (float, optional, default=2.0): Factor by which the scale is multiplied during | 82 | growth_factor (float, optional, default=2.0): Factor by which the scale is multiplied during |
| 83 | :meth:`update` if no inf/NaN gradients occur for ``growth_interval`` consecutive iterations. | 83 | :meth:`update` if no inf/NaN gradients occur for ``growth_interval`` consecutive iterations. |
| 84 | backoff_factor (float, optional, default=0.5): Factor by which the scale is multiplied during | 84 | backoff_factor (float, optional, default=0.5): Factor by which the scale is multiplied during |
| 85 | :meth:`update` if inf/NaN gradients occur in an iteration. | 85 | :meth:`update` if inf/NaN gradients occur in an iteration. |
| 86 | growth_interval (int, optional, default=2000): Number of consecutive iterations without inf/NaN gradients | 86 | growth_interval (int, optional, default=2000): Number of consecutive iterations without inf/NaN gradients |
| 87 | that must occur for the scale to be multiplied by ``growth_factor``. | 87 | that must occur for the scale to be multiplied by ``growth_factor``. |
| 88 | dynamic (bool, optional, default=True): If ``False``, use static loss scale. | 88 | dynamic (bool, optional, default=True): If ``False``, use static loss scale. |
| 89 | enabled (bool, optional, default=True): If ``False``, disables gradient scaling. :meth:`step` simply | 89 | enabled (bool, optional, default=True): If ``False``, disables gradient scaling. :meth:`step` simply |
| 90 | invokes the underlying ``optimizer.step()``, and other methods become no-ops. | 90 | invokes the underlying ``optimizer.step()``, and other methods become no-ops. |
| 91 | """ | 91 | """ |
| 92 | 92 | ||
| 93 | def __init__(self, | 93 | def __init__(self, |
| 94 | init_scale=2. ** 16, | 94 | init_scale=2. ** 16, |
| 95 | growth_factor=2.0, | 95 | growth_factor=2.0, |
| 96 | backoff_factor=0.5, | 96 | backoff_factor=0.5, |
| 97 | growth_interval=2000, | 97 | growth_interval=2000, |
| 98 | dynamic=True, | 98 | dynamic=True, |
| 99 | enabled=True): | 99 | enabled=True): |
| 100 | if enabled and amp_definitely_not_available(): | 100 | if enabled and amp_definitely_not_available(): |
| 101 | warnings.warn("torch_npu.amp.GradScaler is enabled, but NPU is not available. Disabling.") | 101 | warnings.warn("torch_npu.amp.GradScaler is enabled, but NPU is not available. Disabling.") |
| 102 | self._enabled = False | 102 | self._enabled = False |
| 103 | else: | 103 | else: |
| 104 | self._enabled = enabled | 104 | self._enabled = enabled |
| 105 | 105 | ||
| 106 | if self._enabled: | 106 | if self._enabled: |
| 107 | if growth_factor <= 1.0: | 107 | if growth_factor <= 1.0: |
| 108 | raise ValueError("The growth factor must be > 1.0." + pta_error(ErrCode.VALUE)) | 108 | raise ValueError("The growth factor must be > 1.0." + pta_error(ErrCode.VALUE)) |
| 109 | if backoff_factor >= 1.0: | 109 | if backoff_factor >= 1.0: |
| 110 | raise ValueError("The backoff factor must be < 1.0." + pta_error(ErrCode.VALUE)) | 110 | raise ValueError("The backoff factor must be < 1.0." + pta_error(ErrCode.VALUE)) |
| 111 | 111 | ||
| 112 | self._init_scale = init_scale | 112 | self._init_scale = init_scale |
| 113 | # self._scale will be lazily initialized during the first call to scale() | 113 | # self._scale will be lazily initialized during the first call to scale() |
| 114 | self._scale = None | 114 | self._scale = None |
| 115 | self._growth_factor = growth_factor | 115 | self._growth_factor = growth_factor |
| 116 | self._backoff_factor = backoff_factor | 116 | self._backoff_factor = backoff_factor |
| 117 | self._growth_interval = growth_interval | 117 | self._growth_interval = growth_interval |
| 118 | self._dynamic = dynamic | 118 | self._dynamic = dynamic |
| 119 | self._init_growth_tracker = 0 | 119 | self._init_growth_tracker = 0 |
| 120 | # self._growth_tracker will be lazily initialized during the first call to scale() | 120 | # self._growth_tracker will be lazily initialized during the first call to scale() |
| 121 | self._growth_tracker = None | 121 | self._growth_tracker = None |
| 122 | self._per_optimizer_states = defaultdict(_refresh_per_optimizer_state) | 122 | self._per_optimizer_states = defaultdict(_refresh_per_optimizer_state) |
| 123 | self._has_overflow = False | 123 | self._has_overflow = False |
| 124 | self._clear_overflow_flag = False | 124 | self._clear_overflow_flag = False |
| 125 | self._dist_initialized = False | 125 | self._dist_initialized = False |
| 126 | self._dist_overflow_count = None | 126 | self._dist_overflow_count = None |
| 127 | 127 | ||
| 128 | def _lazy_init_scale_growth_tracker(self, dev): | 128 | def _lazy_init_scale_growth_tracker(self, dev): |
| 129 | if self._growth_tracker is not None: | 129 | if self._growth_tracker is not None: |
| 130 | raise RuntimeError("_growth_tracker initialized before _scale" + pta_error(ErrCode.VALUE)) | 130 | raise RuntimeError("_growth_tracker initialized before _scale" + pta_error(ErrCode.VALUE)) |
| 131 | 131 | ||
| 132 | self._scale = torch.full((), self._init_scale, dtype=torch.float32).pin_memory().to(dev, non_blocking=True) | 132 | self._scale = torch.full((), self._init_scale, dtype=torch.float32).pin_memory().to(dev, non_blocking=True) |
| 133 | self._growth_tracker = torch.full((), self._init_growth_tracker, dtype=torch.int32) | 133 | self._growth_tracker = torch.full((), self._init_growth_tracker, dtype=torch.int32) |
| 134 | self._growth_tracker = self._growth_tracker.pin_memory().to(dev, non_blocking=True) | 134 | self._growth_tracker = self._growth_tracker.pin_memory().to(dev, non_blocking=True) |
| 135 | 135 | ||
| 136 | def _lazy_init_dist_flag_and_dist_overflow_count(self): | 136 | def _lazy_init_dist_flag_and_dist_overflow_count(self): |
| 137 | if self._dist_overflow_count is not None: | 137 | if self._dist_overflow_count is not None: |
| 138 | raise RuntimeError("_dist_overflow_count initialized before _scale" + pta_error(ErrCode.VALUE)) | 138 | raise RuntimeError("_dist_overflow_count initialized before _scale" + pta_error(ErrCode.VALUE)) |
| 139 | try: | 139 | try: |
| 140 | if dist.is_initialized(): | 140 | if dist.is_initialized(): |
| 141 | self._dist_initialized = True | 141 | self._dist_initialized = True |
| 142 | except AttributeError as err: | 142 | except AttributeError as err: |
| 143 | print("torch.distributed has no attribute is_initialized") | 143 | print("torch.distributed has no attribute is_initialized") |
| 144 | 144 | ||
| 145 | self._dist_overflow_count = torch.Tensor([0.]).to('npu') | 145 | self._dist_overflow_count = torch.Tensor([0.]).to('npu') |
| 146 | 146 | ||
| 147 | def scale(self, outputs): | 147 | def scale(self, outputs): |
| 148 | """ | 148 | """ |
| 149 | Multiplies ('scales') a tensor or list of tensors by the scale factor. | 149 | Multiplies ('scales') a tensor or list of tensors by the scale factor. |
| 150 | 150 | ||
| 151 | Returns scaled outputs. If this instance of :class:`GradScaler` is not enabled, outputs are returned | 151 | Returns scaled outputs. If this instance of :class:`GradScaler` is not enabled, outputs are returned |
| 152 | unmodified. | 152 | unmodified. |
| 153 | 153 | ||
| 154 | Args: | 154 | Args: |
| 155 | outputs (Tensor or iterable of Tensors): Outputs to scale. | 155 | outputs (Tensor or iterable of Tensors): Outputs to scale. |
| 156 | """ | 156 | """ |
| 157 | if not self._enabled: | 157 | if not self._enabled: |
| 158 | return outputs | 158 | return outputs |
| 159 | 159 | ||
| 160 | if self._dist_overflow_count is None: | 160 | if self._dist_overflow_count is None: |
| 161 | self._lazy_init_dist_flag_and_dist_overflow_count() | 161 | self._lazy_init_dist_flag_and_dist_overflow_count() |
| 162 | if self._dist_overflow_count is None: | 162 | if self._dist_overflow_count is None: |
| 163 | raise RuntimeError("_dist_overflow_count is None." + pta_error(ErrCode.VALUE)) | 163 | raise RuntimeError("_dist_overflow_count is None." + pta_error(ErrCode.VALUE)) |
| 164 | 164 | ||
| 165 | if self._dynamic and not self._clear_overflow_flag: | 165 | if self._dynamic and not self._clear_overflow_flag: |
| 166 | if not torch_npu.npu.utils.is_support_inf_nan(): | 166 | if not torch_npu.npu.utils.is_support_inf_nan(): |
| 167 | GradScaler.clear_npu_overflow_flag() | 167 | GradScaler.clear_npu_overflow_flag() |
| 168 | self._clear_overflow_flag = True | 168 | self._clear_overflow_flag = True |
| 169 | 169 | ||
| 170 | # Short-circuit for the common case. | 170 | # Short-circuit for the common case. |
| 171 | if isinstance(outputs, torch.Tensor): | 171 | if isinstance(outputs, torch.Tensor): |
| 172 | if not outputs.is_npu: | 172 | if not outputs.is_npu: |
| 173 | raise ValueError("Device type of outputs should be npu." + pta_error(ErrCode.VALUE)) | 173 | raise ValueError("Device type of outputs should be npu." + pta_error(ErrCode.VALUE)) |
| 174 | if self._scale is None: | 174 | if self._scale is None: |
| 175 | self._lazy_init_scale_growth_tracker(outputs.device) | 175 | self._lazy_init_scale_growth_tracker(outputs.device) |
| 176 | if self._scale is None: | 176 | if self._scale is None: |
| 177 | raise RuntimeError("_scale is None." + pta_error(ErrCode.VALUE)) | 177 | raise RuntimeError("_scale is None." + pta_error(ErrCode.VALUE)) |
| 178 | return outputs * self._scale.to(device=outputs.device, non_blocking=True) | 178 | return outputs * self._scale.to(device=outputs.device, non_blocking=True) |
| 179 | 179 | ||
| 180 | # Invoke the more complex machinery only if we're treating multiple outputs. | 180 | # Invoke the more complex machinery only if we're treating multiple outputs. |
| 181 | stash: List[_NpuMultiDeviceReplicator] = [] # holds a reference that can be overwritten by apply_scale | 181 | stash: List[_NpuMultiDeviceReplicator] = [] # holds a reference that can be overwritten by apply_scale |
| 182 | 182 | ||
| 183 | def apply_scale(val): | 183 | def apply_scale(val): |
| 184 | if isinstance(val, torch.Tensor): | 184 | if isinstance(val, torch.Tensor): |
| 185 | if not val.is_npu: | 185 | if not val.is_npu: |
| 186 | raise ValueError("Device type of val should be npu." + pta_error(ErrCode.VALUE)) | 186 | raise ValueError("Device type of val should be npu." + pta_error(ErrCode.VALUE)) |
| 187 | if len(stash) == 0: | 187 | if len(stash) == 0: |
| 188 | if self._scale is None: | 188 | if self._scale is None: |
| 189 | self._lazy_init_scale_growth_tracker(val.device) | 189 | self._lazy_init_scale_growth_tracker(val.device) |
| 190 | if self._scale is None: | 190 | if self._scale is None: |
| 191 | raise RuntimeError("_scale is None." + pta_error(ErrCode.VALUE)) | 191 | raise RuntimeError("_scale is None." + pta_error(ErrCode.VALUE)) |
| 192 | stash.append(_NpuMultiDeviceReplicator(self._scale)) | 192 | stash.append(_NpuMultiDeviceReplicator(self._scale)) |
| 193 | return val * stash[0].get(val.device) | 193 | return val * stash[0].get(val.device) |
| 194 | elif isinstance(val, container_abcs.Iterable): | 194 | elif isinstance(val, container_abcs.Iterable): |
| 195 | iterable = map(apply_scale, val) | 195 | iterable = map(apply_scale, val) |
| 196 | if isinstance(val, list) or isinstance(val, tuple): | 196 | if isinstance(val, list) or isinstance(val, tuple): |
| 197 | return type(val)(iterable) | 197 | return type(val)(iterable) |
| 198 | else: | 198 | else: |
| 199 | return iterable | 199 | return iterable |
| 200 | else: | 200 | else: |
| 201 | raise TypeError("outputs must be a Tensor or an iterable of Tensors" + pta_error(ErrCode.TYPE)) | 201 | raise TypeError("outputs must be a Tensor or an iterable of Tensors" + pta_error(ErrCode.TYPE)) |
| 202 | 202 | ||
| 203 | return apply_scale(outputs) | 203 | return apply_scale(outputs) |
| 204 | 204 | ||
| 205 | def _unscale_grads_(self, optimizer, inv_scale, found_inf, allow_fp16): | 205 | def _unscale_grads_(self, optimizer, inv_scale, found_inf, allow_fp16): |
| 206 | per_device_found_inf = _NpuMultiDeviceReplicator(found_inf) | 206 | per_device_found_inf = _NpuMultiDeviceReplicator(found_inf) |
| 207 | per_device_inv_scale = _NpuMultiDeviceReplicator(inv_scale) | 207 | per_device_inv_scale = _NpuMultiDeviceReplicator(inv_scale) |
| 208 | 208 | ||
| 209 | # To set up _amp_foreach_non_finite_check_and_unscale_, split grads by device and dtype. | 209 | # To set up _amp_foreach_non_finite_check_and_unscale_, split grads by device and dtype. |
| 210 | # There could be hundreds of grads, so we'd like to iterate through them just once. | 210 | # There could be hundreds of grads, so we'd like to iterate through them just once. |
| 211 | # However, we don't know their devices or dtypes in advance. | 211 | # However, we don't know their devices or dtypes in advance. |
| 212 | 212 | ||
| 213 | # Google says mypy struggles with defaultdicts type annotations. | 213 | # Google says mypy struggles with defaultdicts type annotations. |
| 214 | per_device_and_dtype_grads = defaultdict(lambda: defaultdict(list)) | 214 | per_device_and_dtype_grads = defaultdict(lambda: defaultdict(list)) |
| 215 | with torch.no_grad(): | 215 | with torch.no_grad(): |
| 216 | if hasattr(optimizer, 'is_fused_optimizer'): | 216 | if hasattr(optimizer, 'is_fused_optimizer'): |
| 217 | if not optimizer.is_params_grads_combined: | 217 | if not optimizer.is_params_grads_combined: |
| 218 | optimizer._maybe_init_combined_params_and_grads() | 218 | optimizer._maybe_init_combined_params_and_grads() |
| 219 | 219 | ||
| 220 | device = found_inf.device | 220 | device = found_inf.device |
| 221 | for grads_combined_one_dtype in optimizer.grads_all_group_combined: | 221 | for grads_combined_one_dtype in optimizer.grads_all_group_combined: |
| 222 | if grads_combined_one_dtype is None: | 222 | if grads_combined_one_dtype is None: |
| 223 | continue | 223 | continue |
| 224 | if self._dynamic: | 224 | if self._dynamic: |
| 225 | torch._amp_foreach_non_finite_check_and_unscale_( | 225 | torch._amp_foreach_non_finite_check_and_unscale_( |
| 226 | [grads_combined_one_dtype], | 226 | [grads_combined_one_dtype], |
| 227 | per_device_found_inf.get(device), | 227 | per_device_found_inf.get(device), |
| 228 | per_device_inv_scale.get(device)) | 228 | per_device_inv_scale.get(device)) |
| 229 | if per_device_found_inf.get(device).item() > 0: | 229 | if per_device_found_inf.get(device).item() > 0: |
| 230 | self._has_overflow = True | 230 | self._has_overflow = True |
| 231 | else: | 231 | else: |
| 232 | grads_combined_one_dtype.mul_( | 232 | grads_combined_one_dtype.mul_( |
| 233 | per_device_inv_scale.get(device)) | 233 | per_device_inv_scale.get(device)) |
| 234 | else: | 234 | else: |
| 235 | for group in optimizer.param_groups: | 235 | for group in optimizer.param_groups: |
| 236 | for param in group["params"]: | 236 | for param in group["params"]: |
| 237 | if param.grad is None: | 237 | if param.grad is None: |
| 238 | continue | 238 | continue |
| 239 | if (not allow_fp16) and param.grad.dtype == torch.float16: | 239 | if (not allow_fp16) and param.grad.dtype == torch.float16: |
| 240 | raise TypeError("Attempting to unscale FP16 gradients." + pta_error(ErrCode.TYPE)) | 240 | raise TypeError("Attempting to unscale FP16 gradients." + pta_error(ErrCode.TYPE)) |
| 241 | if param.grad.is_sparse: | 241 | if param.grad.is_sparse: |
| 242 | # is_coalesced() == False means the sparse grad has values with duplicate indices. | 242 | # is_coalesced() == False means the sparse grad has values with duplicate indices. |
| 243 | # coalesce() deduplicates indices and adds all values that have the same index. | 243 | # coalesce() deduplicates indices and adds all values that have the same index. |
| 244 | # For scaled fp16 values, there's a good chance coalescing will cause overflow, | 244 | # For scaled fp16 values, there's a good chance coalescing will cause overflow, |
| 245 | # so we should check the coalesced _values(). | 245 | # so we should check the coalesced _values(). |
| 246 | if param.grad.dtype == torch.float16: | 246 | if param.grad.dtype == torch.float16: |
| 247 | param.grad = param.grad.coalesce() | 247 | param.grad = param.grad.coalesce() |
| 248 | to_unscale = param.grad._values() | 248 | to_unscale = param.grad._values() |
| 249 | else: | 249 | else: |
| 250 | to_unscale = param.grad | 250 | to_unscale = param.grad |
| 251 | 251 | ||
| 252 | per_device_and_dtype_grads[to_unscale.device][to_unscale.dtype].append(to_unscale) | 252 | per_device_and_dtype_grads[to_unscale.device][to_unscale.dtype].append(to_unscale) |
| 253 | 253 | ||
| 254 | for device, per_dtype_grads in per_device_and_dtype_grads.items(): | 254 | for device, per_dtype_grads in per_device_and_dtype_grads.items(): |
| 255 | for grads in per_dtype_grads.values(): | 255 | for grads in per_dtype_grads.values(): |
| 256 | if self._dynamic: | 256 | if self._dynamic: |
| 257 | torch._amp_foreach_non_finite_check_and_unscale_(grads, | 257 | torch._amp_foreach_non_finite_check_and_unscale_(grads, |
| 258 | per_device_found_inf.get(device), | 258 | per_device_found_inf.get(device), |
| 259 | per_device_inv_scale.get(device)) | 259 | per_device_inv_scale.get(device)) |
| 260 | if per_device_found_inf.get(device).item() > 0: | 260 | if per_device_found_inf.get(device).item() > 0: |
| 261 | self._has_overflow = True | 261 | self._has_overflow = True |
| 262 | else: | 262 | else: |
| 263 | for grad in grads: | 263 | for grad in grads: |
| 264 | grad.mul_(per_device_inv_scale.get(device)) | 264 | grad.mul_(per_device_inv_scale.get(device)) |
| 265 | 265 | ||
| 266 | self._sync_dist_overflow_count() | 266 | self._sync_dist_overflow_count() |
| 267 | if self._has_overflow: | 267 | if self._has_overflow: |
| 268 | per_device_found_inf.get(found_inf.device).add_(1) | 268 | per_device_found_inf.get(found_inf.device).add_(1) |
| 269 | else: | 269 | else: |
| 270 | per_device_found_inf.get(found_inf.device) | 270 | per_device_found_inf.get(found_inf.device) |
| 271 | 271 | ||
| 272 | return per_device_found_inf._per_device_tensors | 272 | return per_device_found_inf._per_device_tensors |
| 273 | 273 | ||
| 274 | def unscale_(self, optimizer): | 274 | def unscale_(self, optimizer): |
| 275 | """ | 275 | """ |
| 276 | Divides ("unscales") the optimizer's gradient tensors by the scale factor. | 276 | Divides ("unscales") the optimizer's gradient tensors by the scale factor. |
| 277 | 277 | ||
| 278 | :meth:`unscale_` is optional, serving cases where you need to | 278 | :meth:`unscale_` is optional, serving cases where you need to |
| 279 | :ref:`modify or inspect gradients<working-with-unscaled-gradients>` | 279 | :ref:`modify or inspect gradients<working-with-unscaled-gradients>` |
| 280 | between the backward pass(es) and :meth:`step`. | 280 | between the backward pass(es) and :meth:`step`. |
| 281 | If :meth:`unscale_` is not called explicitly, gradients will be unscaled automatically during :meth:`step`. | 281 | If :meth:`unscale_` is not called explicitly, gradients will be unscaled automatically during :meth:`step`. |
| 282 | 282 | ||
| 283 | Simple example, using :meth:`unscale_` to enable clipping of unscaled gradients:: | 283 | Simple example, using :meth:`unscale_` to enable clipping of unscaled gradients:: |
| 284 | 284 | ||
| 285 | ... | 285 | ... |
| 286 | scaler.scale(loss).backward() | 286 | scaler.scale(loss).backward() |
| 287 | scaler.unscale_(optimizer) | 287 | scaler.unscale_(optimizer) |
| 288 | torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm) | 288 | torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm) |
| 289 | scaler.step(optimizer) | 289 | scaler.step(optimizer) |
| 290 | scaler.update() | 290 | scaler.update() |
| 291 | 291 | ||
| 292 | Args: | 292 | Args: |
| 293 | optimizer (torch.optim.Optimizer): Optimizer that owns the gradients to be unscaled. | 293 | optimizer (torch.optim.Optimizer): Optimizer that owns the gradients to be unscaled. |
| 294 | 294 | ||
| 295 | .. note:: | 295 | .. note:: |
| 296 | :meth:`unscale_` does not incur a CPU-NPU sync. | 296 | :meth:`unscale_` does not incur a CPU-NPU sync. |
| 297 | 297 | ||
| 298 | .. warning:: | 298 | .. warning:: |
| 299 | :meth:`unscale_` should only be called once per optimizer per :meth:`step` call, | 299 | :meth:`unscale_` should only be called once per optimizer per :meth:`step` call, |
| 300 | and only after all gradients for that optimizer's assigned parameters have been accumulated. | 300 | and only after all gradients for that optimizer's assigned parameters have been accumulated. |
| 301 | Calling :meth:`unscale_` twice for a given optimizer between each :meth:`step` triggers a RuntimeError. | 301 | Calling :meth:`unscale_` twice for a given optimizer between each :meth:`step` triggers a RuntimeError. |
| 302 | 302 | ||
| 303 | .. warning:: | 303 | .. warning:: |
| 304 | :meth:`unscale_` may unscale sparse gradients out of place, replacing the ``.grad`` attribute. | 304 | :meth:`unscale_` may unscale sparse gradients out of place, replacing the ``.grad`` attribute. |
| 305 | """ | 305 | """ |
| 306 | if not self._enabled: | 306 | if not self._enabled: |
| 307 | return | 307 | return |
| 308 | 308 | ||
| 309 | self._check_scale_growth_tracker("unscale_") | 309 | self._check_scale_growth_tracker("unscale_") |
| 310 | 310 | ||
| 311 | optimizer_state = self._per_optimizer_states[id(optimizer)] | 311 | optimizer_state = self._per_optimizer_states[id(optimizer)] |
| 312 | 312 | ||
| 313 | if optimizer_state["stage"] is OptState.UNSCALED: | 313 | if optimizer_state["stage"] is OptState.UNSCALED: |
| 314 | raise RuntimeError("unscale_() has already been called on this optimizer since the last update()." + | 314 | raise RuntimeError("unscale_() has already been called on this optimizer since the last update()." + |
| 315 | pta_error(ErrCode.INTERNAL)) | 315 | pta_error(ErrCode.INTERNAL)) |
| 316 | elif optimizer_state["stage"] is OptState.STEPPED: | 316 | elif optimizer_state["stage"] is OptState.STEPPED: |
| 317 | raise RuntimeError("unscale_() is being called after step()." + | 317 | raise RuntimeError("unscale_() is being called after step()." + |
| 318 | pta_error(ErrCode.INTERNAL)) | 318 | pta_error(ErrCode.INTERNAL)) |
| 319 | 319 | ||
| 320 | # FP32 division can be imprecise for certain compile options, so we carry out the reciprocal in FP64. | 320 | # FP32 division can be imprecise for certain compile options, so we carry out the reciprocal in FP64. |
| 321 | if self._scale is None: | 321 | if self._scale is None: |
| 322 | raise RuntimeError("_scale is None." + pta_error(ErrCode.VALUE)) | 322 | raise RuntimeError("_scale is None." + pta_error(ErrCode.VALUE)) |
| 323 | inv_scale = self._scale.float().reciprocal() | 323 | inv_scale = self._scale.float().reciprocal() |
| 324 | found_inf = torch.full((), 0.0, dtype=torch.float32).pin_memory().to(self._scale.device, non_blocking=True) | 324 | found_inf = torch.full((), 0.0, dtype=torch.float32).pin_memory().to(self._scale.device, non_blocking=True) |
| 325 | 325 | ||
| 326 | optimizer_state["found_inf_per_device"] = self._unscale_grads_(optimizer, inv_scale, found_inf, False) | 326 | optimizer_state["found_inf_per_device"] = self._unscale_grads_(optimizer, inv_scale, found_inf, False) |
| 327 | optimizer_state["stage"] = OptState.UNSCALED | 327 | optimizer_state["stage"] = OptState.UNSCALED |
| 328 | 328 | ||
| 329 | def _maybe_opt_step(self, optimizer, optimizer_state, *args, **kwargs): | 329 | def _maybe_opt_step(self, optimizer, optimizer_state, *args, **kwargs): |
| 330 | retval = None | 330 | retval = None |
| 331 | if not sum(v.item() for v in optimizer_state["found_inf_per_device"].values()) and not self._has_overflow: | 331 | if not sum(v.item() for v in optimizer_state["found_inf_per_device"].values()) and not self._has_overflow: |
| 332 | retval = optimizer.step(*args, **kwargs) | 332 | retval = optimizer.step(*args, **kwargs) |
| 333 | else: | 333 | else: |
| 334 | print("Gradient overflow. Skipping step") | 334 | print("Gradient overflow. Skipping step") |
| 335 | return retval | 335 | return retval |
| 336 | 336 | ||
| 337 | def step(self, optimizer, *args, **kwargs): | 337 | def step(self, optimizer, *args, **kwargs): |
| 338 | """ | 338 | """ |
| 339 | :meth:`step` carries out the following two operations: | 339 | :meth:`step` carries out the following two operations: |
| 340 | 340 | ||
| 341 | 1. Internally invokes ``unscale_(optimizer)`` (unless :meth:`unscale_` was explicitly called for ``optimizer`` | 341 | 1. Internally invokes ``unscale_(optimizer)`` (unless :meth:`unscale_` was explicitly called for ``optimizer`` |
| 342 | earlier in the iteration). As part of the :meth:`unscale_`, gradients are checked for infs/NaNs. | 342 | earlier in the iteration). As part of the :meth:`unscale_`, gradients are checked for infs/NaNs. |
| 343 | 2. If no inf/NaN gradients are found, invokes ``optimizer.step()`` using the unscaled | 343 | 2. If no inf/NaN gradients are found, invokes ``optimizer.step()`` using the unscaled |
| 344 | gradients. Otherwise, ``optimizer.step()`` is skipped to avoid corrupting the params. | 344 | gradients. Otherwise, ``optimizer.step()`` is skipped to avoid corrupting the params. |
| 345 | 345 | ||
| 346 | ``*args`` and ``**kwargs`` are forwarded to ``optimizer.step()``. | 346 | ``*args`` and ``**kwargs`` are forwarded to ``optimizer.step()``. |
| 347 | 347 | ||
| 348 | Returns the return value of ``optimizer.step(*args, **kwargs)``. | 348 | Returns the return value of ``optimizer.step(*args, **kwargs)``. |
| 349 | 349 | ||
| 350 | Args: | 350 | Args: |
| 351 | optimizer (torch.optim.Optimizer): Optimizer that applies the gradients. | 351 | optimizer (torch.optim.Optimizer): Optimizer that applies the gradients. |
| 352 | args: Any arguments. | 352 | args: Any arguments. |
| 353 | kwargs: Any keyword arguments. | 353 | kwargs: Any keyword arguments. |
| 354 | 354 | ||
| 355 | .. warning:: | 355 | .. warning:: |
| 356 | Closure use is not currently supported. | 356 | Closure use is not currently supported. |
| 357 | """ | 357 | """ |
| 358 | if (not self._enabled): | 358 | if (not self._enabled): |
| 359 | return optimizer.step(*args, **kwargs) | 359 | return optimizer.step(*args, **kwargs) |
| 360 | 360 | ||
| 361 | if "closure" in kwargs: | 361 | if "closure" in kwargs: |
| 362 | raise RuntimeError("Closure use is not currently supported if GradScaler is enabled." + | 362 | raise RuntimeError("Closure use is not currently supported if GradScaler is enabled." + |
| 363 | pta_error(ErrCode.NOT_SUPPORT)) | 363 | pta_error(ErrCode.NOT_SUPPORT)) |
| 364 | 364 | ||
| 365 | self._check_scale_growth_tracker("step") | 365 | self._check_scale_growth_tracker("step") |
| 366 | 366 | ||
| 367 | optimizer_state = self._per_optimizer_states[id(optimizer)] | 367 | optimizer_state = self._per_optimizer_states[id(optimizer)] |
| 368 | 368 | ||
| 369 | if optimizer_state["stage"] is OptState.STEPPED: | 369 | if optimizer_state["stage"] is OptState.STEPPED: |
| 370 | raise RuntimeError("step() has already been called since the last update()." + | 370 | raise RuntimeError("step() has already been called since the last update()." + |
| 371 | pta_error(ErrCode.INTERNAL)) | 371 | pta_error(ErrCode.INTERNAL)) |
| 372 | 372 | ||
| 373 | retval = None | 373 | retval = None |
| 374 | 374 | ||
| 375 | if (hasattr(optimizer, "_step_supports_amp_scaling") and optimizer._step_supports_amp_scaling): | 375 | if (hasattr(optimizer, "_step_supports_amp_scaling") and optimizer._step_supports_amp_scaling): |
| 376 | # This optimizer has customized scale-handling logic, so we can call optimizer.step() directly. | 376 | # This optimizer has customized scale-handling logic, so we can call optimizer.step() directly. |
| 377 | # The contract with custom optimizers is that their step() should accept an additional, | 377 | # The contract with custom optimizers is that their step() should accept an additional, |
| 378 | # optional grad_scaler kwarg. We append self to the kwargs so the custom optimizer has full information: | 378 | # optional grad_scaler kwarg. We append self to the kwargs so the custom optimizer has full information: |
| 379 | # it can query its own state, invoke unscale_ on itself, etc | 379 | # it can query its own state, invoke unscale_ on itself, etc |
| 380 | retval = optimizer.step(*args, **dict(kwargs, grad_scaler=self)) | 380 | retval = optimizer.step(*args, **dict(kwargs, grad_scaler=self)) |
| 381 | optimizer_state["stage"] = OptState.STEPPED | 381 | optimizer_state["stage"] = OptState.STEPPED |
| 382 | return retval | 382 | return retval |
| 383 | 383 | ||
| 384 | if optimizer_state["stage"] is OptState.READY: | 384 | if optimizer_state["stage"] is OptState.READY: |
| 385 | self.unscale_(optimizer) | 385 | self.unscale_(optimizer) |
| 386 | 386 | ||
| 387 | if len(optimizer_state["found_inf_per_device"]) <= 0: | 387 | if len(optimizer_state["found_inf_per_device"]) <= 0: |
| 388 | raise RuntimeError("No inf checks were recorded for this optimizer." + pta_error(ErrCode.INTERNAL)) | 388 | raise RuntimeError("No inf checks were recorded for this optimizer." + pta_error(ErrCode.INTERNAL)) |
| 389 | 389 | ||
| 390 | if self._dynamic: | 390 | if self._dynamic: |
| 391 | retval = self._maybe_opt_step(optimizer, optimizer_state, *args, **kwargs) | 391 | retval = self._maybe_opt_step(optimizer, optimizer_state, *args, **kwargs) |
| 392 | optimizer_state["stage"] = OptState.STEPPED | 392 | optimizer_state["stage"] = OptState.STEPPED |
| 393 | return retval | 393 | return retval |
| 394 | 394 | ||
| 395 | retval = optimizer.step(*args, **kwargs) | 395 | retval = optimizer.step(*args, **kwargs) |
| 396 | optimizer_state["stage"] = OptState.STEPPED | 396 | optimizer_state["stage"] = OptState.STEPPED |
| 397 | 397 | ||
| 398 | return retval | 398 | return retval |
| 399 | 399 | ||
| 400 | def update(self, new_scale=None): | 400 | def update(self, new_scale=None): |
| 401 | """ | 401 | """ |
| 402 | Updates the scale factor. | 402 | Updates the scale factor. |
| 403 | 403 | ||
| 404 | If any optimizer steps were skipped the scale is multiplied by ``backoff_factor`` | 404 | If any optimizer steps were skipped the scale is multiplied by ``backoff_factor`` |
| 405 | to reduce it. If ``growth_interval`` unskipped iterations occurred consecutively, | 405 | to reduce it. If ``growth_interval`` unskipped iterations occurred consecutively, |
| 406 | the scale is multiplied by ``growth_factor`` to increase it. | 406 | the scale is multiplied by ``growth_factor`` to increase it. |
| 407 | 407 | ||
| 408 | Passing ``new_scale`` sets the scale directly. | 408 | Passing ``new_scale`` sets the scale directly. |
| 409 | 409 | ||
| 410 | Args: | 410 | Args: |
| 411 | new_scale (float or :class:`torch.npu.FloatTensor`, optional, default=None): New scale factor. | 411 | new_scale (float or :class:`torch.npu.FloatTensor`, optional, default=None): New scale factor. |
| 412 | 412 | ||
| 413 | .. warning:: | 413 | .. warning:: |
| 414 | :meth:`update` should only be called at the end of the iteration, after ``scaler.step(optimizer)`` has | 414 | :meth:`update` should only be called at the end of the iteration, after ``scaler.step(optimizer)`` has |
| 415 | been invoked for all optimizers used this iteration. | 415 | been invoked for all optimizers used this iteration. |
| 416 | """ | 416 | """ |
| 417 | if not self._enabled: | 417 | if not self._enabled: |
| 418 | return | 418 | return |
| 419 | 419 | ||
| 420 | _scale, _ = self._check_scale_growth_tracker("update") | 420 | _scale, _ = self._check_scale_growth_tracker("update") |
| 421 | 421 | ||
| 422 | if new_scale is not None: | 422 | if new_scale is not None: |
| 423 | # Accept a new user-defined scale. | 423 | # Accept a new user-defined scale. |
| 424 | if isinstance(new_scale, float): | 424 | if isinstance(new_scale, float): |
| 425 | self._scale = torch.full((), new_scale, dtype=torch.float32) | 425 | self._scale = torch.full((), new_scale, dtype=torch.float32) |
| 426 | self._scale = self._scale.pin_memory().to(_scale.device, non_blocking=True) | 426 | self._scale = self._scale.pin_memory().to(_scale.device, non_blocking=True) |
| 427 | else: | 427 | else: |
| 428 | reason = "new_scale should be a float or a 1-element torch.npu.FloatTensor with requires_grad=False." | 428 | reason = "new_scale should be a float or a 1-element torch.npu.FloatTensor with requires_grad=False." |
| 429 | if not isinstance(new_scale, torch.npu.FloatTensor): # type: ignore[attr-defined] | 429 | if not isinstance(new_scale, torch.npu.FloatTensor): # type: ignore[attr-defined] |
| 430 | raise TypeError(reason + pta_error(ErrCode.TYPE)) | 430 | raise TypeError(reason + pta_error(ErrCode.TYPE)) |
| 431 | if new_scale.numel() != 1: | 431 | if new_scale.numel() != 1: |
O 【openlibing.ci】识别到代码检查告警抑制注释,匹配工具:mypy,请Committer检视其合理性。 ![]() ![]() | |||
| 432 | raise ValueError(reason + pta_error(ErrCode.VALUE)) | 432 | raise ValueError(reason + pta_error(ErrCode.VALUE)) |
| 433 | if new_scale.requires_grad: | 433 | if new_scale.requires_grad: |
| 434 | raise ValueError(reason + pta_error(ErrCode.VALUE)) | 434 | raise ValueError(reason + pta_error(ErrCode.VALUE)) |
| 435 | self._scale = new_scale | 435 | self._scale = new_scale |
| 436 | elif self._dynamic: | 436 | elif self._dynamic: |
| 437 | self._npu_update_scale() | 437 | self._npu_update_scale() |
| 438 | 438 | ||
| 439 | # To prepare for next iteration, clear the data collected from optimizers this iteration. | 439 | # To prepare for next iteration, clear the data collected from optimizers this iteration. |
| 440 | self._per_optimizer_states = defaultdict(_refresh_per_optimizer_state) | 440 | self._per_optimizer_states = defaultdict(_refresh_per_optimizer_state) |
| 441 | self._has_overflow = False | 441 | self._has_overflow = False |
| 442 | self._clear_overflow_flag = False | 442 | self._clear_overflow_flag = False |
| 443 | 443 | ||
| 444 | def state_dict(self): | 444 | def state_dict(self): |
| 445 | state = super(GradScaler, self).state_dict() | 445 | state = super(GradScaler, self).state_dict() |
| 446 | if self._enabled: | 446 | if self._enabled: |
| 447 | state["dynamic"] = self._dynamic | 447 | state["dynamic"] = self._dynamic |
| 448 | return state | 448 | return state |
| 449 | 449 | ||
| 450 | def load_state_dict(self, state_dict): | 450 | def load_state_dict(self, state_dict): |
| 451 | if not self._enabled: | 451 | if not self._enabled: |
| 452 | return | 452 | return |
| 453 | 453 | ||
| 454 | if len(state_dict) == 0: | 454 | if len(state_dict) == 0: |
| 455 | raise RuntimeError("The source state dict is empty, possibly because it was saved " | 455 | raise RuntimeError("The source state dict is empty, possibly because it was saved " |
| 456 | "from a disabled instance of GradScaler." + pta_error(ErrCode.VALUE)) | 456 | "from a disabled instance of GradScaler." + pta_error(ErrCode.VALUE)) |
| 457 | 457 | ||
| 458 | super(GradScaler, self).load_state_dict(state_dict) | 458 | super(GradScaler, self).load_state_dict(state_dict) |
| 459 | self._dynamic = state_dict["dynamic"] | 459 | self._dynamic = state_dict["dynamic"] |
| 460 | 460 | ||
| 461 | @staticmethod | 461 | @staticmethod |
| 462 | def get_npu_overflow_flag(): | 462 | def get_npu_overflow_flag(): |
| 463 | float_status = torch.zeros(8).pin_memory().to('npu', non_blocking=True) | 463 | float_status = torch.zeros(8).pin_memory().to('npu', non_blocking=True) |
| 464 | result = torch_npu.npu_get_float_status(float_status) | 464 | result = torch_npu.npu_get_float_status(float_status) |
| 465 | if (result.cpu()[0] != 0): | 465 | if (result.cpu()[0] != 0): |
| 466 | return True | 466 | return True |
| 467 | else: | 467 | else: |
| 468 | return False | 468 | return False |
| 469 | 469 | ||
| 470 | @staticmethod | 470 | @staticmethod |
| 471 | def clear_npu_overflow_flag(): | 471 | def clear_npu_overflow_flag(): |
| 472 | float_status = torch.zeros(8).pin_memory().to('npu', non_blocking=True) | 472 | float_status = torch.zeros(8).pin_memory().to('npu', non_blocking=True) |
| 473 | result = torch_npu.npu_clear_float_status(float_status) | 473 | result = torch_npu.npu_clear_float_status(float_status) |
| 474 | 474 | ||
| 475 | def _sync_dist_overflow_count(self): | 475 | def _sync_dist_overflow_count(self): |
| 476 | if torch_npu.npu.utils.is_support_inf_nan(): | 476 | if torch_npu.npu.utils.is_support_inf_nan(): |
| 477 | return | 477 | return |
| 478 | if self._dynamic and self._dist_initialized: | 478 | if self._dynamic and self._dist_initialized: |
| 479 | if self._has_overflow: | 479 | if self._has_overflow: |
| 480 | self._dist_overflow_count.add_(1) | 480 | self._dist_overflow_count.add_(1) |
| 481 | dist.all_reduce(self._dist_overflow_count) | 481 | dist.all_reduce(self._dist_overflow_count) |
| 482 | self._dist_overflow_count.zero_() | 482 | self._dist_overflow_count.zero_() |
| 483 | else: | 483 | else: |
| 484 | dist.all_reduce(self._dist_overflow_count) | 484 | dist.all_reduce(self._dist_overflow_count) |
| 485 | if self._dist_overflow_count.item() != 0: | 485 | if self._dist_overflow_count.item() != 0: |
| 486 | self._has_overflow = True | 486 | self._has_overflow = True |
| 487 | self._dist_overflow_count.zero_() | 487 | self._dist_overflow_count.zero_() |
| 488 | 488 | ||
| 489 | def _npu_update_scale(self): | 489 | def _npu_update_scale(self): |
| 490 | if self._has_overflow: | 490 | if self._has_overflow: |
| 491 | self._scale.mul_(self._backoff_factor) | 491 | self._scale.mul_(self._backoff_factor) |
| 492 | self._growth_tracker.zero_() | 492 | self._growth_tracker.zero_() |
| 493 | print(("Loss scaler reducing loss scale " | 493 | print(("Loss scaler reducing loss scale " |
| 494 | "to {}").format(self._scale.item())) | 494 | "to {}").format(self._scale.item())) |
| 495 | else: | 495 | else: |
| 496 | # Entering this branch means we just carried out a successful step, | 496 | # Entering this branch means we just carried out a successful step, |
| 497 | # so growth_tracker is incremented before comparing to growth_interval. | 497 | # so growth_tracker is incremented before comparing to growth_interval. |
| 498 | self._growth_tracker.add_(1) | 498 | self._growth_tracker.add_(1) |
| 499 | if self._growth_tracker.item() == self._growth_interval: | 499 | if self._growth_tracker.item() == self._growth_interval: |
| 500 | new_scale = self._scale * self._growth_factor | 500 | new_scale = self._scale * self._growth_factor |
| 501 | if not torch.isinf(new_scale): | 501 | if not torch.isinf(new_scale): |
| 502 | self._scale = new_scale | 502 | self._scale = new_scale |
| 503 | self._growth_tracker.zero_() | 503 | self._growth_tracker.zero_() |
| 504 | print(("Loss scaler increasing loss scale " | 504 | print(("Loss scaler increasing loss scale " |
| 505 | "to {}").format(self._scale.item())) | 505 | "to {}").format(self._scale.item())) |
| @@ -1,110 +1,110 @@ | |||
| 1 | import unittest | 1 | import unittest |
| 2 | from functools import wraps | 2 | from functools import wraps |
| 3 | from typing import ( | 3 | from typing import ( |
| 4 | Tuple, | 4 | Tuple, |
| 5 | Dict, | 5 | Dict, |
| 6 | Any, | 6 | Any, |
| 7 | ) | 7 | ) |
| 8 | from collections import namedtuple | 8 | from collections import namedtuple |
| 9 | import sys | 9 | import sys |
| 10 | import os | 10 | import os |
| 11 | from contextlib import contextmanager | 11 | from contextlib import contextmanager |
| 12 | import torch | 12 | import torch |
| 13 | import torch.distributed as dist | 13 | import torch.distributed as dist |
| 14 | import torch_npu | 14 | import torch_npu |
| 15 | 15 | ||
| 16 | 16 | ||
| 17 | TestSkip = namedtuple('TestSkip', 'exit_code, message') | 17 | TestSkip = namedtuple('TestSkip', 'exit_code, message') |
| 18 | TEST_SKIPS = { | 18 | TEST_SKIPS = { |
| 19 | "multi-npu": TestSkip(75, "Multi-NPU condition not satisfied"), | 19 | "multi-npu": TestSkip(75, "Multi-NPU condition not satisfied"), |
| 20 | "multi-npu-1": TestSkip(75, "Need at least 1 ASCEND devices"), | 20 | "multi-npu-1": TestSkip(75, "Need at least 1 ASCEND devices"), |
| 21 | "multi-npu-2": TestSkip(75, "Need at least 2 ASCEND devices"), | 21 | "multi-npu-2": TestSkip(75, "Need at least 2 ASCEND devices"), |
| 22 | "multi-npu-3": TestSkip(75, "Need at least 3 ASCEND devices"), | 22 | "multi-npu-3": TestSkip(75, "Need at least 3 ASCEND devices"), |
| 23 | "multi-npu-4": TestSkip(75, "Need at least 4 ASCEND devices"), | 23 | "multi-npu-4": TestSkip(75, "Need at least 4 ASCEND devices"), |
| 24 | "multi-npu-5": TestSkip(75, "Need at least 5 ASCEND devices"), | 24 | "multi-npu-5": TestSkip(75, "Need at least 5 ASCEND devices"), |
| 25 | "multi-npu-6": TestSkip(75, "Need at least 6 ASCEND devices"), | 25 | "multi-npu-6": TestSkip(75, "Need at least 6 ASCEND devices"), |
| 26 | "multi-npu-7": TestSkip(75, "Need at least 7 ASCEND devices"), | 26 | "multi-npu-7": TestSkip(75, "Need at least 7 ASCEND devices"), |
| 27 | "multi-npu-8": TestSkip(75, "Need at least 8 ASCEND devices"), | 27 | "multi-npu-8": TestSkip(75, "Need at least 8 ASCEND devices"), |
| 28 | "hccl":TestSkip(76, "c10d not compiled with HCCL support"), | 28 | "hccl":TestSkip(76, "c10d not compiled with HCCL support"), |
| 29 | "known_issues":TestSkip(77, "Test skipped due to known issues"), | 29 | "known_issues":TestSkip(77, "Test skipped due to known issues"), |
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | 32 | ||
| 33 | def skipIfUnsupportMultiNPU(npu_number_needed): | 33 | def skipIfUnsupportMultiNPU(npu_number_needed): |
| 34 | def skip_dec(func): | 34 | def skip_dec(func): |
| 35 | @wraps(func) | 35 | @wraps(func) |
| 36 | def wrapper(self, *args, **kwargs): | 36 | def wrapper(self, *args, **kwargs): |
| 37 | if not torch.npu.is_available() or torch.npu.device_count() < npu_number_needed: | 37 | if not torch.npu.is_available() or torch.npu.device_count() < npu_number_needed: |
| 38 | raise unittest.SkipTest(f"Multi-NPU {npu_number_needed} condition not satisfied") | 38 | raise unittest.SkipTest(f"Multi-NPU {npu_number_needed} condition not satisfied") |
| 39 | return func(self, *args, **kwargs) | 39 | return func(self, *args, **kwargs) |
| 40 | return wrapper | 40 | return wrapper |
| 41 | return skip_dec | 41 | return skip_dec |
| 42 | 42 | ||
| 43 | 43 | ||
| 44 | def with_comms(func): | 44 | def with_comms(func): |
| 45 | if func is None: | 45 | if func is None: |
| 46 | raise RuntimeError("Test function is None.") | 46 | raise RuntimeError("Test function is None.") |
| 47 | 47 | ||
| 48 | @wraps(func) # pyre-ignore[6] | 48 | @wraps(func) # pyre-ignore[6] |
| 49 | def wrapper( | 49 | def wrapper( |
| 50 | self, *args: Tuple[object], **kwargs: Dict[str, Any] # type: ignore[misc] | 50 | self, *args: Tuple[object], **kwargs: Dict[str, Any] # type: ignore[misc] |
| 51 | ) -> None: | 51 | ) -> None: |
| 52 | # if backend not specified, and npu available, then use hccl, else gloo | 52 | # if backend not specified, and npu available, then use hccl, else gloo |
O 【openlibing.ci】识别到代码检查告警抑制注释,匹配工具:mypy,请Committer检视其合理性。 ![]() ![]() | |||
| 53 | if torch.npu.is_available() and torch.npu.device_count() >= self.world_size: | 53 | if torch.npu.is_available() and torch.npu.device_count() >= self.world_size: |
| 54 | self.device_type = "npu" | 54 | self.device_type = "npu" |
| 55 | else: | 55 | else: |
| 56 | self.device_type = "cpu" | 56 | self.device_type = "cpu" |
| 57 | 57 | ||
| 58 | pg_backend = ( | 58 | pg_backend = ( |
| 59 | "hccl" if self.device_type == "npu" else "gloo" | 59 | "hccl" if self.device_type == "npu" else "gloo" |
| 60 | ) | 60 | ) |
| 61 | if pg_backend == "hccl" and torch.npu.device_count() < self.world_size: | 61 | if pg_backend == "hccl" and torch.npu.device_count() < self.world_size: |
| 62 | raise RuntimeError(TEST_SKIPS[f"multi-npu-{self.world_size}"].message) | 62 | raise RuntimeError(TEST_SKIPS[f"multi-npu-{self.world_size}"].message) |
| 63 | 63 | ||
| 64 | init_pg(backend=pg_backend, world_size=self.world_size, rank=self.rank, file_name=self.file_name) | 64 | init_pg(backend=pg_backend, world_size=self.world_size, rank=self.rank, file_name=self.file_name) |
| 65 | 65 | ||
| 66 | torch.npu.manual_seed(0) | 66 | torch.npu.manual_seed(0) |
| 67 | torch.npu.initial_seed() | 67 | torch.npu.initial_seed() |
| 68 | func(self, *args, **kwargs) # type: ignore[misc] | 68 | func(self, *args, **kwargs) # type: ignore[misc] |
| 69 | self.destroy_pg() | 69 | self.destroy_pg() |
| 70 | 70 | ||
| 71 | return wrapper | 71 | return wrapper |
O 【openlibing.ci】识别到代码检查告警抑制注释,匹配工具:mypy,请Committer检视其合理性。 ![]() ![]() | |||
| 72 | 72 | ||
| 73 | 73 | ||
| 74 | def init_pg(backend: str = "hccl", world_size=1, rank=0, file_name="file://") -> None: | 74 | def init_pg(backend: str = "hccl", world_size=1, rank=0, file_name="file://") -> None: |
| 75 | if backend == "hccl" and torch.npu.device_count() < world_size: | 75 | if backend == "hccl" and torch.npu.device_count() < world_size: |
| 76 | raise RuntimeError(TEST_SKIPS[f"multi-npu-{world_size}"].message) | 76 | raise RuntimeError(TEST_SKIPS[f"multi-npu-{world_size}"].message) |
| 77 | 77 | ||
| 78 | if backend not in ["hccl", "gloo"]: | 78 | if backend not in ["hccl", "gloo"]: |
| 79 | raise RuntimeError(f"Backend {backend} not supported!") | 79 | raise RuntimeError(f"Backend {backend} not supported!") |
| 80 | 80 | ||
| 81 | dist.init_process_group( | 81 | dist.init_process_group( |
| 82 | backend=backend, | 82 | backend=backend, |
| 83 | world_size=world_size, | 83 | world_size=world_size, |
| 84 | rank=rank, # pyre-ignore[16] | 84 | rank=rank, # pyre-ignore[16] |
| 85 | init_method=f"file://{file_name}", # pyre-ignore[16] | 85 | init_method=f"file://{file_name}", # pyre-ignore[16] |
| 86 | ) | 86 | ) |
| 87 | 87 | ||
| 88 | # set device for hccl pg for collectives | 88 | # set device for hccl pg for collectives |
| 89 | if backend == "hccl": | 89 | if backend == "hccl": |
| 90 | torch.npu.set_device(rank) | 90 | torch.npu.set_device(rank) |
| 91 | 91 | ||
| 92 | 92 | ||
| 93 | @contextmanager | 93 | @contextmanager |
| 94 | def _dynamo_dist_per_rank_init(rank, world_size, init_pg_=True): | 94 | def _dynamo_dist_per_rank_init(rank, world_size, init_pg_=True): |
| 95 | # To avoid multiple inheritance from _dynamo.test_case.TestCase and MultiProcessTestCase, | 95 | # To avoid multiple inheritance from _dynamo.test_case.TestCase and MultiProcessTestCase, |
| 96 | # Just manually implement the most important part of the dynamo behavior to reset/clear. | 96 | # Just manually implement the most important part of the dynamo behavior to reset/clear. |
| 97 | torch_npu.npu.set_device(rank) | 97 | torch_npu.npu.set_device(rank) |
| 98 | os.environ['MASTER_ADDR'] = 'localhost' | 98 | os.environ['MASTER_ADDR'] = 'localhost' |
| 99 | os.environ['MASTER_PORT'] = '6789' | 99 | os.environ['MASTER_PORT'] = '6789' |
| 100 | if init_pg_: | 100 | if init_pg_: |
| 101 | dist.init_process_group(backend="hccl", rank=rank, world_size=world_size) | 101 | dist.init_process_group(backend="hccl", rank=rank, world_size=world_size) |
| 102 | torch._dynamo.reset() | 102 | torch._dynamo.reset() |
| 103 | torch._dynamo.utils.counters.clear() | 103 | torch._dynamo.utils.counters.clear() |
| 104 | try: | 104 | try: |
| 105 | yield | 105 | yield |
| 106 | finally: | 106 | finally: |
| 107 | torch._dynamo.reset() | 107 | torch._dynamo.reset() |
| 108 | torch._dynamo.utils.counters.clear() | 108 | torch._dynamo.utils.counters.clear() |
| 109 | if init_pg_: | 109 | if init_pg_: |
| 110 | dist.destroy_process_group() | 110 | dist.destroy_process_group() |
| @@ -1,142 +1,142 @@ | |||
| 1 | from functools import wraps, partialmethod | 1 | from functools import wraps, partialmethod |
| 2 | 2 | ||
| 3 | import os | 3 | import os |
| 4 | import inspect | 4 | import inspect |
| 5 | import itertools | 5 | import itertools |
| 6 | import torch | 6 | import torch |
| 7 | 7 | ||
| 8 | 8 | ||
| 9 | def feed_data(func, new_name, *args, **kwargs): | 9 | def feed_data(func, new_name, *args, **kwargs): |
| 10 | """ | 10 | """ |
| 11 | This internal method decorator feeds the test data item to the test. | 11 | This internal method decorator feeds the test data item to the test. |
| 12 | """ | 12 | """ |
| 13 | @wraps(func) | 13 | @wraps(func) |
| 14 | def wrapper(self): | 14 | def wrapper(self): |
| 15 | return func(self, *args, **kwargs) | 15 | return func(self, *args, **kwargs) |
| 16 | wrapper.__name__ = new_name | 16 | wrapper.__name__ = new_name |
| 17 | wrapper.__wrapped__ = func | 17 | wrapper.__wrapped__ = func |
| 18 | return wrapper | 18 | return wrapper |
| 19 | 19 | ||
| 20 | 20 | ||
| 21 | def instantiate_tests(arg=None, **kwargs): | 21 | def instantiate_tests(arg=None, **kwargs): |
| 22 | 22 | ||
| 23 | def wrapper(cls): | 23 | def wrapper(cls): |
| 24 | def gen_testcase(cls, func, name, key_list, func_args, value): | 24 | def gen_testcase(cls, func, name, key_list, func_args, value): |
| 25 | new_kwargs = dict(device="npu") if "device" in func_args else {} | 25 | new_kwargs = dict(device="npu") if "device" in func_args else {} |
| 26 | test_name = name | 26 | test_name = name |
| 27 | for k, v in zip(key_list, value): | 27 | for k, v in zip(key_list, value): |
| 28 | func_key = None | 28 | func_key = None |
| 29 | if k == "format": | 29 | if k == "format": |
| 30 | test_name += ("_" + str(v)) | 30 | test_name += ("_" + str(v)) |
| 31 | elif k == "dtype": | 31 | elif k == "dtype": |
| 32 | test_name += ("_" + str(v).split('.')[1]) | 32 | test_name += ("_" + str(v).split('.')[1]) |
| 33 | for _func_key in func_args: | 33 | for _func_key in func_args: |
| 34 | if k in _func_key: | 34 | if k in _func_key: |
| 35 | if func_key is not None: | 35 | if func_key is not None: |
| 36 | raise RuntimeError(f"Multiple matches for {k}") | 36 | raise RuntimeError(f"Multiple matches for {k}") |
| 37 | func_key = _func_key | 37 | func_key = _func_key |
| 38 | new_kwargs[func_key] = v | 38 | new_kwargs[func_key] = v |
| 39 | setattr(cls, test_name, feed_data(func, test_name, **new_kwargs)) | 39 | setattr(cls, test_name, feed_data(func, test_name, **new_kwargs)) |
| 40 | 40 | ||
| 41 | for name, func in list(cls.__dict__.items()): | 41 | for name, func in list(cls.__dict__.items()): |
| 42 | data = {} | 42 | data = {} |
| 43 | if hasattr(func, "dtypes"): | 43 | if hasattr(func, "dtypes"): |
| 44 | data['dtype'] = func.dtypes | 44 | data['dtype'] = func.dtypes |
| 45 | if hasattr(func, "formats"): | 45 | if hasattr(func, "formats"): |
| 46 | data['format'] = func.formats | 46 | data['format'] = func.formats |
| 47 | 47 | ||
| 48 | key_list = data.keys() | 48 | key_list = data.keys() |
| 49 | if not key_list: | 49 | if not key_list: |
| 50 | continue | 50 | continue |
| 51 | 51 | ||
| 52 | func_args = inspect.getfullargspec(func).args | 52 | func_args = inspect.getfullargspec(func).args |
| 53 | value_list = [data.get(key) for key in key_list] | 53 | value_list = [data.get(key) for key in key_list] |
| 54 | for value in itertools.product(*value_list): | 54 | for value in itertools.product(*value_list): |
| 55 | gen_testcase(cls, func, name, key_list, func_args, value) | 55 | gen_testcase(cls, func, name, key_list, func_args, value) |
| 56 | 56 | ||
| 57 | delattr(cls, name) | 57 | delattr(cls, name) |
| 58 | return cls | 58 | return cls |
| 59 | 59 | ||
| 60 | return wrapper(arg) | 60 | return wrapper(arg) |
| 61 | 61 | ||
| 62 | 62 | ||
| 63 | def gen_ops_testcase(cls, func, name, keys, value, op_info): | 63 | def gen_ops_testcase(cls, func, name, keys, value, op_info): |
| 64 | new_kwargs = {} | 64 | new_kwargs = {} |
| 65 | test_name = f'{func.__name__}_{name}' | 65 | test_name = f'{func.__name__}_{name}' |
| 66 | 66 | ||
| 67 | for k, v in zip(keys, value): | 67 | for k, v in zip(keys, value): |
| 68 | if k == "npu_format": | 68 | if k == "npu_format": |
| 69 | test_name += ("_" + str(v)) | 69 | test_name += ("_" + str(v)) |
| 70 | elif k == "dtype": | 70 | elif k == "dtype": |
| 71 | test_name += ("_" + str(v).split('.')[1]) | 71 | test_name += ("_" + str(v).split('.')[1]) |
| 72 | new_kwargs[k] = v | 72 | new_kwargs[k] = v |
| 73 | 73 | ||
| 74 | new_kwargs['op'] = op_info | 74 | new_kwargs['op'] = op_info |
| 75 | new_func = partialmethod(func, **new_kwargs) | 75 | new_func = partialmethod(func, **new_kwargs) |
| 76 | 76 | ||
| 77 | setattr(cls, test_name, new_func) | 77 | setattr(cls, test_name, new_func) |
| 78 | for decorator in op_info.get_decorators(cls.__name__, func.__name__, 'cpu', value[0], {}): | 78 | for decorator in op_info.get_decorators(cls.__name__, func.__name__, 'cpu', value[0], {}): |
| 79 | setattr(cls, test_name, decorator(new_func)) | 79 | setattr(cls, test_name, decorator(new_func)) |
| 80 | 80 | ||
| 81 | 81 | ||
| 82 | def gen_op_input(testcase, func, op_info): | 82 | def gen_op_input(testcase, func, op_info): |
| 83 | data = { | 83 | data = { |
| 84 | 'dtype': func.dtypes if hasattr(func, "dtypes") else op_info.dtypesIfNPU, | 84 | 'dtype': func.dtypes if hasattr(func, "dtypes") else op_info.dtypesIfNPU, |
| 85 | 'npu_format': func.formats if hasattr(func, "formats") else op_info.formats | 85 | 'npu_format': func.formats if hasattr(func, "formats") else op_info.formats |
| 86 | } | 86 | } |
| 87 | 87 | ||
| 88 | if 'test_variant_consistency_eager' in testcase: | 88 | if 'test_variant_consistency_eager' in testcase: |
| 89 | if torch.float32 in op_info.dtypesIfNPU: | 89 | if torch.float32 in op_info.dtypesIfNPU: |
| 90 | data['dtype'] = {torch.float32} | 90 | data['dtype'] = {torch.float32} |
| 91 | else: | 91 | else: |
| 92 | data['dtype'] = {list(op_info.dtypesIfNPU)[-1]} | 92 | data['dtype'] = {list(op_info.dtypesIfNPU)[-1]} |
| 93 | 93 | ||
| 94 | return data | 94 | return data |
| 95 | 95 | ||
| 96 | 96 | ||
| 97 | def instantiate_ops_tests(op_db): | 97 | def instantiate_ops_tests(op_db): |
| 98 | 98 | ||
| 99 | def wrapper(cls): | 99 | def wrapper(cls): |
| 100 | testcases = [x for x in dir(cls) if x.startswith('test_')] | 100 | testcases = [x for x in dir(cls) if x.startswith('test_')] |
| 101 | for testcase in testcases: | 101 | for testcase in testcases: |
| 102 | if hasattr(cls, testcase): | 102 | if hasattr(cls, testcase): |
| 103 | func = getattr(cls, testcase) | 103 | func = getattr(cls, testcase) |
| 104 | for op_info in op_db: | 104 | for op_info in op_db: |
| 105 | data = gen_op_input(testcase, func, op_info) | 105 | data = gen_op_input(testcase, func, op_info) |
| 106 | keys = data.keys() | 106 | keys = data.keys() |
| 107 | values = [data.get(key) for key in keys] | 107 | values = [data.get(key) for key in keys] |
| 108 | 108 | ||
| 109 | for value in itertools.product(*values): | 109 | for value in itertools.product(*values): |
| 110 | gen_ops_testcase(cls, func, op_info.name, keys, value, op_info) | 110 | gen_ops_testcase(cls, func, op_info.name, keys, value, op_info) |
| 111 | 111 | ||
| 112 | delattr(cls, testcase) | 112 | delattr(cls, testcase) |
| 113 | 113 | ||
| 114 | return cls | 114 | return cls |
| 115 | 115 | ||
| 116 | return wrapper | 116 | return wrapper |
| 117 | 117 | ||
| 118 | 118 | ||
| 119 | class Dtypes(object): | 119 | class Dtypes(object): |
| 120 | 120 | ||
| 121 | def __init__(self, *args): | 121 | def __init__(self, *args): |
| 122 | if (args is None or len(args) == 0): | 122 | if (args is None or len(args) == 0): |
| 123 | raise RuntimeError("No dtypes given") | 123 | raise RuntimeError("No dtypes given") |
| 124 | if not all(isinstance(arg, torch.dtype) for arg in args): | 124 | if not all(isinstance(arg, torch.dtype) for arg in args): |
| 125 | raise RuntimeError("Unknown dtype in {0}".format(str(args))) | 125 | raise RuntimeError("Unknown dtype in {0}".format(str(args))) |
| 126 | self.args = args | 126 | self.args = args |
| 127 | 127 | ||
| 128 | def __call__(self, fn): | 128 | def __call__(self, fn): |
| 129 | fn.dtypes = self.args | 129 | fn.dtypes = self.args |
| 130 | return fn | 130 | return fn |
| 131 | 131 | ||
| 132 | 132 | ||
| 133 | class Formats(object): | 133 | class Formats(object): |
| 134 | 134 | ||
| 135 | def __init__(self, *args): | 135 | def __init__(self, *args): |
| 136 | if args is None or len(args) == 0: | 136 | if args is None or len(args) == 0: |
| 137 | raise RuntimeError("No formats given") | 137 | raise RuntimeError("No formats given") |
| 138 | self.args = args | 138 | self.args = args |
| 139 | 139 | ||
| 140 | def __call__(self, fn): | 140 | def __call__(self, fn): |
| 141 | fn.formats = self.args | 141 | fn.formats = self.args |
| 142 | return fn | 142 | return fn |
| @@ -1,60 +1,60 @@ | |||
| 1 | import os | 1 | import os |
| 2 | import setuptools | 2 | import setuptools |
| 3 | 3 | ||
| 4 | import torch | 4 | import torch |
| 5 | import torch.utils.cpp_extension as TorchExtension | 5 | import torch.utils.cpp_extension as TorchExtension |
| 6 | 6 | ||
| 7 | import torch_npu | 7 | import torch_npu |
| 8 | 8 | ||
| 9 | PYTORCH_NPU_INSTALL_PATH = os.path.dirname(os.path.realpath(torch_npu.__file__)) | 9 | PYTORCH_NPU_INSTALL_PATH = os.path.dirname(os.path.realpath(torch_npu.__file__)) |
| 10 | 10 | ||
| 11 | 11 | ||
| 12 | def NpuExtension(name, sources, *args, **kwargs): | 12 | def NpuExtension(name, sources, *args, **kwargs): |
| 13 | r''' | 13 | r''' |
| 14 | Creates a :class:`setuptools.Extension` for C++. | 14 | Creates a :class:`setuptools.Extension` for C++. |
| 15 | 15 | ||
| 16 | Convenience method that creates a :class:`setuptools.Extension` with the | 16 | Convenience method that creates a :class:`setuptools.Extension` with the |
| 17 | bare minimum (but often sufficient) arguments to build a C++ extension. | 17 | bare minimum (but often sufficient) arguments to build a C++ extension. |
| 18 | 18 | ||
| 19 | All arguments are forwarded to the :class:`setuptools.Extension` | 19 | All arguments are forwarded to the :class:`setuptools.Extension` |
| 20 | constructor. | 20 | constructor. |
| 21 | 21 | ||
| 22 | Example: | 22 | Example: |
| 23 | >>> from setuptools import setup | 23 | >>> from setuptools import setup |
| 24 | >>> from torch_npu.utils.cpp_extension import NpuExtension | 24 | >>> from torch_npu.utils.cpp_extension import NpuExtension |
| 25 | >>> setup( | 25 | >>> setup( |
| 26 | name='extension', | 26 | name='extension', |
| 27 | ext_modules=[ | 27 | ext_modules=[ |
| 28 | NpuExtension( | 28 | NpuExtension( |
| 29 | name='extension', | 29 | name='extension', |
| 30 | sources=['extension.cpp'], | 30 | sources=['extension.cpp'], |
| 31 | extra_compile_args=['-g']), | 31 | extra_compile_args=['-g']), |
| 32 | ], | 32 | ], |
| 33 | cmdclass={ | 33 | cmdclass={ |
| 34 | 'build_ext': BuildExtension | 34 | 'build_ext': BuildExtension |
| 35 | }) | 35 | }) |
| 36 | ''' | 36 | ''' |
| 37 | 37 | ||
| 38 | torch_npu_dir = PYTORCH_NPU_INSTALL_PATH | 38 | torch_npu_dir = PYTORCH_NPU_INSTALL_PATH |
| 39 | include_dirs = kwargs.get('include_dirs', []) | 39 | include_dirs = kwargs.get('include_dirs', []) |
| 40 | include_dirs.append(os.path.join(torch_npu_dir, 'include')) | 40 | include_dirs.append(os.path.join(torch_npu_dir, 'include')) |
| 41 | include_dirs.append(os.path.join(torch_npu_dir, 'include', 'third_party', 'acl', 'inc')) | 41 | include_dirs.append(os.path.join(torch_npu_dir, 'include', 'third_party', 'acl', 'inc')) |
| 42 | include_dirs.append(os.path.join(torch_npu_dir, 'include', 'third_party', 'hccl', 'inc')) | 42 | include_dirs.append(os.path.join(torch_npu_dir, 'include', 'third_party', 'hccl', 'inc')) |
| 43 | include_dirs += TorchExtension.include_paths() | 43 | include_dirs += TorchExtension.include_paths() |
| 44 | kwargs['include_dirs'] = include_dirs | 44 | kwargs['include_dirs'] = include_dirs |
| 45 | 45 | ||
| 46 | library_dirs = kwargs.get('library_dirs', []) | 46 | library_dirs = kwargs.get('library_dirs', []) |
| 47 | library_dirs.append(os.path.join(torch_npu_dir, 'lib')) | 47 | library_dirs.append(os.path.join(torch_npu_dir, 'lib')) |
| 48 | library_dirs += TorchExtension.library_paths() | 48 | library_dirs += TorchExtension.library_paths() |
| 49 | kwargs['library_dirs'] = library_dirs | 49 | kwargs['library_dirs'] = library_dirs |
| 50 | 50 | ||
| 51 | libraries = kwargs.get('libraries', []) | 51 | libraries = kwargs.get('libraries', []) |
| 52 | libraries.append('c10') | 52 | libraries.append('c10') |
| 53 | libraries.append('torch') | 53 | libraries.append('torch') |
| 54 | libraries.append('torch_cpu') | 54 | libraries.append('torch_cpu') |
| 55 | libraries.append('torch_python') | 55 | libraries.append('torch_python') |
| 56 | libraries.append('torch_npu') | 56 | libraries.append('torch_npu') |
| 57 | kwargs['libraries'] = libraries | 57 | kwargs['libraries'] = libraries |
| 58 | 58 | ||
| 59 | kwargs['language'] = 'c++' | 59 | kwargs['language'] = 'c++' |
| 60 | return setuptools.Extension(name, sources, *args, **kwargs) | 60 | return setuptools.Extension(name, sources, *args, **kwargs) |
| @@ -38,4 +38,3 @@ class FlopsCounter(_FlopsCounter): | |||
| 38 | super().__init__() | 38 | super().__init__() |
| 39 | warnings.warn("torch_npu.utils.flops_count.FlopsCounter() will be deprecated. " | 39 | warnings.warn("torch_npu.utils.flops_count.FlopsCounter() will be deprecated. " |
| 40 | "If necessary, please use torch_npu.utils.FlopsCounter().", FutureWarning) | 40 | "If necessary, please use torch_npu.utils.FlopsCounter().", FutureWarning) |
| 41 | |||
| @@ -1,103 +1,103 @@ | |||
| 1 | import torch | 1 | import torch |
| 2 | import torch.distributed as dist | 2 | import torch.distributed as dist |
| 3 | from torch.autograd.function import Function | 3 | from torch.autograd.function import Function |
| 4 | 4 | ||
| 5 | import torch_npu | 5 | import torch_npu |
| 6 | from torch_npu.utils._error_code import ErrCode, ops_error | 6 | from torch_npu.utils._error_code import ErrCode, ops_error |
| 7 | 7 | ||
| 8 | 8 | ||
| 9 | __all__ = ["SyncBatchNorm"] | 9 | __all__ = ["SyncBatchNorm"] |
| 10 | 10 | ||
| 11 | 11 | ||
| 12 | class SyncBatchNorm(Function): | 12 | class SyncBatchNorm(Function): |
| 13 | 13 | ||
| 14 | @staticmethod | 14 | @staticmethod |
| 15 | def forward(self, input_tensor, weight, bias, running_mean, running_var, eps, momentum, process_group, world_size): | 15 | def forward(self, input_tensor, weight, bias, running_mean, running_var, eps, momentum, process_group, world_size): |
| 16 | input_tensor = input_tensor.contiguous() | 16 | input_tensor = input_tensor.contiguous() |
| 17 | input_shape = input_tensor.shape | 17 | input_shape = input_tensor.shape |
| 18 | input_tensor_ = input_tensor.reshape(input_shape[0], input_shape[1], 1, -1) | 18 | input_tensor_ = input_tensor.reshape(input_shape[0], input_shape[1], 1, -1) |
| 19 | # calculate sum/sum_square for input. | 19 | # calculate sum/sum_square for input. |
| 20 | sum_val, sum_square_val = torch_npu.batch_norm_reduce(input_tensor_, eps) | 20 | sum_val, sum_square_val = torch_npu.batch_norm_reduce(input_tensor_, eps) |
| 21 | 21 | ||
| 22 | count = torch.full((1,), | 22 | count = torch.full((1,), |
| 23 | input_tensor.numel() // input_tensor.size(1), | 23 | input_tensor.numel() // input_tensor.size(1), |
| 24 | dtype=sum_val.dtype, | 24 | dtype=sum_val.dtype, |
| 25 | device=sum_val.device) | 25 | device=sum_val.device) |
| 26 | 26 | ||
| 27 | num_channels = input_tensor.shape[1] | 27 | num_channels = input_tensor.shape[1] |
| 28 | # C, C, 1 -> (2C + 1) | 28 | # C, C, 1 -> (2C + 1) |
| 29 | combined = torch.cat([sum_val, sum_square_val, count], dim=0) | 29 | combined = torch.cat([sum_val, sum_square_val, count], dim=0) |
| 30 | # world_size * (2C + 1) | 30 | # world_size * (2C + 1) |
| 31 | combined_list = [torch.empty_like(combined) for k in range(world_size)] | 31 | combined_list = [torch.empty_like(combined) for k in range(world_size)] |
| 32 | dist.all_gather(combined_list, combined, process_group, async_op=False) | 32 | dist.all_gather(combined_list, combined, process_group, async_op=False) |
| 33 | combined = torch.stack(combined_list, dim=0) | 33 | combined = torch.stack(combined_list, dim=0) |
| 34 | # world_size * (2C + 1) -> world_size * C, world_size * C, world_size * 1 | 34 | # world_size * (2C + 1) -> world_size * C, world_size * C, world_size * 1 |
| 35 | sum_all, square_sum_all, count_all = torch.split(combined, num_channels, dim=1) | 35 | sum_all, square_sum_all, count_all = torch.split(combined, num_channels, dim=1) |
| 36 | 36 | ||
| 37 | size = count_all.view(-1).sum() | 37 | size = count_all.view(-1).sum() |
| 38 | if size == 1: | 38 | if size == 1: |
| 39 | raise ValueError('Expected more than 1 value per channel when training, got input size {}'.format(size) + | 39 | raise ValueError('Expected more than 1 value per channel when training, got input size {}'.format(size) + |
| 40 | ops_error(ErrCode.VALUE)) | 40 | ops_error(ErrCode.VALUE)) |
| 41 | 41 | ||
| 42 | # calculate global mean & invstd | 42 | # calculate global mean & invstd |
| 43 | mean, invstd = torch_npu.batch_norm_gather_stats_update(input_tensor, | 43 | mean, invstd = torch_npu.batch_norm_gather_stats_update(input_tensor, |
| 44 | sum_all, | 44 | sum_all, |
| 45 | square_sum_all, | 45 | square_sum_all, |
| 46 | running_mean, | 46 | running_mean, |
| 47 | running_var, | 47 | running_var, |
| 48 | momentum, | 48 | momentum, |
| 49 | eps, | 49 | eps, |
| 50 | count_all.view(-1)) | 50 | count_all.view(-1)) |
| 51 | 51 | ||
| 52 | self.save_for_backward(input_tensor, weight, mean, invstd, count_all) | 52 | self.save_for_backward(input_tensor, weight, mean, invstd, count_all) |
| 53 | self.process_group = process_group | 53 | self.process_group = process_group |
| 54 | 54 | ||
| 55 | # apply element-wise normalization | 55 | # apply element-wise normalization |
| 56 | out = torch.batch_norm_elemt(input_tensor, weight, bias, mean, invstd, eps) | 56 | out = torch.batch_norm_elemt(input_tensor, weight, bias, mean, invstd, eps) |
| 57 | return out | 57 | return out |
| 58 | 58 | ||
| 59 | @staticmethod | 59 | @staticmethod |
| 60 | def backward(self, grad_output): | 60 | def backward(self, grad_output): |
| 61 | if not grad_output.is_contiguous(memory_format=torch.channels_last): | 61 | if not grad_output.is_contiguous(memory_format=torch.channels_last): |
| 62 | grad_output = grad_output.contiguous() | 62 | grad_output = grad_output.contiguous() |
| 63 | saved_input, weight, mean, invstd, count_tensor = self.saved_tensors | 63 | saved_input, weight, mean, invstd, count_tensor = self.saved_tensors |
| 64 | grad_input = grad_weight = grad_bias = None | 64 | grad_input = grad_weight = grad_bias = None |
| 65 | process_group = self.process_group | 65 | process_group = self.process_group |
| 66 | 66 | ||
| 67 | # calculate local stats as well as grad_weight / grad_bias | 67 | # calculate local stats as well as grad_weight / grad_bias |
| 68 | sum_dy, sum_dy_xmu, grad_weight, grad_bias = torch.batch_norm_backward_reduce(grad_output, | 68 | sum_dy, sum_dy_xmu, grad_weight, grad_bias = torch.batch_norm_backward_reduce(grad_output, |
| 69 | saved_input, | 69 | saved_input, |
| 70 | mean, | 70 | mean, |
| 71 | invstd, | 71 | invstd, |
| 72 | weight, | 72 | weight, |
| 73 | self.needs_input_grad[0], | 73 | self.needs_input_grad[0], |
| 74 | self.needs_input_grad[1], | 74 | self.needs_input_grad[1], |
| 75 | self.needs_input_grad[2]) | 75 | self.needs_input_grad[2]) |
| 76 | 76 | ||
| 77 | if self.needs_input_grad[0]: | 77 | if self.needs_input_grad[0]: |
| 78 | # synchronizing stats used to calculate input gradient. | 78 | # synchronizing stats used to calculate input gradient. |
| 79 | num_channels = sum_dy.shape[0] | 79 | num_channels = sum_dy.shape[0] |
| 80 | combined = torch.cat([sum_dy, sum_dy_xmu], dim=0) | 80 | combined = torch.cat([sum_dy, sum_dy_xmu], dim=0) |
| 81 | torch.distributed.all_reduce( | 81 | torch.distributed.all_reduce( |
| 82 | combined, torch.distributed.ReduceOp.SUM, process_group, async_op=False) | 82 | combined, torch.distributed.ReduceOp.SUM, process_group, async_op=False) |
| 83 | sum_dy, sum_dy_xmu = torch.split(combined, num_channels) | 83 | sum_dy, sum_dy_xmu = torch.split(combined, num_channels) |
| 84 | 84 | ||
| 85 | # backward pass for gradient calculation | 85 | # backward pass for gradient calculation |
| 86 | grad_input = torch.batch_norm_backward_elemt(grad_output, | 86 | grad_input = torch.batch_norm_backward_elemt(grad_output, |
| 87 | saved_input, | 87 | saved_input, |
| 88 | mean, | 88 | mean, |
| 89 | invstd, | 89 | invstd, |
| 90 | weight, | 90 | weight, |
| 91 | sum_dy, | 91 | sum_dy, |
| 92 | sum_dy_xmu, | 92 | sum_dy_xmu, |
| 93 | count_tensor) | 93 | count_tensor) |
| 94 | 94 | ||
| 95 | # synchronizing of grad_weight / grad_bias is not needed as distributed | 95 | # synchronizing of grad_weight / grad_bias is not needed as distributed |
| 96 | # training would handle all reduce. | 96 | # training would handle all reduce. |
| 97 | if weight is None or not self.needs_input_grad[1]: | 97 | if weight is None or not self.needs_input_grad[1]: |
| 98 | grad_weight = None | 98 | grad_weight = None |
| 99 | 99 | ||
| 100 | if weight is None or not self.needs_input_grad[2]: | 100 | if weight is None or not self.needs_input_grad[2]: |
| 101 | grad_bias = None | 101 | grad_bias = None |
| 102 | 102 | ||
| 103 | return grad_input, grad_weight, grad_bias, None, None, None, None, None, None | 103 | return grad_input, grad_weight, grad_bias, None, None, None, None, None, None |
| @@ -1,88 +1,88 @@ | |||
| 1 | from functools import wraps | 1 | from functools import wraps |
| 2 | 2 | ||
| 3 | import torch | 3 | import torch |
| 4 | 4 | ||
| 5 | import torch_npu | 5 | import torch_npu |
| 6 | from torch_npu.utils._error_code import ErrCode, pta_error | 6 | from torch_npu.utils._error_code import ErrCode, pta_error |
| 7 | from torch_npu.utils.storage import _reduce_ex | 7 | from torch_npu.utils.storage import _reduce_ex |
| 8 | 8 | ||
| 9 | 9 | ||
| 10 | __all__ = [] | 10 | __all__ = [] |
| 11 | 11 | ||
| 12 | 12 | ||
| 13 | def _npu(self, *args, **kwargs): | 13 | def _npu(self, *args, **kwargs): |
| 14 | return torch_npu._C.npu(self, *args, **kwargs) | 14 | return torch_npu._C.npu(self, *args, **kwargs) |
| 15 | 15 | ||
| 16 | 16 | ||
| 17 | @property | 17 | @property |
| 18 | def _is_npu(self): | 18 | def _is_npu(self): |
| 19 | return torch_npu._C.is_npu(self) | 19 | return torch_npu._C.is_npu(self) |
| 20 | 20 | ||
| 21 | 21 | ||
| 22 | class _NPUTensortypeCache(object): | 22 | class _NPUTensortypeCache(object): |
| 23 | init = False | 23 | init = False |
| 24 | tensortype_list = [] | 24 | tensortype_list = [] |
| 25 | tensortype_dict = {} | 25 | tensortype_dict = {} |
| 26 | 26 | ||
| 27 | @classmethod | 27 | @classmethod |
| 28 | def tensortype_list_dict_init(cls): | 28 | def tensortype_list_dict_init(cls): |
| 29 | if not cls.init: | 29 | if not cls.init: |
| 30 | cls.tensortype_list += [ | 30 | cls.tensortype_list += [ |
| 31 | torch_npu.npu.BoolTensor, | 31 | torch_npu.npu.BoolTensor, |
| 32 | torch_npu.npu.ByteTensor, | 32 | torch_npu.npu.ByteTensor, |
| 33 | torch_npu.npu.CharTensor, | 33 | torch_npu.npu.CharTensor, |
| 34 | torch_npu.npu.DoubleTensor, | 34 | torch_npu.npu.DoubleTensor, |
| 35 | torch_npu.npu.FloatTensor, | 35 | torch_npu.npu.FloatTensor, |
| 36 | torch_npu.npu.HalfTensor, | 36 | torch_npu.npu.HalfTensor, |
| 37 | torch_npu.npu.IntTensor, | 37 | torch_npu.npu.IntTensor, |
| 38 | torch_npu.npu.LongTensor, | 38 | torch_npu.npu.LongTensor, |
| 39 | torch_npu.npu.ShortTensor, | 39 | torch_npu.npu.ShortTensor, |
| 40 | torch_npu.npu.BFloat16Tensor, | 40 | torch_npu.npu.BFloat16Tensor, |
| 41 | ] | 41 | ] |
| 42 | 42 | ||
| 43 | cls.tensortype_str_list = [ | 43 | cls.tensortype_str_list = [ |
| 44 | "torch_npu.npu.BoolTensor", | 44 | "torch_npu.npu.BoolTensor", |
| 45 | "torch_npu.npu.ByteTensor", | 45 | "torch_npu.npu.ByteTensor", |
| 46 | "torch_npu.npu.CharTensor", | 46 | "torch_npu.npu.CharTensor", |
| 47 | "torch_npu.npu.DoubleTensor", | 47 | "torch_npu.npu.DoubleTensor", |
| 48 | "torch_npu.npu.FloatTensor", | 48 | "torch_npu.npu.FloatTensor", |
| 49 | "torch_npu.npu.HalfTensor", | 49 | "torch_npu.npu.HalfTensor", |
| 50 | "torch_npu.npu.IntTensor", | 50 | "torch_npu.npu.IntTensor", |
| 51 | "torch_npu.npu.LongTensor", | 51 | "torch_npu.npu.LongTensor", |
| 52 | "torch_npu.npu.ShortTensor", | 52 | "torch_npu.npu.ShortTensor", |
| 53 | "torch_npu.npu.BFloat16Tensor", | 53 | "torch_npu.npu.BFloat16Tensor", |
| 54 | ] | 54 | ] |
| 55 | 55 | ||
| 56 | for tensortype, tensortype_str in zip(cls.tensortype_list, cls.tensortype_str_list): | 56 | for tensortype, tensortype_str in zip(cls.tensortype_list, cls.tensortype_str_list): |
| 57 | cls.tensortype_dict[tensortype_str] = tensortype | 57 | cls.tensortype_dict[tensortype_str] = tensortype |
| 58 | cls.tensortype_dict[tensortype_str.replace('torch_npu.', 'torch.')] = tensortype | 58 | cls.tensortype_dict[tensortype_str.replace('torch_npu.', 'torch.')] = tensortype |
| 59 | 59 | ||
| 60 | cls.init = True | 60 | cls.init = True |
| 61 | 61 | ||
| 62 | @classmethod | 62 | @classmethod |
| 63 | def get_tensortype_list(cls): | 63 | def get_tensortype_list(cls): |
| 64 | return cls.tensortype_list | 64 | return cls.tensortype_list |
| 65 | 65 | ||
| 66 | @classmethod | 66 | @classmethod |
| 67 | def get_tensortype_dict(cls): | 67 | def get_tensortype_dict(cls): |
| 68 | return cls.tensortype_dict | 68 | return cls.tensortype_dict |
| 69 | 69 | ||
| 70 | 70 | ||
| 71 | def _npu_type(self, dtype=None, non_blocking=False, **kwargs): | 71 | def _npu_type(self, dtype=None, non_blocking=False, **kwargs): |
| 72 | if dtype is None: | 72 | if dtype is None: |
| 73 | return self.type_raw(dtype, non_blocking, **kwargs) | 73 | return self.type_raw(dtype, non_blocking, **kwargs) |
| 74 | 74 | ||
| 75 | _NPUTensortypeCache.tensortype_list_dict_init() | 75 | _NPUTensortypeCache.tensortype_list_dict_init() |
| 76 | if isinstance(dtype, str) and dtype in _NPUTensortypeCache.get_tensortype_dict(): | 76 | if isinstance(dtype, str) and dtype in _NPUTensortypeCache.get_tensortype_dict(): |
| 77 | tensortype_class = _NPUTensortypeCache.get_tensortype_dict()[dtype] | 77 | tensortype_class = _NPUTensortypeCache.get_tensortype_dict()[dtype] |
| 78 | return self.to(dtype=tensortype_class.dtype, device='npu', non_blocking=non_blocking) | 78 | return self.to(dtype=tensortype_class.dtype, device='npu', non_blocking=non_blocking) |
| 79 | elif dtype in _NPUTensortypeCache.get_tensortype_list(): | 79 | elif dtype in _NPUTensortypeCache.get_tensortype_list(): |
| 80 | return self.to(dtype=dtype.dtype, device='npu', non_blocking=non_blocking) | 80 | return self.to(dtype=dtype.dtype, device='npu', non_blocking=non_blocking) |
| 81 | else: | 81 | else: |
| 82 | return self.type_raw(dtype, non_blocking, **kwargs) | 82 | return self.type_raw(dtype, non_blocking, **kwargs) |
| 83 | 83 | ||
| 84 | 84 | ||
| 85 | def _add_tensor_methods(): | 85 | def _add_tensor_methods(): |
| 86 | torch.Tensor.type_raw = torch.Tensor.type | 86 | torch.Tensor.type_raw = torch.Tensor.type |
| 87 | torch.Tensor.type = _npu_type | 87 | torch.Tensor.type = _npu_type |
| 88 | torch.Tensor.__reduce_ex__ = _reduce_ex | 88 | torch.Tensor.__reduce_ex__ = _reduce_ex |


此条代码评论区间+2至+5
【openlibing.ci】识别到代码检查告警抑制注释,匹配工具:flake8,请Committer检视其合理性。