已合并
[feature] 提升msKL tiling/kernel快速调用接口使用体验,支持liboptiling.so和kernel .o自动匹配 #73
gong-siwei创建于 16 天前
[feature] 提升msKL tiling/kernel快速调用接口使用体验,支持liboptiling.so和kernel .o自动匹配 #73
已合并
共 9 个文件变更+987-76
| @@ -65,14 +65,15 @@ The APIs provided by the msKL tool can call the tiling function in the msOpGen p | |||
| 65 | 65 | ||
| 66 | Calls the user's tiling function. | 66 | Calls the user's tiling function. |
| 67 | 67 | ||
| 68 | -> [!NOTE] Note | 68 | +> [!NOTE] Note |
| 69 | > tiling_func does not support calling the GetCompileInfo interface in [Basic Data Structure and Interface Reference](https://www.hiascend.com/document/detail/en/canncommercial/83RC1/API/basicdataapi/atlasopapi_07_00001.html). | 69 | > tiling_func does not support calling the GetCompileInfo interface in [Basic Data Structure and Interface Reference](https://www.hiascend.com/document/detail/en/canncommercial/83RC1/API/basicdataapi/atlasopapi_07_00001.html). |
| 70 | 70 | ||
| 71 | **Prototype** | 71 | **Prototype** |
| 72 | 72 | ||
| 73 | ```python | 73 | ```python |
| 74 | -def tiling_func(op_type: str, inputs: list, outputs: list, lib_path: str, | 74 | +def tiling_func(op_type: str, inputs: list = None, outputs: list = None, lib_path: str = None, |
| 75 | - inputs_info: list = None, outputs_info: list = None, attr=None, soc_version: str = None) -> TilingOutput | 75 | + inputs_info: list = None, outputs_info: list = None, attr=None, |
| 76 | + soc_version: str = None, workspace: str = None) -> TilingOutput | ||
| 76 | ``` | 77 | ``` |
| 77 | 78 | ||
| 78 | **Parameters** | 79 | **Parameters** |
| @@ -201,10 +202,20 @@ def tiling_func(op_type: str, inputs: list, outputs: list, lib_path: str, | |||
| 201 | </td> | 202 | </td> |
| 202 | <td class="cellrowborder" valign="top" width="7.5200000000000005%" headers="mcps1.1.5.1.3 "><p id="p14829330143515"><a name="p14829330143515"></a><a name="p14829330143515"></a>Optional parameter.</p> | 203 | <td class="cellrowborder" valign="top" width="7.5200000000000005%" headers="mcps1.1.5.1.3 "><p id="p14829330143515"><a name="p14829330143515"></a><a name="p14829330143515"></a>Optional parameter.</p> |
| 203 | </td> | 204 | </td> |
| 204 | -<td class="cellrowborder" valign="top" width="65.27%" headers="mcps1.1.5.1.4 "><p id="p16858115495219"><a name="p16858115495219"></a><a name="p16858115495219"></a>Path to the liboptiling.so file generated by compiling the msOpGen project. It can be found in the project directory using <strong id="b8137203510342"><a name="b8137203510342"></a><a name="b8137203510342"></a>find . -name 'liboptiling.so'</strong>. The msKL tool retrieves the user's tiling function in the order of deployed operators and then searches for <strong id="b1026950296"><a name="b1026950296"></a><a name="b1026950296"></a>.so</strong> files.</p> | 205 | +<td class="cellrowborder" valign="top" width="65.27%" headers="mcps1.1.5.1.4 "><p id="p16858115495219"><a name="p16858115495219"></a><a name="p16858115495219"></a>Path to the liboptiling.so file generated by compiling the msOpGen project. If this parameter is not specified, the msKL tool automatically searches for the liboptiling.so file recursively under the <strong id="b1026950296"><a name="b1026950296"></a><a name="b1026950296"></a>workspace</strong> directory (if multiple files are found, the package under the <strong id="b8137203510342"><a name="b8137203510342"></a><a name="b8137203510342"></a>_CPack_Packages</strong> or <strong id="b8137203510343"><a name="b8137203510343"></a><a name="b8137203510343"></a>op_tiling</strong> directory is preferred). If no such file is found, it falls back to the operators deployed in the CANN environment. If this parameter is explicitly configured, the path specified by this parameter is used and automatic search is skipped.</p> |
| 205 | <p id="p2026182761114"><a name="p2026182761114"></a><a name="p2026182761114"></a>Data Type: str.</p> | 206 | <p id="p2026182761114"><a name="p2026182761114"></a><a name="p2026182761114"></a>Data Type: str.</p> |
| 206 | </td> | 207 | </td> |
| 207 | </tr> | 208 | </tr> |
| 209 | +<tr id="row198901511"><td class="cellrowborder" valign="top" width="17.16%" headers="mcps1.1.5.1.1 "><p id="p198901511"><a name="p198901511"></a><a name="p198901511"></a>workspace</p> | ||
| 210 | +</td> | ||
| 211 | +<td class="cellrowborder" valign="top" width="10.05%" headers="mcps1.1.5.1.2 "><p id="p198901512"><a name="p198901512"></a><a name="p198901512"></a>Input</p> | ||
| 212 | +</td> | ||
| 213 | +<td class="cellrowborder" valign="top" width="7.5200000000000005%" headers="mcps1.1.5.1.3 "><p id="p198901513"><a name="p198901513"></a><a name="p198901513"></a>Optional parameter.</p> | ||
| 214 | +</td> | ||
| 215 | +<td class="cellrowborder" valign="top" width="65.27%" headers="mcps1.1.5.1.4 "><p id="p198901514"><a name="p198901514"></a><a name="p198901514"></a>Root directory of the operator project, for example, the CustomOp directory of an msOpGen project. If this parameter is not specified, the current directory is used by default. The msKL tool automatically searches for the liboptiling.so file and the kernel .o file recursively under this directory (the <strong id="b198901515"><a name="b198901515"></a><a name="b198901515"></a>liboptiling.so</strong> file and the kernel .o file whose path contains the <strong id="b198901516"><a name="b198901516"></a><a name="b198901516"></a>kernel</strong> directory) for use by tiling_func and get_kernel_from_binary.</p> | ||
| 216 | +<p id="p198901516"><a name="p198901516"></a><a name="p198901516"></a>Data Type: str.</p> | ||
| 217 | +</td> | ||
| 218 | +</tr> | ||
| 208 | <tr id="row331912112322"><td class="cellrowborder" valign="top" width="17.16%" headers="mcps1.1.5.1.1 "><p id="p18319161123219"><a name="p18319161123219"></a><a name="p18319161123219"></a>soc_version</p> | 219 | <tr id="row331912112322"><td class="cellrowborder" valign="top" width="17.16%" headers="mcps1.1.5.1.1 "><p id="p18319161123219"><a name="p18319161123219"></a><a name="p18319161123219"></a>soc_version</p> |
| 209 | </td> | 220 | </td> |
| 210 | <td class="cellrowborder" valign="top" width="10.05%" headers="mcps1.1.5.1.2 "><p id="p0700152183213"><a name="p0700152183213"></a><a name="p0700152183213"></a>Input</p> | 221 | <td class="cellrowborder" valign="top" width="10.05%" headers="mcps1.1.5.1.2 "><p id="p0700152183213"><a name="p0700152183213"></a><a name="p0700152183213"></a>Input</p> |
| @@ -278,7 +289,7 @@ output = np.zeros([M, N]).astype(np.float32) | |||
| 278 | tiling_output = mskl.tiling_func( | 289 | tiling_output = mskl.tiling_func( |
| 279 | op_type="MatmulLeakyreluCustom", | 290 | op_type="MatmulLeakyreluCustom", |
| 280 | inputs=[input_a, input_b, input_bias], outputs=[output], | 291 | inputs=[input_a, input_b, input_bias], outputs=[output], |
| 281 | - lib_path="liboptiling.so", # Tiling code compilation artifact. | 292 | + lib_path="liboptiling.so", # Tiling code compilation artifact. |
| 282 | ) | 293 | ) |
| 283 | ``` | 294 | ``` |
| 284 | 295 | ||
| @@ -291,7 +302,7 @@ Generates an instance that can call the user's Kernel function. | |||
| 291 | **Prototype** | 302 | **Prototype** |
| 292 | 303 | ||
| 293 | ```python | 304 | ```python |
| 294 | -def get_kernel_from_binary(kernel_binary_file: str, kernel_type: str = None, tiling_key: int = None) -> CompiledKernel | 305 | +def get_kernel_from_binary(kernel_binary_file: str = None, kernel_type: str = None, tiling_key: int = None) -> CompiledKernel |
| 295 | ``` | 306 | ``` |
| 296 | 307 | ||
| 297 | **Parameters** | 308 | **Parameters** |
| @@ -311,9 +322,9 @@ def get_kernel_from_binary(kernel_binary_file: str, kernel_type: str = None, til | |||
| 311 | </td> | 322 | </td> |
| 312 | <td class="cellrowborder" valign="top" width="10.07%" headers="mcps1.1.5.1.2 "><p id="p1239865692418"><a name="p1239865692418"></a><a name="p1239865692418"></a>Input</p> | 323 | <td class="cellrowborder" valign="top" width="10.07%" headers="mcps1.1.5.1.2 "><p id="p1239865692418"><a name="p1239865692418"></a><a name="p1239865692418"></a>Input</p> |
| 313 | </td> | 324 | </td> |
| 314 | -<td class="cellrowborder" valign="top" width="7.5200000000000005%" headers="mcps1.1.5.1.3 "><p id="p224816370358"><a name="p224816370358"></a><a name="p224816370358"></a>Required parameter.</p> | 325 | +<td class="cellrowborder" valign="top" width="7.5200000000000005%" headers="mcps1.1.5.1.3 "><p id="p224816370358"><a name="p224816370358"></a><a name="p224816370358"></a>Optional parameter.</p> |
| 315 | </td> | 326 | </td> |
| 316 | -<td class="cellrowborder" valign="top" width="65.25%" headers="mcps1.1.5.1.4 "><p id="p858655320395"><a name="p858655320395"></a><a name="p858655320395"></a>Path to the operator's kernel.o file. It can be found by executing the <strong id="b23211229105614"><a name="b23211229105614"></a><a name="b23211229105614"></a>find . -name '*.o'</strong> command in the project directory.</p> | 327 | +<td class="cellrowborder" valign="top" width="65.25%" headers="mcps1.1.5.1.4 "><p id="p858655320395"><a name="p858655320395"></a><a name="p858655320395"></a>Path to the operator's kernel.o file. If this parameter is not specified, the msKL tool automatically searches for <strong id="b23211229105614"><a name="b23211229105614"></a><a name="b23211229105614"></a>*.o</strong> files whose path contains the <strong id="b23211229105616"><a name="b23211229105616"></a><a name="b23211229105616"></a>kernel</strong> directory recursively under the tiling_func <strong id="b23211229105617"><a name="b23211229105617"></a><a name="b23211229105617"></a>workspace</strong> directory. If multiple .o files are found, the target kernel .o file is automatically selected based on <strong id="b23211229105618"><a name="b23211229105618"></a><a name="b23211229105618"></a>op_type → device soc → the same-directory JSON (dtype/format/shape)</strong>.</p> |
| 317 | <p id="p960565114599"><a name="p960565114599"></a><a name="p960565114599"></a>Data Type: str.</p> | 328 | <p id="p960565114599"><a name="p960565114599"></a><a name="p960565114599"></a>Data Type: str.</p> |
| 318 | </td> | 329 | </td> |
| 319 | </tr> | 330 | </tr> |
| @@ -399,7 +410,7 @@ An executable Kernel object. | |||
| 399 | </tbody> | 410 | </tbody> |
| 400 | </table> | 411 | </table> |
| 401 | 412 | ||
| 402 | -> [!NOTE] | 413 | +> [!NOTE] |
| 403 | > The Kernel object type is CompiledKernel, which supports invoking the Kernel as follows: kernel[blockdim](arg1, arg2, ..., timeout=-1, device_id=0, repeat=1). During actual invocation, ensure that the input arguments of the CompiledKernel function are consistent with those when invoking the Kernel. | 414 | > The Kernel object type is CompiledKernel, which supports invoking the Kernel as follows: kernel[blockdim](arg1, arg2, ..., timeout=-1, device_id=0, repeat=1). During actual invocation, ensure that the input arguments of the CompiledKernel function are consistent with those when invoking the Kernel. |
| 404 | 415 | ||
| 405 | **Sample** | 416 | **Sample** |
| @@ -557,8 +568,8 @@ The File Path of the generated code. | |||
| 557 | **Sample** | 568 | **Sample** |
| 558 | 569 | ||
| 559 | ```py | 570 | ```py |
| 560 | -config = mskl.KernelInvokeConfig(kernel_file, kernel_name) | 571 | +config = mskl.KernelInvokeConfig(kernel_file, kernel_name) |
| 561 | -gen_file = mskl.Launcher(config).code_gen() | 572 | +gen_file = mskl.Launcher(config).code_gen() |
| 562 | ``` | 573 | ``` |
| 563 | 574 | ||
| 564 | **Related Class/Structure Definitions** | 575 | **Related Class/Structure Definitions** |
| @@ -572,7 +583,7 @@ class KernelInvokeConfig: | |||
| 572 | pass | 583 | pass |
| 573 | # The user can only pass a parameter of type KernelInvokeConfig. | 584 | # The user can only pass a parameter of type KernelInvokeConfig. |
| 574 | class Launcher: | 585 | class Launcher: |
| 575 | - def __init__(self, config: KernelInvokeConfig): | 586 | + def __init__(self, config: KernelInvokeConfig): |
| 576 | ... | 587 | ... |
| 577 | a class that generates launch source code for a kernel | 588 | a class that generates launch source code for a kernel |
| 578 | 589 | ||
| @@ -65,14 +65,15 @@ msKL工具提供的接口可以调用msOpGen工程中的tiling函数以及用户 | |||
| 65 | 65 | ||
| 66 | 调用用户的tiling函数。 | 66 | 调用用户的tiling函数。 |
| 67 | 67 | ||
| 68 | -> [!NOTE] | 68 | +> [!NOTE] |
| 69 | > tiling_func不支持调用《[基础数据结构和接口参考](https://www.hiascend.com/document/detail/zh/canncommercial/83RC1/API/basicdataapi/atlasopapi_07_00001.html)》中的GetCompileInfo接口。 | 69 | > tiling_func不支持调用《[基础数据结构和接口参考](https://www.hiascend.com/document/detail/zh/canncommercial/83RC1/API/basicdataapi/atlasopapi_07_00001.html)》中的GetCompileInfo接口。 |
| 70 | 70 | ||
| 71 | **函数原型** | 71 | **函数原型** |
| 72 | 72 | ||
| 73 | ```python | 73 | ```python |
| 74 | -def tiling_func(op_type: str, inputs: list, outputs: list, lib_path: str, | 74 | +def tiling_func(op_type: str, inputs: list = None, outputs: list = None, lib_path: str = None, |
| 75 | - inputs_info: list = None, outputs_info: list = None, attr=None, soc_version: str = None) -> TilingOutput | 75 | + inputs_info: list = None, outputs_info: list = None, attr=None, |
| 76 | + soc_version: str = None, workspace: str = None) -> TilingOutput | ||
| 76 | ``` | 77 | ``` |
| 77 | 78 | ||
| 78 | **参数说明** | 79 | **参数说明** |
| @@ -201,10 +202,20 @@ def tiling_func(op_type: str, inputs: list, outputs: list, lib_path: str, | |||
| 201 | </td> | 202 | </td> |
| 202 | <td class="cellrowborder" valign="top" width="7.5200000000000005%" headers="mcps1.1.5.1.3 "><p id="p14829330143515"><a name="p14829330143515"></a><a name="p14829330143515"></a>可选参数。</p> | 203 | <td class="cellrowborder" valign="top" width="7.5200000000000005%" headers="mcps1.1.5.1.3 "><p id="p14829330143515"><a name="p14829330143515"></a><a name="p14829330143515"></a>可选参数。</p> |
| 203 | </td> | 204 | </td> |
| 204 | -<td class="cellrowborder" valign="top" width="65.27%" headers="mcps1.1.5.1.4 "><p id="p16858115495219"><a name="p16858115495219"></a><a name="p16858115495219"></a>msOpGen工程编译生成的liboptiling.so文件的路径,可在工程目录下通过<strong id="b8137203510342"><a name="b8137203510342"></a><a name="b8137203510342"></a>find . -name 'liboptiling.so'</strong>进行查找。msKL工具会按已部署算子、<strong id="b1026950296"><a name="b1026950296"></a><a name="b1026950296"></a>.so</strong>文件的查找顺序获取用户tiling函数。</p> | 205 | +<td class="cellrowborder" valign="top" width="65.27%" headers="mcps1.1.5.1.4 "><p id="p16858115495219"><a name="p16858115495219"></a><a name="p16858115495219"></a>msOpGen工程编译生成的liboptiling.so文件的路径。若不配置该参数,msKL工具会自动在<strong id="b8137203510342"><a name="b8137203510342"></a><a name="b8137203510342"></a>workspace</strong>目录下递归搜索liboptiling.so文件(存在多个时优先选择<strong id="b1026950296"><a name="b1026950296"></a><a name="b1026950296"></a>_CPack_Packages</strong>或<strong id="b1026950297"><a name="b1026950297"></a><a name="b1026950297"></a>op_tiling</strong>目录下的部署包);若未搜索到,再回退使用已部署在CANN环境中的算子。若显式配置该参数,则以该参数指定的路径为准,不再执行自动搜索。</p> |
| 205 | <p id="p2026182761114"><a name="p2026182761114"></a><a name="p2026182761114"></a>数据类型:str。</p> | 206 | <p id="p2026182761114"><a name="p2026182761114"></a><a name="p2026182761114"></a>数据类型:str。</p> |
| 206 | </td> | 207 | </td> |
| 207 | </tr> | 208 | </tr> |
| 209 | +<tr id="row198901511"><td class="cellrowborder" valign="top" width="17.16%" headers="mcps1.1.5.1.1 "><p id="p198901511"><a name="p198901511"></a><a name="p198901511"></a>workspace</p> | ||
| 210 | +</td> | ||
| 211 | +<td class="cellrowborder" valign="top" width="10.05%" headers="mcps1.1.5.1.2 "><p id="p198901512"><a name="p198901512"></a><a name="p198901512"></a>输入</p> | ||
| 212 | +</td> | ||
| 213 | +<td class="cellrowborder" valign="top" width="7.5200000000000005%" headers="mcps1.1.5.1.3 "><p id="p198901513"><a name="p198901513"></a><a name="p198901513"></a>可选参数。</p> | ||
| 214 | +</td> | ||
| 215 | +<td class="cellrowborder" valign="top" width="65.27%" headers="mcps1.1.5.1.4 "><p id="p198901514"><a name="p198901514"></a><a name="p198901514"></a>算子工程根目录路径,例如msOpGen工程的CustomOp目录。若不配置该参数,默认使用当前目录。msKL工具会在该目录下递归搜索<strong id="b198901515"><a name="b198901515"></a><a name="b198901515"></a>liboptiling.so</strong>文件和路径包含<strong id="b198901516"><a name="b198901516"></a><a name="b198901516"></a>kernel</strong>目录的kernel .o文件,供tiling_func和get_kernel_from_binary使用。</p> | ||
| 216 | +<p id="p198901516"><a name="p198901516"></a><a name="p198901516"></a>数据类型:str。</p> | ||
| 217 | +</td> | ||
| 218 | +</tr> | ||
| 208 | <tr id="row331912112322"><td class="cellrowborder" valign="top" width="17.16%" headers="mcps1.1.5.1.1 "><p id="p18319161123219"><a name="p18319161123219"></a><a name="p18319161123219"></a>soc_version</p> | 219 | <tr id="row331912112322"><td class="cellrowborder" valign="top" width="17.16%" headers="mcps1.1.5.1.1 "><p id="p18319161123219"><a name="p18319161123219"></a><a name="p18319161123219"></a>soc_version</p> |
| 209 | </td> | 220 | </td> |
| 210 | <td class="cellrowborder" valign="top" width="10.05%" headers="mcps1.1.5.1.2 "><p id="p0700152183213"><a name="p0700152183213"></a><a name="p0700152183213"></a>输入</p> | 221 | <td class="cellrowborder" valign="top" width="10.05%" headers="mcps1.1.5.1.2 "><p id="p0700152183213"><a name="p0700152183213"></a><a name="p0700152183213"></a>输入</p> |
| @@ -277,7 +288,7 @@ output = np.zeros([M, N]).astype(np.float32) | |||
| 277 | tiling_output = mskl.tiling_func( | 288 | tiling_output = mskl.tiling_func( |
| 278 | op_type="MatmulLeakyreluCustom", | 289 | op_type="MatmulLeakyreluCustom", |
| 279 | inputs=[input_a, input_b, input_bias], outputs=[output], | 290 | inputs=[input_a, input_b, input_bias], outputs=[output], |
| 280 | - lib_path="liboptiling.so", # tiling函数编译产物 | 291 | + lib_path="liboptiling.so", # tiling函数编译产物 |
| 281 | ) | 292 | ) |
| 282 | ``` | 293 | ``` |
| 283 | 294 | ||
| @@ -290,7 +301,7 @@ tiling_output = mskl.tiling_func( | |||
| 290 | **函数原型** | 301 | **函数原型** |
| 291 | 302 | ||
| 292 | ```python | 303 | ```python |
| 293 | -def get_kernel_from_binary(kernel_binary_file: str, kernel_type: str = None, tiling_key: int = None) -> CompiledKernel | 304 | +def get_kernel_from_binary(kernel_binary_file: str = None, kernel_type: str = None, tiling_key: int = None) -> CompiledKernel |
| 294 | ``` | 305 | ``` |
| 295 | 306 | ||
| 296 | **参数说明** | 307 | **参数说明** |
| @@ -310,9 +321,9 @@ def get_kernel_from_binary(kernel_binary_file: str, kernel_type: str = None, til | |||
| 310 | </td> | 321 | </td> |
| 311 | <td class="cellrowborder" valign="top" width="10.07%" headers="mcps1.1.5.1.2 "><p id="p1239865692418"><a name="p1239865692418"></a><a name="p1239865692418"></a>输入</p> | 322 | <td class="cellrowborder" valign="top" width="10.07%" headers="mcps1.1.5.1.2 "><p id="p1239865692418"><a name="p1239865692418"></a><a name="p1239865692418"></a>输入</p> |
| 312 | </td> | 323 | </td> |
| 313 | -<td class="cellrowborder" valign="top" width="7.5200000000000005%" headers="mcps1.1.5.1.3 "><p id="p224816370358"><a name="p224816370358"></a><a name="p224816370358"></a>必选参数。</p> | 324 | +<td class="cellrowborder" valign="top" width="7.5200000000000005%" headers="mcps1.1.5.1.3 "><p id="p224816370358"><a name="p224816370358"></a><a name="p224816370358"></a>可选参数。</p> |
| 314 | </td> | 325 | </td> |
| 315 | -<td class="cellrowborder" valign="top" width="65.25%" headers="mcps1.1.5.1.4 "><p id="p858655320395"><a name="p858655320395"></a><a name="p858655320395"></a>算子kernel.o路径,可以在工程目录下执行<strong id="b23211229105614"><a name="b23211229105614"></a><a name="b23211229105614"></a>find . -name '*.o'</strong>命令进行查找。</p> | 326 | +<td class="cellrowborder" valign="top" width="65.25%" headers="mcps1.1.5.1.4 "><p id="p858655320395"><a name="p858655320395"></a><a name="p858655320395"></a>算子kernel.o路径。若不配置该参数,msKL工具会自动在tiling_func的<strong id="b23211229105614"><a name="b23211229105614"></a><a name="b23211229105614"></a>workspace</strong>目录下递归搜索路径包含<strong id="b23211229105616"><a name="b23211229105616"></a><a name="b23211229105616"></a>kernel</strong>目录的<strong id="b23211229105617"><a name="b23211229105617"></a><a name="b23211229105617"></a>*.o</strong>文件;若搜索到多个,则按<strong id="b23211229105618"><a name="b23211229105618"></a><a name="b23211229105618"></a>算子名(op_type) → 设备soc → 同目录json(dtype/format/shape)</strong>自动选取目标kernel .o文件。</p> |
| 316 | <p id="p960565114599"><a name="p960565114599"></a><a name="p960565114599"></a>数据类型:str。</p> | 327 | <p id="p960565114599"><a name="p960565114599"></a><a name="p960565114599"></a>数据类型:str。</p> |
| 317 | </td> | 328 | </td> |
| 318 | </tr> | 329 | </tr> |
| @@ -399,7 +410,7 @@ def get_kernel_from_binary(kernel_binary_file: str, kernel_type: str = None, til | |||
| 399 | </table> | 410 | </table> |
| 400 | 411 | ||
| 401 | > [!NOTE] | 412 | > [!NOTE] |
| 402 | -> | 413 | +> |
| 403 | > Kernel对象类型为CompiledKernel,支持如下方式调用Kernel:kernel[blockdim](arg1, arg2, ..., timeout=-1, device_id=0, repeat=1),实际调用时,需保证CompiledKernel函数的入参和调用Kernel时的入参一致。 | 414 | > Kernel对象类型为CompiledKernel,支持如下方式调用Kernel:kernel[blockdim](arg1, arg2, ..., timeout=-1, device_id=0, repeat=1),实际调用时,需保证CompiledKernel函数的入参和调用Kernel时的入参一致。 |
| 404 | 415 | ||
| 405 | **调用示例** | 416 | **调用示例** |
| @@ -557,8 +568,8 @@ def code_gen(self, gen_file) | |||
| 557 | **调用示例** | 568 | **调用示例** |
| 558 | 569 | ||
| 559 | ```py | 570 | ```py |
| 560 | -config = mskl.KernelInvokeConfig(kernel_file, kernel_name) | 571 | +config = mskl.KernelInvokeConfig(kernel_file, kernel_name) |
| 561 | -mskl.Launcher(config).code_gen(gen_file) | 572 | +mskl.Launcher(config).code_gen(gen_file) |
| 562 | ``` | 573 | ``` |
| 563 | 574 | ||
| 564 | **相关类/结构体定义** | 575 | **相关类/结构体定义** |
| @@ -572,7 +583,7 @@ class KernelInvokeConfig: | |||
| 572 | pass | 583 | pass |
| 573 | # 用户仅能传KernelInvokeConfig类型 | 584 | # 用户仅能传KernelInvokeConfig类型 |
| 574 | class Launcher: | 585 | class Launcher: |
| 575 | - def __init__(self, config: KernelInvokeConfig): | 586 | + def __init__(self, config: KernelInvokeConfig): |
| 576 | ... | 587 | ... |
| 577 | a class that generates launch source code for a kernel | 588 | a class that generates launch source code for a kernel |
| 578 | 589 | ||
| @@ -23,7 +23,7 @@ import numpy as np | |||
| 23 | from ..utils import safe_check, logger | 23 | from ..utils import safe_check, logger |
| 24 | from ..utils.safe_check import FileChecker | 24 | from ..utils.safe_check import FileChecker |
| 25 | from .context import context | 25 | from .context import context |
| 26 | -from ..utils.launcher_utils import get_cann_path | 26 | +from ..utils.launcher_utils import get_cann_path, search_tiling_so, get_workspace_path |
| 27 | from ..utils.autotune_utils import ( | 27 | from ..utils.autotune_utils import ( |
| 28 | is_torch_tensor_instance, | 28 | is_torch_tensor_instance, |
| 29 | safe_prod, | 29 | safe_prod, |
| @@ -217,6 +217,7 @@ class TilingConfig: | |||
| 217 | outputs_info: list = None, | 217 | outputs_info: list = None, |
| 218 | attr=None, | 218 | attr=None, |
| 219 | soc_version: str = None, | 219 | soc_version: str = None, |
| 220 | + workspace: str = None, | ||
| 220 | ): | 221 | ): |
| 221 | # op_type 做透传处理 | 222 | # op_type 做透传处理 |
| 222 | if not self._is_valid_key_str(op_type): | 223 | if not self._is_valid_key_str(op_type): |
| @@ -224,7 +225,7 @@ class TilingConfig: | |||
| 224 | self.op_type = op_type | 225 | self.op_type = op_type |
| 225 | self._parse_io_params(inputs, outputs, inputs_info, outputs_info) | 226 | self._parse_io_params(inputs, outputs, inputs_info, outputs_info) |
| 226 | self._parse_attr(attr) | 227 | self._parse_attr(attr) |
| 227 | - self._parse_lib_path(lib_path) | 228 | + self._parse_lib_path(lib_path, workspace) |
| 228 | self._parse_soc_version(soc_version) | 229 | self._parse_soc_version(soc_version) |
| 229 | 230 | ||
| 230 | 231 | ||
| @@ -361,9 +362,14 @@ class TilingConfig: | |||
| 361 | elif (t is not None) and (not is_torch_or_numpy_tensor(t)): | 362 | elif (t is not None) and (not is_torch_or_numpy_tensor(t)): |
| 362 | raise ValueError(f'Type of {para}[{idx}] should be torch.Tensor, numpy.ndarray or the list of above') | 363 | raise ValueError(f'Type of {para}[{idx}] should be torch.Tensor, numpy.ndarray or the list of above') |
| 363 | 364 | ||
| 364 | - def _parse_lib_path(self, lib_path: str): | 365 | + def _parse_lib_path(self, lib_path: str, workspace: str = None): |
| 365 | if lib_path is None: | 366 | if lib_path is None: |
| 366 | - # load liboptiling.so in cann as default | 367 | + # 未显式传入lib_path时,在整个workspace目录下递归搜索liboptiling.so |
| 368 | + # (存在多个时优先打包产物_CPack_Packages/op_tiling下的部署包),搜索失败则回退到cann默认的liboptiling.so | ||
| 369 | + tiling_so = search_tiling_so(get_workspace_path(workspace)) | ||
| 370 | + if tiling_so is not None: | ||
| 371 | + self.lib_path = _escape_cpp_string(os.path.realpath(tiling_so)) | ||
| 372 | + return | ||
| 367 | cann_path = get_cann_path() | 373 | cann_path = get_cann_path() |
| 368 | tiling_so = os.path.join(cann_path, "lib64/liboptiling.so") | 374 | tiling_so = os.path.join(cann_path, "lib64/liboptiling.so") |
| 369 | self.lib_path = _escape_cpp_string(tiling_so) if os.path.exists(tiling_so) else '' | 375 | self.lib_path = _escape_cpp_string(tiling_so) if os.path.exists(tiling_so) else '' |
| @@ -688,7 +694,7 @@ class TilingConfig: | |||
| 688 | checker = FileChecker(data_path, 'file') | 694 | checker = FileChecker(data_path, 'file') |
| 689 | if not checker.check_input_file(): | 695 | if not checker.check_input_file(): |
| 690 | raise PermissionError(f'{param_name}["data_path"] check permission failed') | 696 | raise PermissionError(f'{param_name}["data_path"] check permission failed') |
| 691 | - info['data_path'] = repr(os.path.abspath(data_path))[1:-1] | 697 | + info['data_path'] = _escape_cpp_string(os.path.abspath(data_path)) |
| 692 | 698 | ||
| 693 | ch = ChainHandler( | 699 | ch = ChainHandler( |
| 694 | [verify_fmt, verify_ori_format, verify_shape, verify_ori_shape, verify_dtype, verify_data_path] | 700 | [verify_fmt, verify_ori_format, verify_shape, verify_ori_shape, verify_dtype, verify_data_path] |
| @@ -742,14 +748,9 @@ class TilingConfig: | |||
| 742 | ) | 748 | ) |
| 743 | last_param = '' | 749 | last_param = '' |
| 744 | if tensor['data_path'] != '': | 750 | if tensor['data_path'] != '': |
| 745 | - escaped_path = ( | 751 | + # data_path 已由 _escape_cpp_string 统一完成转义(反斜杠/双引号/换行/制表符等), |
| 746 | - tensor['data_path'] | 752 | + # 直接嵌入C++字符串字面量,避免二次转义导致路径错误 |
| 747 | - .replace('\\', '\\\\') | 753 | + escaped_path = tensor['data_path'] |
| 748 | - .replace('"', '\\"') | ||
| 749 | - .replace('\n', '\\n') | ||
| 750 | - .replace('\t', '\\t') | ||
| 751 | - .replace('\r', '\\r') | ||
| 752 | - ) | ||
| 753 | last_param = f", string{{\"{escaped_path}\"}}" | 754 | last_param = f", string{{\"{escaped_path}\"}}" |
| 754 | elif tensor['dtype'] in supported_types and tensor['addr'] != 0: | 755 | elif tensor['dtype'] in supported_types and tensor['addr'] != 0: |
| 755 | if not isinstance(tensor['addr'], int) or tensor['addr'] < 0: | 756 | if not isinstance(tensor['addr'], int) or tensor['addr'] < 0: |
| @@ -16,6 +16,7 @@ | |||
| 16 | # See the Mulan PSL v2 for more details. | 16 | # See the Mulan PSL v2 for more details. |
| 17 | # ------------------------------------------------------------------------- | 17 | # ------------------------------------------------------------------------- |
| 18 | 18 | ||
| 19 | + | ||
| 19 | class Context: | 20 | class Context: |
| 20 | """ | 21 | """ |
| 21 | Maintain context of modules(code_generator, compile, etc.) | 22 | Maintain context of modules(code_generator, compile, etc.) |
| @@ -28,8 +29,10 @@ class Context: | |||
| 28 | self._launch_src_file = None | 29 | self._launch_src_file = None |
| 29 | self._build_script = None | 30 | self._build_script = None |
| 30 | self._blockdim = None | 31 | self._blockdim = None |
| 31 | - self._tiling_output = None # mskl.launcher.opgen_workflow.TilingOutput | 32 | + self._tiling_output = None |
| 32 | - self._op_type = None # str like AddCustom | 33 | + self._op_type = None |
| 34 | + self._workspace = None | ||
| 35 | + self._io_info = None | ||
| 33 | self._autotune_in_progress = False | 36 | self._autotune_in_progress = False |
| 34 | self._prelaunch_flag = False | 37 | self._prelaunch_flag = False |
| 35 | 38 | ||
| @@ -142,8 +145,40 @@ class Context: | |||
| 142 | def op_type(self, value): | 145 | def op_type(self, value): |
| 143 | self._op_type = value | 146 | self._op_type = value |
| 144 | 147 | ||
| 148 | + | ||
| 149 | + def workspace(self): | ||
| 150 | + return self._workspace | ||
| 151 | + | ||
| 152 | + | ||
| 153 | + def workspace(self, value): | ||
| 154 | + self._workspace = value | ||
| 155 | + | ||
| 156 | + | ||
| 157 | + def io_info(self): | ||
| 158 | + # {'inputs': [{'dtype','format','shape'},...], 'outputs': [...]},用于kernel .o自动匹配 | ||
| 159 | + return self._io_info | ||
| 160 | + | ||
| 161 | + | ||
| 162 | + def io_info(self, value): | ||
| 163 | + self._io_info = value | ||
| 164 | + | ||
| 145 | def reset(self): | 165 | def reset(self): |
| 146 | - self.__init__() | 166 | + # 重置实例状态,避免直接调用 __init__(pylint C2801) |
| 167 | + self._config = None | ||
| 168 | + self._kernel_name = None | ||
| 169 | + self._kernel_src_file = None | ||
| 170 | + self._launch_src_file = None | ||
| 171 | + self._build_script = None | ||
| 172 | + self._blockdim = None | ||
| 173 | + self._tiling_output = None | ||
| 174 | + self._op_type = None | ||
| 175 | + self._workspace = None | ||
| 176 | + self._io_info = None | ||
| 177 | + self._autotune_in_progress = False | ||
| 178 | + self._prelaunch_flag = False | ||
| 179 | + self._decl_args = None | ||
| 180 | + self._template_args = None | ||
| 181 | + self._kernel_args = None | ||
| 147 | 182 | ||
| 148 | 183 | ||
| 149 | -context = Context() | 184 | +context = Context() |
| @@ -23,6 +23,14 @@ from .code_generator import Launcher | |||
| 23 | from .compiler import compile_tiling, compile_kernel_binary, CompiledKernel | 23 | from .compiler import compile_tiling, compile_kernel_binary, CompiledKernel |
| 24 | from .context import context | 24 | from .context import context |
| 25 | from ..utils.safe_check import FileChecker, DATA_DIRECTORY_AUTHORITY | 25 | from ..utils.safe_check import FileChecker, DATA_DIRECTORY_AUTHORITY |
| 26 | +from ..utils.launcher_utils import ( | ||
| 27 | + get_workspace_path, | ||
| 28 | + search_kernel_binary_files, | ||
| 29 | + match_kernel_binary_by_op_type, | ||
| 30 | + match_kernel_binary_by_soc, | ||
| 31 | + match_kernel_binary_by_json, | ||
| 32 | + get_device_soc_name, | ||
| 33 | +) | ||
| 26 | from ..utils import logger | 34 | from ..utils import logger |
| 27 | 35 | ||
| 28 | TILING_FUNC_CNT = 0 | 36 | TILING_FUNC_CNT = 0 |
| @@ -50,12 +58,84 @@ class TilingOutput: | |||
| 50 | self.tiling_key = tiling_output["tiling_key"] | 58 | self.tiling_key = tiling_output["tiling_key"] |
| 51 | 59 | ||
| 52 | 60 | ||
| 53 | -def tiling_func(op_type: str, inputs: list = None, outputs: list = None, lib_path: str = None, | 61 | +def _tensor_dtype(t) -> str: |
| 54 | - inputs_info: list = None, outputs_info: list = None, attr=None, | 62 | + """获取tensor的dtype字符串(float16/float32等),供kernel .o的json匹配使用。""" |
| 55 | - soc_version: str = None) -> TilingOutput: | 63 | + if t is None: |
| 64 | + return '' | ||
| 65 | + if isinstance(t, np.ndarray): | ||
| 66 | + return t.dtype.name | ||
| 67 | + if hasattr(t, 'dtype'): | ||
| 68 | + return str(t.dtype).replace('torch.', '') | ||
| 69 | + return '' | ||
| 70 | + | ||
| 71 | + | ||
| 72 | +def _build_io_info(inputs, outputs, inputs_info, outputs_info) -> dict: | ||
| 73 | + """从tiling_func入参提取inputs/outputs的dtype/format/shape信息,用于kernel .o自动匹配。""" | ||
| 74 | + | ||
| 75 | + def _io_of(t, info): | ||
| 76 | + if info is None: | ||
| 77 | + info = {} | ||
| 78 | + dtype = _tensor_dtype(t) or info.get('dtype', '') | ||
| 79 | + fmt = info.get('format', '') or 'ND' | ||
| 80 | + shape = list(t.shape) if (t is not None and hasattr(t, 'shape')) else list(info.get('shape', [])) | ||
| 81 | + return {'dtype': dtype, 'format': fmt, 'shape': shape} | ||
| 82 | + | ||
| 83 | + def _flatten(tensors, infos): | ||
| 84 | + tensors = list(tensors) if tensors else [] | ||
| 85 | + infos = list(infos) if infos else [] | ||
| 86 | + length = max(len(tensors), len(infos)) | ||
| 87 | + res = [] | ||
| 88 | + for i in range(length): | ||
| 89 | + t = tensors[i] if i < len(tensors) else None | ||
| 90 | + info = infos[i] if i < len(infos) else None | ||
| 91 | + if isinstance(t, list) or isinstance(info, list): | ||
| 92 | + t_list = t if isinstance(t, list) else [None] | ||
| 93 | + info_list = info if isinstance(info, list) else [info] | ||
| 94 | + for j in range(max(len(t_list), len(info_list))): | ||
| 95 | + res.append( | ||
| 96 | + _io_of(t_list[j] if j < len(t_list) else None, info_list[j] if j < len(info_list) else None) | ||
| 97 | + ) | ||
| 98 | + else: | ||
| 99 | + res.append(_io_of(t, info)) | ||
| 100 | + return res | ||
| 101 | + | ||
| 102 | + return { | ||
| 103 | + 'inputs': _flatten(inputs, inputs_info), | ||
| 104 | + 'outputs': _flatten(outputs, outputs_info), | ||
| 105 | + } | ||
| 106 | + | ||
| 107 | + | ||
| 108 | +def tiling_func( | ||
| 109 | + op_type: str, | ||
| 110 | + inputs: list = None, | ||
| 111 | + outputs: list = None, | ||
| 112 | + lib_path: str = None, | ||
| 113 | + inputs_info: list = None, | ||
| 114 | + outputs_info: list = None, | ||
| 115 | + attr=None, | ||
| 116 | + soc_version: str = None, | ||
| 117 | + workspace: str = None, | ||
| 118 | +) -> TilingOutput: | ||
| 119 | + """ | ||
| 120 | + :param op_type: op type, e.g. AddCustom | ||
| 121 | + :param inputs: tensors of kernel inputs | ||
| 122 | + :param outputs: tensors of kernel outputs | ||
| 123 | + :param lib_path: optional, path of liboptiling.so. If not set, it will be searched recursively in | ||
| 124 | + the <workspace> directory (the package under _CPack_Packages/op_tiling is preferred) | ||
| 125 | + :param inputs_info: info of kernel inputs | ||
| 126 | + :param outputs_info: info of kernel outputs | ||
| 127 | + :param attr: attrs of the op | ||
| 128 | + :param soc_version: soc version | ||
| 129 | + :param workspace: optional, root directory of the op project, default is current directory. | ||
| 130 | + Used to search liboptiling.so and kernel .o automatically | ||
| 131 | + :return: TilingOutput | ||
| 132 | + """ | ||
| 56 | global TILING_FUNC_CNT | 133 | global TILING_FUNC_CNT |
| 57 | TILING_FUNC_CNT += 1 | 134 | TILING_FUNC_CNT += 1 |
| 58 | - config = TilingConfig(op_type, inputs, outputs, lib_path, inputs_info, outputs_info, attr, soc_version) | 135 | + workspace = get_workspace_path(workspace) |
| 136 | + context.workspace = workspace | ||
| 137 | + context.io_info = _build_io_info(inputs, outputs, inputs_info, outputs_info) | ||
| 138 | + config = TilingConfig(op_type, inputs, outputs, lib_path, inputs_info, outputs_info, attr, soc_version, workspace) | ||
| 59 | tmp_path = init_tmp_folder() | 139 | tmp_path = init_tmp_folder() |
| 60 | cpp_path = os.path.join(tmp_path, f'_mskl_gen_tiling.{TILING_FUNC_CNT}.cpp') | 140 | cpp_path = os.path.join(tmp_path, f'_mskl_gen_tiling.{TILING_FUNC_CNT}.cpp') |
| 61 | Launcher(config).code_gen(cpp_path) | 141 | Launcher(config).code_gen(cpp_path) |
| @@ -71,15 +151,65 @@ def tiling_func(op_type: str, inputs: list = None, outputs: list = None, lib_pat | |||
| 71 | return output | 151 | return output |
| 72 | 152 | ||
| 73 | 153 | ||
| 74 | -def get_kernel_from_binary(kernel_binary_file: str, kernel_type: str = None, tiling_key: int = None) -> CompiledKernel: | 154 | +def _auto_search_kernel_binary() -> str: |
| 155 | + """根据tiling_func传入的workspace自动搜索kernel .o文件。 | ||
| 156 | + 选取顺序:算子名(op_type) -> 设备soc目录 -> 同目录json(dtype/format/shape)。 | ||
| 75 | """ | 157 | """ |
| 76 | - :param kernel_binary_file: path of kernel.o | 158 | + workspace = context.workspace if context.workspace is not None else os.getcwd() |
| 159 | + o_files = search_kernel_binary_files(workspace) | ||
| 160 | + if not o_files: | ||
| 161 | + raise FileNotFoundError( | ||
| 162 | + f'Cannot find any kernel .o file under {workspace}, please input [kernel_binary_file] or check [workspace]' | ||
| 163 | + ) | ||
| 164 | + # 1. 按算子名过滤:kernel .o 文件名以 <op_type>_ 开头 | ||
| 165 | + op_type = context.op_type | ||
| 166 | + if op_type: | ||
| 167 | + o_files = match_kernel_binary_by_op_type(o_files, op_type) | ||
| 168 | + if not o_files: | ||
| 169 | + raise FileNotFoundError( | ||
| 170 | + f'Cannot find kernel .o matching op_type {op_type} under {workspace}, ' | ||
| 171 | + f'please input [kernel_binary_file] or check [op_type]' | ||
| 172 | + ) | ||
| 173 | + if len(o_files) == 1: | ||
| 174 | + return o_files[0] | ||
| 175 | + # 2. 优先选择与当前设备soc匹配的kernel .o | ||
| 176 | + soc_files = match_kernel_binary_by_soc(o_files, get_device_soc_name()) | ||
| 177 | + candidates = soc_files if soc_files else o_files | ||
| 178 | + if len(candidates) == 1: | ||
| 179 | + return candidates[0] | ||
| 180 | + # 3. 根据kernel .o同目录json中的inputs/outputs信息(dtype/format/shape)区分 | ||
| 181 | + json_files = match_kernel_binary_by_json(candidates, context.io_info) | ||
| 182 | + if len(json_files) == 1: | ||
| 183 | + return json_files[0] | ||
| 184 | + if len(json_files) > 1: | ||
| 185 | + logger.warning( | ||
| 186 | + f'Multiple kernel .o files match op_type {op_type}, soc and io: ' | ||
| 187 | + f'{json_files}, use the first one {json_files[0]}' | ||
| 188 | + ) | ||
| 189 | + return json_files[0] | ||
| 190 | + logger.warning(f'Multiple kernel .o files match op_type {op_type}: {candidates}, use the first one {candidates[0]}') | ||
| 191 | + return candidates[0] | ||
| 192 | + | ||
| 193 | + | ||
| 194 | +def get_kernel_from_binary( | ||
| 195 | + kernel_binary_file: str = None, kernel_type: str = None, tiling_key: int = None | ||
| 196 | +) -> CompiledKernel: | ||
| 197 | + """ | ||
| 198 | + :param kernel_binary_file: optional, path of kernel.o. If not set, it will be searched recursively in | ||
| 199 | + the <workspace> directory and matched by op_type/soc/json | ||
| 77 | :param kernel_type: ['mix', 'cube', 'vec'] | 200 | :param kernel_type: ['mix', 'cube', 'vec'] |
| 78 | :param tiling_key: None will use tiling_func()'s return value | 201 | :param tiling_key: None will use tiling_func()'s return value |
| 79 | :return: CompiledKernel | 202 | :return: CompiledKernel |
| 80 | """ | 203 | """ |
| 81 | global GET_KERNEL_FROM_BINARY_CNT | 204 | global GET_KERNEL_FROM_BINARY_CNT |
| 82 | GET_KERNEL_FROM_BINARY_CNT += 1 | 205 | GET_KERNEL_FROM_BINARY_CNT += 1 |
| 206 | + if kernel_binary_file is None: | ||
| 207 | + if tiling_key is None: | ||
| 208 | + if context.tiling_output is None: | ||
| 209 | + raise Exception('Please call mskl.tiling_func or assign [tiling_key]') | ||
| 210 | + tiling_key = context.tiling_output.tiling_key | ||
| 211 | + kernel_binary_file = _auto_search_kernel_binary() | ||
| 212 | + logger.debug(f'Automatically searched kernel binary file is {kernel_binary_file}') | ||
| 83 | config = KernelBinaryInvokeConfig(kernel_binary_file, kernel_type, tiling_key) | 213 | config = KernelBinaryInvokeConfig(kernel_binary_file, kernel_type, tiling_key) |
| 84 | tmp_path = init_tmp_folder() | 214 | tmp_path = init_tmp_folder() |
| 85 | context.opgen_tmp_dir_path = tmp_path | 215 | context.opgen_tmp_dir_path = tmp_path |
| @@ -87,6 +217,7 @@ def get_kernel_from_binary(kernel_binary_file: str, kernel_type: str = None, til | |||
| 87 | Launcher(config).code_gen(cpp_path) | 217 | Launcher(config).code_gen(cpp_path) |
| 88 | so_path = os.path.join(tmp_path, f'_mskl_gen_binary_module.{GET_KERNEL_FROM_BINARY_CNT}.so') | 218 | so_path = os.path.join(tmp_path, f'_mskl_gen_binary_module.{GET_KERNEL_FROM_BINARY_CNT}.so') |
| 89 | kernel = compile_kernel_binary(cpp_path, so_path) | 219 | kernel = compile_kernel_binary(cpp_path, so_path) |
| 90 | - logger.debug(f'Call get_kernel_from_binary {GET_KERNEL_FROM_BINARY_CNT} success, ' | 220 | + logger.debug( |
| 91 | - f'kernel path is {kernel_binary_file}') | 221 | + f'Call get_kernel_from_binary {GET_KERNEL_FROM_BINARY_CNT} success, kernel path is {kernel_binary_file}' |
| 222 | + ) | ||
| 92 | return kernel | 223 | return kernel |
| @@ -16,9 +16,213 @@ | |||
| 16 | # See the Mulan PSL v2 for more details. | 16 | # See the Mulan PSL v2 for more details. |
| 17 | # ------------------------------------------------------------------------- | 17 | # ------------------------------------------------------------------------- |
| 18 | 18 | ||
| 19 | +import json | ||
| 19 | import os | 20 | import os |
| 21 | +from mskl.utils import logger | ||
| 20 | from mskl.utils.safe_check import FileChecker | 22 | from mskl.utils.safe_check import FileChecker |
| 21 | 23 | ||
| 24 | +TILING_SO_NAME = 'liboptiling.so' | ||
| 25 | +KERNEL_DIR_NAME = 'kernel' | ||
| 26 | +# 递归搜索workspace时跳过的目录,避免遍历无关内容 | ||
| 27 | +_SKIP_DIRS = {'.git', '__pycache__', 'node_modules', '.venv', 'third_party'} | ||
| 28 | + | ||
| 29 | + | ||
| 30 | +def get_workspace_path(workspace: str = None) -> str: | ||
| 31 | + """将workspace参数归一化为绝对路径,未指定时使用当前目录。""" | ||
| 32 | + if workspace is None: | ||
| 33 | + workspace = os.getcwd() | ||
| 34 | + if not isinstance(workspace, str) or not workspace: | ||
| 35 | + raise ValueError('workspace should be a non-empty str') | ||
| 36 | + workspace = os.path.abspath(workspace) | ||
| 37 | + checker = FileChecker(workspace, "dir") | ||
| 38 | + if not checker.check_input_file(): | ||
| 39 | + if not os.path.exists(workspace): | ||
| 40 | + raise FileNotFoundError(f'workspace {workspace} not exist, please verify path') | ||
| 41 | + raise PermissionError(f'workspace {workspace} check permission failed, please verify path and permissions') | ||
| 42 | + return workspace | ||
| 43 | + | ||
| 44 | + | ||
| 45 | +def search_tiling_so(workspace: str = None): | ||
| 46 | + """在整个workspace目录下递归搜索 liboptiling.so。 | ||
| 47 | + 存在多个时,优先选择打包产物(路径含 _CPack_Packages 或 op_tiling)中的文件。 | ||
| 48 | + """ | ||
| 49 | + workspace = get_workspace_path(workspace) | ||
| 50 | + found = [] | ||
| 51 | + for dirpath, dirnames, filenames in os.walk(workspace): | ||
| 52 | + dirnames[:] = [d for d in dirnames if d not in _SKIP_DIRS] | ||
| 53 | + for name in filenames: | ||
| 54 | + if name == TILING_SO_NAME: | ||
| 55 | + found.append(os.path.join(dirpath, name)) | ||
| 56 | + if not found: | ||
| 57 | + return None | ||
| 58 | + for path in found: | ||
| 59 | + parts = os.path.normpath(path).split(os.sep) | ||
| 60 | + if '_CPack_Packages' in parts or 'op_tiling' in parts: | ||
| 61 | + return path | ||
| 62 | + return found[0] | ||
| 63 | + | ||
| 64 | + | ||
| 65 | +def search_kernel_binary_files(workspace: str = None): | ||
| 66 | + """在整个workspace目录下递归搜索路径含 kernel 目录的 *.o 文件。""" | ||
| 67 | + workspace = get_workspace_path(workspace) | ||
| 68 | + o_files = [] | ||
| 69 | + for dirpath, dirnames, filenames in os.walk(workspace): | ||
| 70 | + dirnames[:] = [d for d in dirnames if d not in _SKIP_DIRS] | ||
| 71 | + if KERNEL_DIR_NAME not in dirpath.split(os.sep): | ||
| 72 | + continue | ||
| 73 | + for name in filenames: | ||
| 74 | + if name.endswith('.o'): | ||
| 75 | + o_files.append(os.path.join(dirpath, name)) | ||
| 76 | + return o_files | ||
| 77 | + | ||
| 78 | + | ||
| 79 | +def match_kernel_binary_by_op_type(o_files, op_type: str): | ||
| 80 | + """从kernel .o文件中,根据算子名称(文件名以 <op_type>_ 开头)筛选出匹配的文件。""" | ||
| 81 | + if not op_type: | ||
| 82 | + return o_files | ||
| 83 | + prefix = op_type + '_' | ||
| 84 | + return [o_file for o_file in o_files if os.path.basename(o_file).startswith(prefix)] | ||
| 85 | + | ||
| 86 | + | ||
| 87 | +def get_device_soc_name() -> str: | ||
| 88 | + """获取当前设备soc名称(如 Ascend950PR_9579),获取失败时返回空串。""" | ||
| 89 | + cann_path = get_cann_path() | ||
| 90 | + try: | ||
| 91 | + import ctypes | ||
| 92 | + | ||
| 93 | + acl = ctypes.CDLL(os.path.join(cann_path, 'lib64/libascendcl.so'), mode=ctypes.RTLD_GLOBAL) | ||
| 94 | + acl.aclrtGetSocName.restype = ctypes.c_char_p | ||
| 95 | + name = acl.aclrtGetSocName() | ||
| 96 | + if name: | ||
| 97 | + return name.decode() | ||
| 98 | + except Exception as e: # 获取失败时回退到rtGetSocVersion | ||
| 99 | + logger.debug(f'Get soc name by aclrtGetSocName failed: {e}') | ||
| 100 | + try: | ||
| 101 | + import ctypes | ||
| 102 | + | ||
| 103 | + rt = ctypes.CDLL(os.path.join(cann_path, 'lib64/libruntime.so'), mode=ctypes.RTLD_GLOBAL) | ||
| 104 | + rt.rtGetSocVersion.argtypes = [ctypes.c_char_p, ctypes.c_uint64] | ||
| 105 | + rt.rtGetSocVersion.restype = ctypes.c_int | ||
| 106 | + buf = ctypes.create_string_buffer(64) | ||
| 107 | + if rt.rtGetSocVersion(buf, 64) == 0 and buf.value: | ||
| 108 | + return buf.value.decode() | ||
| 109 | + except Exception as e: # 获取失败时返回空串,后续回退到默认匹配 | ||
| 110 | + logger.debug(f'Get soc version by rtGetSocVersion failed: {e}') | ||
| 111 | + return '' | ||
| 112 | + | ||
| 113 | + | ||
| 114 | +def _get_soc_dir(o_file: str) -> str: | ||
| 115 | + """从kernel .o路径中提取soc目录名,路径形如 .../kernel/<soc>/<op>/<file>.o。""" | ||
| 116 | + parts = os.path.normpath(o_file).split(os.sep) | ||
| 117 | + try: | ||
| 118 | + idx = parts.index(KERNEL_DIR_NAME) | ||
| 119 | + if idx + 1 < len(parts): | ||
| 120 | + return parts[idx + 1].lower() | ||
| 121 | + except ValueError: | ||
| 122 | + pass | ||
| 123 | + return '' | ||
| 124 | + | ||
| 125 | + | ||
| 126 | +def match_kernel_binary_by_soc(o_files, soc_name: str): | ||
| 127 | + """按设备soc(如 Ascend950PR_9579)筛选kernel .o,优先匹配路径中soc目录与设备soc一致的。""" | ||
| 128 | + if not soc_name: | ||
| 129 | + return [] | ||
| 130 | + soc_lower = soc_name.lower() | ||
| 131 | + matched = [] | ||
| 132 | + for o_file in o_files: | ||
| 133 | + soc_dir = _get_soc_dir(o_file) | ||
| 134 | + # ascend950 匹配 Ascend950PR_9579 / ascend950pr;ascend910b 匹配 Ascend910B3 等 | ||
| 135 | + if soc_dir and (soc_lower.startswith(soc_dir) or soc_dir.startswith(soc_lower)): | ||
| 136 | + matched.append(o_file) | ||
| 137 | + return matched | ||
| 138 | + | ||
| 139 | + | ||
| 140 | +def _normalize_dtype(dtype: str) -> str: | ||
| 141 | + """归一化dtype名称,兼容 float/float32、half/float16 等别名。""" | ||
| 142 | + if not dtype: | ||
| 143 | + return '' | ||
| 144 | + aliases = { | ||
| 145 | + 'half': 'float16', | ||
| 146 | + 'float': 'float32', | ||
| 147 | + 'fp16': 'float16', | ||
| 148 | + 'fp32': 'float32', | ||
| 149 | + 'fp64': 'float64', | ||
| 150 | + 'double': 'float64', | ||
| 151 | + 'int': 'int32', | ||
| 152 | + } | ||
| 153 | + d = str(dtype).lower() | ||
| 154 | + return aliases.get(d, d) | ||
| 155 | + | ||
| 156 | + | ||
| 157 | +def _match_json_dtype(json_dtype: str, actual_dtype: str) -> bool: | ||
| 158 | + if not json_dtype or not actual_dtype: | ||
| 159 | + return True # 信息缺失时不作为排除条件 | ||
| 160 | + return _normalize_dtype(json_dtype) == _normalize_dtype(actual_dtype) | ||
| 161 | + | ||
| 162 | + | ||
| 163 | +def _match_json_format(json_format: str, actual_format: str, match_mode: str = '') -> bool: | ||
| 164 | + if not json_format or not actual_format: | ||
| 165 | + return True | ||
| 166 | + if 'agnostic' in str(match_mode).lower(): | ||
| 167 | + return True # FormatAgnostic:任意format均可 | ||
| 168 | + return str(json_format).lower() == str(actual_format).lower() | ||
| 169 | + | ||
| 170 | + | ||
| 171 | +def _match_json_shape(json_shape, actual_shape) -> bool: | ||
| 172 | + if not json_shape: | ||
| 173 | + return True | ||
| 174 | + # 全部为负值(如 [-2])表示动态shape,匹配任意shape | ||
| 175 | + if all(dim < 0 for dim in json_shape): | ||
| 176 | + return True | ||
| 177 | + if len(json_shape) != len(actual_shape): | ||
| 178 | + return False | ||
| 179 | + for json_dim, actual_dim in zip(json_shape, actual_shape): | ||
| 180 | + if json_dim < 0: # 单个维度为负值表示该维度动态 | ||
| 181 | + continue | ||
| 182 | + if json_dim != actual_dim: | ||
| 183 | + return False | ||
| 184 | + return True | ||
| 185 | + | ||
| 186 | + | ||
| 187 | +def _json_supports_io(meta: dict, io_info: dict) -> bool: | ||
| 188 | + """判断kernel meta json中记录的inputs/outputs是否与本次调用的io信息匹配。""" | ||
| 189 | + support = meta.get('supportInfo', {}) | ||
| 190 | + for io_key, actual_list in (('inputs', io_info.get('inputs', [])), ('outputs', io_info.get('outputs', []))): | ||
| 191 | + json_list = support.get(io_key, []) | ||
| 192 | + if len(actual_list) > len(json_list): | ||
| 193 | + return False | ||
| 194 | + for idx, actual in enumerate(actual_list): | ||
| 195 | + spec = json_list[idx] or {} | ||
| 196 | + if not _match_json_dtype(spec.get('dtype', ''), actual.get('dtype', '')): | ||
| 197 | + return False | ||
| 198 | + if not _match_json_format( | ||
| 199 | + spec.get('format', ''), actual.get('format', ''), spec.get('format_match_mode', '') | ||
| 200 | + ): | ||
| 201 | + return False | ||
| 202 | + if not _match_json_shape(spec.get('shape'), actual.get('shape', [])): | ||
| 203 | + return False | ||
| 204 | + return True | ||
| 205 | + | ||
| 206 | + | ||
| 207 | +def match_kernel_binary_by_json(o_files, io_info: dict): | ||
| 208 | + """根据kernel .o同目录json中的inputs/outputs信息(dtype/format/shape)筛选kernel .o。""" | ||
| 209 | + if not o_files or not io_info: | ||
| 210 | + return [] | ||
| 211 | + matched = [] | ||
| 212 | + for o_file in o_files: | ||
| 213 | + json_path = o_file[:-1] + 'json' # .o -> .json | ||
| 214 | + if not os.path.exists(json_path): | ||
| 215 | + continue | ||
| 216 | + try: | ||
| 217 | + with open(json_path, 'r', encoding='utf-8') as f: | ||
| 218 | + meta = json.load(f) | ||
| 219 | + except Exception as e: # json解析失败时跳过该候选 | ||
| 220 | + logger.warning(f'Read kernel meta json {json_path} failed, skip it. error: {e}') | ||
| 221 | + continue | ||
| 222 | + if _json_supports_io(meta, io_info): | ||
| 223 | + matched.append(o_file) | ||
| 224 | + return matched | ||
| 225 | + | ||
| 22 | 226 | ||
| 23 | def get_cann_path() -> str: | 227 | def get_cann_path() -> str: |
| 24 | cann_path = os.getenv('ASCEND_HOME_PATH') | 228 | cann_path = os.getenv('ASCEND_HOME_PATH') |
| @@ -21,10 +21,11 @@ import unittest | |||
| 21 | import pathlib | 21 | import pathlib |
| 22 | import stat | 22 | import stat |
| 23 | import tempfile | 23 | import tempfile |
| 24 | +import shutil | ||
| 24 | import numpy as np | 25 | import numpy as np |
| 25 | 26 | ||
| 26 | from test.utils.test_base import TestBase # pylint: disable=E0611 | 27 | from test.utils.test_base import TestBase # pylint: disable=E0611 |
| 27 | -from mskl.launcher.config import KernelInvokeConfig, TilingConfig, KernelBinaryInvokeConfig | 28 | +from mskl.launcher.config import KernelInvokeConfig, TilingConfig, KernelBinaryInvokeConfig, _escape_cpp_string |
| 28 | 29 | ||
| 29 | 30 | ||
| 30 | class MockTilingOutput: | 31 | class MockTilingOutput: |
| @@ -102,6 +103,25 @@ class TestTilingConfig(unittest.TestCase): | |||
| 102 | ) | 103 | ) |
| 103 | self.assertEqual(len(config.outputs_list), 1) | 104 | self.assertEqual(len(config.outputs_list), 1) |
| 104 | 105 | ||
| 106 | + def test_tiling_config_data_path_escaped_once(self): | ||
| 107 | + # data_path 含反斜杠/双引号,应统一由 _escape_cpp_string 转义,并直接嵌入生成的tds中(不二次转义) | ||
| 108 | + tmp = tempfile.mkdtemp() | ||
| 109 | + try: | ||
| 110 | + data_path = os.path.join(tmp, 'data "quote"\\dir.bin') | ||
| 111 | + with open(data_path, 'w', encoding='utf-8') as f: | ||
| 112 | + f.write('') | ||
| 113 | + config = TilingConfig( | ||
| 114 | + op_type="TestOp", | ||
| 115 | + inputs=[self.test_input], | ||
| 116 | + outputs=[self.test_output], | ||
| 117 | + inputs_info=[{"shape": [128, 128], "dtype": "float32", "format": "nd", "data_path": data_path}], | ||
| 118 | + ) | ||
| 119 | + escaped = _escape_cpp_string(os.path.abspath(data_path)) | ||
| 120 | + self.assertEqual(config.inputs_list[0][0]['data_path'], escaped) | ||
| 121 | + self.assertIn(f'string{{"{escaped}"}}', config.tds) | ||
| 122 | + finally: | ||
| 123 | + shutil.rmtree(tmp, ignore_errors=True) | ||
| 124 | + | ||
| 105 | def test_tiling_config_with_soc_version(self): | 125 | def test_tiling_config_with_soc_version(self): |
| 106 | config = TilingConfig( | 126 | config = TilingConfig( |
| 107 | op_type="TestOp", inputs=[self.test_input], outputs=[self.test_output], soc_version="Ascend910B" | 127 | op_type="TestOp", inputs=[self.test_input], outputs=[self.test_output], soc_version="Ascend910B" |
| @@ -135,6 +155,56 @@ class TestTilingConfig(unittest.TestCase): | |||
| 135 | def test_tiling_config_empty_lib_path(self): | 155 | def test_tiling_config_empty_lib_path(self): |
| 136 | self.assertRaises(ValueError, TilingConfig, "TestOp", [self.test_input], [self.test_output], lib_path="") | 156 | self.assertRaises(ValueError, TilingConfig, "TestOp", [self.test_input], [self.test_output], lib_path="") |
| 137 | 157 | ||
| 158 | + def test_tiling_config_auto_search_liboptiling_by_workspace(self): | ||
| 159 | + ws = './_test_mskl_cfg_ws' | ||
| 160 | + so_path = os.path.join( | ||
| 161 | + ws, | ||
| 162 | + 'build_out/_CPack_Packages/Linux/External/custom.run/packages/vendors/customize/' | ||
| 163 | + 'op_impl/ai_core/tbe/op_tiling/liboptiling.so', | ||
| 164 | + ) | ||
| 165 | + try: | ||
| 166 | + if os.path.exists(ws): | ||
| 167 | + shutil.rmtree(ws, ignore_errors=True) | ||
| 168 | + os.makedirs(os.path.dirname(so_path), exist_ok=True) | ||
| 169 | + with open(so_path, 'w', encoding='utf-8') as f: | ||
| 170 | + f.write('test so') | ||
| 171 | + config = TilingConfig(op_type="TestOp", inputs=[self.test_input], outputs=[self.test_output], workspace=ws) | ||
| 172 | + self.assertIn(os.path.realpath(so_path), config.lib_path) | ||
| 173 | + finally: | ||
| 174 | + if os.path.exists(ws): | ||
| 175 | + shutil.rmtree(ws, ignore_errors=True) | ||
| 176 | + | ||
| 177 | + def test_tiling_config_explicit_lib_path_priority_over_workspace(self): | ||
| 178 | + ws = './_test_mskl_cfg_ws' | ||
| 179 | + with tempfile.NamedTemporaryFile(suffix='.so', delete=False) as f: | ||
| 180 | + lib_path = f.name | ||
| 181 | + try: | ||
| 182 | + if os.path.exists(ws): | ||
| 183 | + shutil.rmtree(ws, ignore_errors=True) | ||
| 184 | + os.makedirs(ws, exist_ok=True) | ||
| 185 | + config = TilingConfig( | ||
| 186 | + op_type="TestOp", | ||
| 187 | + inputs=[self.test_input], | ||
| 188 | + outputs=[self.test_output], | ||
| 189 | + lib_path=lib_path, | ||
| 190 | + workspace=ws, | ||
| 191 | + ) | ||
| 192 | + self.assertEqual(config.lib_path, lib_path.replace('\\', '\\\\')) | ||
| 193 | + finally: | ||
| 194 | + os.remove(lib_path) | ||
| 195 | + if os.path.exists(ws): | ||
| 196 | + shutil.rmtree(ws, ignore_errors=True) | ||
| 197 | + | ||
| 198 | + def test_tiling_config_invalid_workspace(self): | ||
| 199 | + self.assertRaises( | ||
| 200 | + Exception, | ||
| 201 | + TilingConfig, | ||
| 202 | + "TestOp", | ||
| 203 | + [self.test_input], | ||
| 204 | + [self.test_output], | ||
| 205 | + workspace='./_not_exist_mskl_ws_dir', | ||
| 206 | + ) | ||
| 207 | + | ||
| 138 | def test_tiling_config_invalid_op_type(self): | 208 | def test_tiling_config_invalid_op_type(self): |
| 139 | # 只测试真正会触发的情况 | 209 | # 只测试真正会触发的情况 |
| 140 | invalid_ops = ["", "invalid op", "op@name"] | 210 | invalid_ops = ["", "invalid op", "op@name"] |
| @@ -0,0 +1,202 @@ | |||
| 1 | +#!/usr/bin/python | ||
| 2 | +# -*- coding: UTF-8 -*- | ||
| 3 | +# ------------------------------------------------------------------------- | ||
| 4 | +# This file is part of the MindStudio project. | ||
| 5 | +# Copyright (c) 2025 Huawei Technologies Co.,Ltd. | ||
| 6 | +# | ||
| 7 | +# MindStudio is licensed under Mulan PSL v2. | ||
| 8 | +# You can use this software according to the terms and conditions of the Mulan PSL v2. | ||
| 9 | +# You may obtain a copy of Mulan PSL v2 at: | ||
| 10 | +# | ||
| 11 | +# http://license.coscl.org.cn/MulanPSL2 | ||
| 12 | +# | ||
| 13 | +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, | ||
| 14 | +# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, | ||
| 15 | +# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. | ||
| 16 | +# See the Mulan PSL v2 for more details. | ||
| 17 | +# ------------------------------------------------------------------------- | ||
| 18 | + | ||
| 19 | +import os | ||
| 20 | +import json | ||
| 21 | +import unittest | ||
| 22 | + | ||
| 23 | +from mskl.utils.launcher_utils import ( | ||
| 24 | + get_workspace_path, | ||
| 25 | + search_tiling_so, | ||
| 26 | + search_kernel_binary_files, | ||
| 27 | + match_kernel_binary_by_op_type, | ||
| 28 | + match_kernel_binary_by_soc, | ||
| 29 | + match_kernel_binary_by_json, | ||
| 30 | + _match_json_shape, | ||
| 31 | +) | ||
| 32 | + | ||
| 33 | + | ||
| 34 | +def _touch(path): | ||
| 35 | + os.makedirs(os.path.dirname(path), exist_ok=True) | ||
| 36 | + with open(path, 'w', encoding='utf-8') as f: | ||
| 37 | + f.write('test file') | ||
| 38 | + | ||
| 39 | + | ||
| 40 | +class TestLauncherUtils(unittest.TestCase): | ||
| 41 | + def setUp(self): | ||
| 42 | + self.workspace = './_test_mskl_workspace' | ||
| 43 | + self._cleanup() | ||
| 44 | + os.makedirs(self.workspace, exist_ok=True) | ||
| 45 | + | ||
| 46 | + def tearDown(self): | ||
| 47 | + self._cleanup() | ||
| 48 | + | ||
| 49 | + def _cleanup(self): | ||
| 50 | + if os.path.exists(self.workspace): | ||
| 51 | + import shutil | ||
| 52 | + | ||
| 53 | + shutil.rmtree(self.workspace, ignore_errors=True) | ||
| 54 | + | ||
| 55 | + def test_get_workspace_path_default_to_cwd(self): | ||
| 56 | + self.assertEqual(get_workspace_path(), os.getcwd()) | ||
| 57 | + | ||
| 58 | + def test_get_workspace_path_with_relative_path(self): | ||
| 59 | + os.makedirs(self.workspace, exist_ok=True) | ||
| 60 | + self.assertEqual(get_workspace_path(self.workspace), os.path.abspath(self.workspace)) | ||
| 61 | + | ||
| 62 | + def test_get_workspace_path_invalid(self): | ||
| 63 | + for invalid in ['', None]: | ||
| 64 | + if invalid is None: | ||
| 65 | + continue # None表示使用当前目录 | ||
| 66 | + self.assertRaises(ValueError, get_workspace_path, invalid) | ||
| 67 | + | ||
| 68 | + def test_get_workspace_path_not_exist(self): | ||
| 69 | + nonexist = os.path.join(self.workspace, 'not_exist_dir') | ||
| 70 | + self.assertRaises(FileNotFoundError, get_workspace_path, nonexist) | ||
| 71 | + | ||
| 72 | + def test_search_tiling_so_found(self): | ||
| 73 | + so_path = os.path.join( | ||
| 74 | + self.workspace, | ||
| 75 | + 'build_out/_CPack_Packages/Linux/External/custom.run/packages/vendors/customize/' | ||
| 76 | + 'op_impl/ai_core/tbe/op_tiling/liboptiling.so', | ||
| 77 | + ) | ||
| 78 | + _touch(so_path) | ||
| 79 | + self.assertEqual(search_tiling_so(self.workspace), os.path.abspath(so_path)) | ||
| 80 | + | ||
| 81 | + def test_search_tiling_so_not_found(self): | ||
| 82 | + self.assertIsNone(search_tiling_so(self.workspace)) | ||
| 83 | + | ||
| 84 | + def test_search_tiling_so_prefer_cpack(self): | ||
| 85 | + # 同时存在时优先选择_CPack_Packages/op_tiling下的liboptiling.so | ||
| 86 | + cpack_so = os.path.join( | ||
| 87 | + self.workspace, | ||
| 88 | + 'build_out/_CPack_Packages/Linux/External/custom.run/packages/vendors/customize/' | ||
| 89 | + 'op_impl/ai_core/tbe/op_tiling/liboptiling.so', | ||
| 90 | + ) | ||
| 91 | + other_so = os.path.join(self.workspace, 'op_host/liboptiling.so') | ||
| 92 | + _touch(cpack_so) | ||
| 93 | + _touch(other_so) | ||
| 94 | + self.assertEqual(search_tiling_so(self.workspace), os.path.abspath(cpack_so)) | ||
| 95 | + | ||
| 96 | + def test_search_kernel_binary_files_only_in_kernel_dir(self): | ||
| 97 | + kernel_o = os.path.join( | ||
| 98 | + self.workspace, | ||
| 99 | + 'build_out/_CPack_Packages/Linux/External/custom.run/packages/vendors/customize/' | ||
| 100 | + 'op_impl/ai_core/tbe/kernel/ascend910b/custom_op/OpCustom_1.o', | ||
| 101 | + ) | ||
| 102 | + other_o = os.path.join( | ||
| 103 | + self.workspace, | ||
| 104 | + 'build_out/_CPack_Packages/Linux/External/custom.run/packages/vendors/customize/' | ||
| 105 | + 'op_impl/ai_core/tbe/op_tiling/other.o', | ||
| 106 | + ) | ||
| 107 | + _touch(kernel_o) | ||
| 108 | + _touch(other_o) | ||
| 109 | + result = search_kernel_binary_files(self.workspace) | ||
| 110 | + self.assertEqual(result, [os.path.abspath(kernel_o)]) | ||
| 111 | + | ||
| 112 | + def test_match_kernel_binary_by_op_type(self): | ||
| 113 | + o_files = [ | ||
| 114 | + '/p/kernel/ascend950/add_custom/AddCustom_hash_0.o', | ||
| 115 | + '/p/kernel/ascend950/leaky_relu_custom/LeakyReluCustom_hash_0.o', | ||
| 116 | + '/p/kernel/ascend950/add_custom_template/AddCustomTemplate_hash_1.o', | ||
| 117 | + ] | ||
| 118 | + self.assertEqual( | ||
| 119 | + match_kernel_binary_by_op_type(o_files, 'AddCustom'), | ||
| 120 | + ['/p/kernel/ascend950/add_custom/AddCustom_hash_0.o'], | ||
| 121 | + ) | ||
| 122 | + # 未传op_type时不进行过滤 | ||
| 123 | + self.assertEqual(match_kernel_binary_by_op_type(o_files, None), o_files) | ||
| 124 | + | ||
| 125 | + def test_match_kernel_binary_by_soc(self): | ||
| 126 | + o_files = [ | ||
| 127 | + '/p/kernel/ascend910_93/add_custom/AddCustom_hash_0.o', | ||
| 128 | + '/p/kernel/ascend950/add_custom/AddCustom_hash_0.o', | ||
| 129 | + '/p/kernel/ascend910b/add_custom/AddCustom_hash_0.o', | ||
| 130 | + ] | ||
| 131 | + self.assertEqual( | ||
| 132 | + match_kernel_binary_by_soc(o_files, 'Ascend950PR_9579'), | ||
| 133 | + ['/p/kernel/ascend950/add_custom/AddCustom_hash_0.o'], | ||
| 134 | + ) | ||
| 135 | + # 空soc名称时不匹配 | ||
| 136 | + self.assertEqual(match_kernel_binary_by_soc(o_files, ''), []) | ||
| 137 | + | ||
| 138 | + def test_match_json_shape(self): | ||
| 139 | + # 全为负值([-2])表示动态shape | ||
| 140 | + self.assertTrue(_match_json_shape([-2], [8, 2048])) | ||
| 141 | + # -1 表示该维度动态 | ||
| 142 | + self.assertTrue(_match_json_shape([-1, 8], [4, 8])) | ||
| 143 | + self.assertTrue(_match_json_shape([-1, 8], [16, 8])) | ||
| 144 | + # 固定维度需要精确匹配 | ||
| 145 | + self.assertFalse(_match_json_shape([8, 2048], [4, 8])) | ||
| 146 | + # rank不匹配 | ||
| 147 | + self.assertFalse(_match_json_shape([8], [8, 2048])) | ||
| 148 | + | ||
| 149 | + def test_match_kernel_binary_by_json(self): | ||
| 150 | + kernel_a = os.path.join(self.workspace, 'kernel/ascend950/add_custom_template/AddCustomTemplate_hashA.o') | ||
| 151 | + kernel_b = os.path.join(self.workspace, 'kernel/ascend950/add_custom_template/AddCustomTemplate_hashB.o') | ||
| 152 | + _touch(kernel_a) | ||
| 153 | + _touch(kernel_b) | ||
| 154 | + for kernel, dtype in ((kernel_a, 'float16'), (kernel_b, 'float32')): | ||
| 155 | + json_path = kernel[:-1] + 'json' | ||
| 156 | + meta = { | ||
| 157 | + 'supportInfo': { | ||
| 158 | + 'inputs': [ | ||
| 159 | + { | ||
| 160 | + 'name': 'x', | ||
| 161 | + 'dtype': dtype, | ||
| 162 | + 'format': 'ND', | ||
| 163 | + 'shape': [-2], | ||
| 164 | + 'format_match_mode': 'FormatAgnostic', | ||
| 165 | + }, | ||
| 166 | + { | ||
| 167 | + 'name': 'y', | ||
| 168 | + 'dtype': dtype, | ||
| 169 | + 'format': 'ND', | ||
| 170 | + 'shape': [-2], | ||
| 171 | + 'format_match_mode': 'FormatAgnostic', | ||
| 172 | + }, | ||
| 173 | + ], | ||
| 174 | + 'outputs': [ | ||
| 175 | + { | ||
| 176 | + 'name': 'z', | ||
| 177 | + 'dtype': dtype, | ||
| 178 | + 'format': 'ND', | ||
| 179 | + 'shape': [-2], | ||
| 180 | + 'format_match_mode': 'FormatAgnostic', | ||
| 181 | + }, | ||
| 182 | + ], | ||
| 183 | + } | ||
| 184 | + } | ||
| 185 | + with open(json_path, 'w', encoding='utf-8') as f: | ||
| 186 | + json.dump(meta, f) | ||
| 187 | + | ||
| 188 | + io_info = { | ||
| 189 | + 'inputs': [ | ||
| 190 | + {'dtype': 'float16', 'format': 'ND', 'shape': [8, 2048]}, | ||
| 191 | + {'dtype': 'float16', 'format': 'ND', 'shape': [8, 2048]}, | ||
| 192 | + ], | ||
| 193 | + 'outputs': [{'dtype': 'float16', 'format': 'ND', 'shape': [8, 2048]}], | ||
| 194 | + } | ||
| 195 | + matched = match_kernel_binary_by_json([kernel_a, kernel_b], io_info) | ||
| 196 | + self.assertEqual(matched, [kernel_a]) | ||
| 197 | + # json不存在时不参与匹配 | ||
| 198 | + self.assertEqual(match_kernel_binary_by_json(['/p/kernel/x/no_meta.o'], io_info), []) | ||
| 199 | + | ||
| 200 | + | ||
| 201 | +if __name__ == '__main__': | ||
| 202 | + unittest.main() | ||
| @@ -19,16 +19,23 @@ import os | |||
| 19 | import shutil | 19 | import shutil |
| 20 | import numpy as np | 20 | import numpy as np |
| 21 | import unittest | 21 | import unittest |
| 22 | -from unittest.mock import patch, Mock | 22 | +from unittest.mock import patch |
| 23 | 23 | ||
| 24 | -from mskl.launcher.opgen_workflow import TilingOutput, tiling_func, get_kernel_from_binary, TMP_FOLDER | 24 | +from mskl.launcher.opgen_workflow import ( |
| 25 | -from mskl.utils import safe_check | 25 | + TilingOutput, |
| 26 | + tiling_func, | ||
| 27 | + get_kernel_from_binary, | ||
| 28 | + TMP_FOLDER, | ||
| 29 | + _build_io_info, | ||
| 30 | +) | ||
| 31 | +from mskl.launcher import opgen_workflow | ||
| 26 | from mskl.launcher.context import context | 32 | from mskl.launcher.context import context |
| 33 | +from mskl.utils import safe_check | ||
| 27 | 34 | ||
| 28 | tiling_dict = { | 35 | tiling_dict = { |
| 29 | "blockdim": 8, | 36 | "blockdim": 8, |
| 30 | "workspace_size": 64, | 37 | "workspace_size": 64, |
| 31 | - "tiling_data": [1,2,3,4], | 38 | + "tiling_data": [1, 2, 3, 4], |
| 32 | "tiling_key": 1111, | 39 | "tiling_key": 1111, |
| 33 | } | 40 | } |
| 34 | 41 | ||
| @@ -37,6 +44,27 @@ def mock_tiling_func(): | |||
| 37 | return tiling_dict | 44 | return tiling_dict |
| 38 | 45 | ||
| 39 | 46 | ||
| 47 | +def _touch(path): | ||
| 48 | + os.makedirs(os.path.dirname(path), exist_ok=True) | ||
| 49 | + with open(path, 'w', encoding='utf-8') as f: | ||
| 50 | + f.write('test file') | ||
| 51 | + | ||
| 52 | + | ||
| 53 | +def _build_workspace(with_tiling_so=True, kernel_o_files=(), kernel_soc='ascend910b'): | ||
| 54 | + ws = os.path.join(os.getcwd(), '_test_mskl_ws') | ||
| 55 | + if os.path.exists(ws): | ||
| 56 | + shutil.rmtree(ws, ignore_errors=True) | ||
| 57 | + base = os.path.join( | ||
| 58 | + ws, | ||
| 59 | + 'build_out/_CPack_Packages/Linux/External/custom.run/packages/vendors/customize/op_impl/ai_core/tbe', | ||
| 60 | + ) | ||
| 61 | + if with_tiling_so: | ||
| 62 | + _touch(os.path.join(base, 'op_tiling/liboptiling.so')) | ||
| 63 | + for o in kernel_o_files: | ||
| 64 | + _touch(os.path.join(base, 'kernel', kernel_soc, 'custom_op', o)) | ||
| 65 | + return ws | ||
| 66 | + | ||
| 67 | + | ||
| 40 | class TestOpgenWorkflow(unittest.TestCase): | 68 | class TestOpgenWorkflow(unittest.TestCase): |
| 41 | LIB = 'test_tiling.so' | 69 | LIB = 'test_tiling.so' |
| 42 | CPP = '_mskl_gen_tiling.cpp' | 70 | CPP = '_mskl_gen_tiling.cpp' |
| @@ -47,16 +75,15 @@ class TestOpgenWorkflow(unittest.TestCase): | |||
| 47 | 75 | ||
| 48 | 76 | ||
| 49 | def setUpClass(cls): | 77 | def setUpClass(cls): |
| 50 | - with os.fdopen(os.open(TestOpgenWorkflow.LIB, | 78 | + with os.fdopen( |
| 51 | - safe_check.OPEN_FLAGS, | 79 | + os.open(TestOpgenWorkflow.LIB, safe_check.OPEN_FLAGS, safe_check.SAVE_DATA_FILE_AUTHORITY), 'w' |
| 52 | - safe_check.SAVE_DATA_FILE_AUTHORITY), | 80 | + ) as f: |
| 53 | - 'w') as f: | ||
| 54 | f.truncate() | 81 | f.truncate() |
| 55 | f.write('this is a test so') | 82 | f.write('this is a test so') |
| 56 | - with os.fdopen(os.open(TestOpgenWorkflow.KERNEL_BINARY_PATH, | 83 | + with os.fdopen( |
| 57 | - safe_check.OPEN_FLAGS, | 84 | + os.open(TestOpgenWorkflow.KERNEL_BINARY_PATH, safe_check.OPEN_FLAGS, safe_check.SAVE_DATA_FILE_AUTHORITY), |
| 58 | - safe_check.SAVE_DATA_FILE_AUTHORITY), | 85 | + 'w', |
| 59 | - 'w') as f: | 86 | + ) as f: |
| 60 | f.truncate() | 87 | f.truncate() |
| 61 | f.write('this is a test kernel binary file') | 88 | f.write('this is a test kernel binary file') |
| 62 | 89 | ||
| @@ -85,13 +112,17 @@ class TestOpgenWorkflow(unittest.TestCase): | |||
| 85 | a = np.random.uniform(1, 100, [2, 32]).astype(np.float16) | 112 | a = np.random.uniform(1, 100, [2, 32]).astype(np.float16) |
| 86 | b = np.random.uniform(1, 100, [2, 32]).astype(np.float16) | 113 | b = np.random.uniform(1, 100, [2, 32]).astype(np.float16) |
| 87 | c = np.zeros([2, 32]).astype(np.float16) | 114 | c = np.zeros([2, 32]).astype(np.float16) |
| 88 | - inputs_info = [{"shape": [2, 32], "dtype": "float16", "format": "ND"}, | 115 | + inputs_info = [ |
| 89 | - {"shape": [2, 32], "dtype": "float16", "format": "ND"}] | 116 | + {"shape": [2, 32], "dtype": "float16", "format": "ND"}, |
| 117 | + {"shape": [2, 32], "dtype": "float16", "format": "ND"}, | ||
| 118 | + ] | ||
| 90 | outputs_info = [{"shape": [2, 32], "dtype": "float16", "format": "ND"}] | 119 | outputs_info = [{"shape": [2, 32], "dtype": "float16", "format": "ND"}] |
| 91 | output = tiling_func( | 120 | output = tiling_func( |
| 92 | op_type="AddCustom", | 121 | op_type="AddCustom", |
| 93 | - inputs_info=inputs_info, outputs_info=outputs_info, | 122 | + inputs_info=inputs_info, |
| 94 | - inputs=[a, b], outputs=[c], | 123 | + outputs_info=outputs_info, |
| 124 | + inputs=[a, b], | ||
| 125 | + outputs=[c], | ||
| 95 | attr={ | 126 | attr={ |
| 96 | "a1": 1, | 127 | "a1": 1, |
| 97 | "a2": False, | 128 | "a2": False, |
| @@ -101,9 +132,9 @@ class TestOpgenWorkflow(unittest.TestCase): | |||
| 101 | "a6": [True, False], | 132 | "a6": [True, False], |
| 102 | "a7": ["asdf", "zxcv"], | 133 | "a7": ["asdf", "zxcv"], |
| 103 | "a8": [[1, 2, 3, 4], [5, 6, 7, 8], [5646, 2345]], | 134 | "a8": [[1, 2, 3, 4], [5, 6, 7, 8], [5646, 2345]], |
| 104 | - "a9": [111, 222, 333] | 135 | + "a9": [111, 222, 333], |
| 105 | }, | 136 | }, |
| 106 | - lib_path=TestOpgenWorkflow.LIB | 137 | + lib_path=TestOpgenWorkflow.LIB, |
| 107 | ) | 138 | ) |
| 108 | mock_compile_tiling.assert_called_once() | 139 | mock_compile_tiling.assert_called_once() |
| 109 | self.assertEqual(output.blockdim, TilingOutput(tiling_dict).blockdim) | 140 | self.assertEqual(output.blockdim, TilingOutput(tiling_dict).blockdim) |
| @@ -116,16 +147,21 @@ class TestOpgenWorkflow(unittest.TestCase): | |||
| 116 | a = np.random.uniform(1, 100, [2, 32]).astype(np.float16) | 147 | a = np.random.uniform(1, 100, [2, 32]).astype(np.float16) |
| 117 | b = np.random.uniform(1, 100, [2, 32]).astype(np.float16) | 148 | b = np.random.uniform(1, 100, [2, 32]).astype(np.float16) |
| 118 | c = np.zeros([2, 32]).astype(np.float16) | 149 | c = np.zeros([2, 32]).astype(np.float16) |
| 119 | - inputs_info = [[{"shape": [2, 32], "dtype": "float16", "format": "ND"}, | 150 | + inputs_info = [ |
| 120 | - {"shape": [2, 32], "dtype": "float16", "format": "ND"}, | 151 | + [ |
| 121 | - {"shape": [2, 32], "dtype": "float16", "format": "ND"}], | 152 | + {"shape": [2, 32], "dtype": "float16", "format": "ND"}, |
| 122 | - {"shape": [2, 32], "dtype": "float16", "format": "ND"}, | 153 | + {"shape": [2, 32], "dtype": "float16", "format": "ND"}, |
| 123 | - ] | 154 | + {"shape": [2, 32], "dtype": "float16", "format": "ND"}, |
| 155 | + ], | ||
| 156 | + {"shape": [2, 32], "dtype": "float16", "format": "ND"}, | ||
| 157 | + ] | ||
| 124 | outputs_info = [{"shape": [2, 32], "dtype": "float16", "format": "ND"}] | 158 | outputs_info = [{"shape": [2, 32], "dtype": "float16", "format": "ND"}] |
| 125 | output = tiling_func( | 159 | output = tiling_func( |
| 126 | op_type="AddCustom", | 160 | op_type="AddCustom", |
| 127 | - inputs_info=inputs_info, outputs_info=outputs_info, | 161 | + inputs_info=inputs_info, |
| 128 | - inputs=[[a, a, b], b], outputs=[c], | 162 | + outputs_info=outputs_info, |
| 163 | + inputs=[[a, a, b], b], | ||
| 164 | + outputs=[c], | ||
| 129 | attr=[ | 165 | attr=[ |
| 130 | {"name": "a1", "dtype": "int", "value": 1}, | 166 | {"name": "a1", "dtype": "int", "value": 1}, |
| 131 | {"name": "a2", "dtype": "bool", "value": False}, | 167 | {"name": "a2", "dtype": "bool", "value": False}, |
| @@ -142,7 +178,7 @@ class TestOpgenWorkflow(unittest.TestCase): | |||
| 142 | {"name": "a11", "dtype": "int64", "value": 2}, | 178 | {"name": "a11", "dtype": "int64", "value": 2}, |
| 143 | {"name": "a12", "dtype": "float32", "value": 1.3}, | 179 | {"name": "a12", "dtype": "float32", "value": 1.3}, |
| 144 | ], | 180 | ], |
| 145 | - lib_path=TestOpgenWorkflow.LIB | 181 | + lib_path=TestOpgenWorkflow.LIB, |
| 146 | ) | 182 | ) |
| 147 | mock_compile_tiling.assert_called_once() | 183 | mock_compile_tiling.assert_called_once() |
| 148 | self.assertEqual(output.blockdim, TilingOutput(tiling_dict).blockdim) | 184 | self.assertEqual(output.blockdim, TilingOutput(tiling_dict).blockdim) |
| @@ -158,3 +194,213 @@ class TestOpgenWorkflow(unittest.TestCase): | |||
| 158 | kernel = get_kernel_from_binary(TestOpgenWorkflow.KERNEL_BINARY_PATH, 'mix') | 194 | kernel = get_kernel_from_binary(TestOpgenWorkflow.KERNEL_BINARY_PATH, 'mix') |
| 159 | mock_compile_kernel_binary.assert_called_once() | 195 | mock_compile_kernel_binary.assert_called_once() |
| 160 | self.assertEqual(kernel, None) | 196 | self.assertEqual(kernel, None) |
| 197 | + | ||
| 198 | + | ||
| 199 | + def test_tiling_func_auto_search_liboptiling_by_workspace(self, mock_compile_tiling): | ||
| 200 | + mock_compile_tiling.return_value = mock_tiling_func | ||
| 201 | + ws = _build_workspace(with_tiling_so=True) | ||
| 202 | + | ||
| 203 | + a = np.random.uniform(1, 100, [2, 32]).astype(np.float16) | ||
| 204 | + b = np.random.uniform(1, 100, [2, 32]).astype(np.float16) | ||
| 205 | + c = np.zeros([2, 32]).astype(np.float16) | ||
| 206 | + inputs_info = [ | ||
| 207 | + {"shape": [2, 32], "dtype": "float16", "format": "ND"}, | ||
| 208 | + {"shape": [2, 32], "dtype": "float16", "format": "ND"}, | ||
| 209 | + ] | ||
| 210 | + outputs_info = [{"shape": [2, 32], "dtype": "float16", "format": "ND"}] | ||
| 211 | + try: | ||
| 212 | + output = tiling_func( | ||
| 213 | + op_type="AddCustom", | ||
| 214 | + inputs_info=inputs_info, | ||
| 215 | + outputs_info=outputs_info, | ||
| 216 | + inputs=[a, b], | ||
| 217 | + outputs=[c], | ||
| 218 | + workspace=ws, | ||
| 219 | + ) | ||
| 220 | + mock_compile_tiling.assert_called_once() | ||
| 221 | + self.assertEqual(output.blockdim, TilingOutput(tiling_dict).blockdim) | ||
| 222 | + self.assertEqual(context.workspace, os.path.abspath(ws)) | ||
| 223 | + expected_so = os.path.join( | ||
| 224 | + ws, | ||
| 225 | + 'build_out/_CPack_Packages/Linux/External/custom.run/packages/vendors/customize/' | ||
| 226 | + 'op_impl/ai_core/tbe/op_tiling/liboptiling.so', | ||
| 227 | + ) | ||
| 228 | + gen_cpp = os.path.join(TMP_FOLDER, f'_mskl_gen_tiling.{opgen_workflow.TILING_FUNC_CNT}.cpp') | ||
| 229 | + with open(gen_cpp, 'r', encoding='utf-8') as f: | ||
| 230 | + self.assertIn(os.path.realpath(expected_so), f.read()) | ||
| 231 | + finally: | ||
| 232 | + context.workspace = None | ||
| 233 | + if os.path.exists(ws): | ||
| 234 | + shutil.rmtree(ws, ignore_errors=True) | ||
| 235 | + | ||
| 236 | + | ||
| 237 | + def test_tiling_func_explicit_lib_path_priority_over_workspace(self, mock_compile_tiling): | ||
| 238 | + mock_compile_tiling.return_value = mock_tiling_func | ||
| 239 | + ws = _build_workspace(with_tiling_so=True) | ||
| 240 | + with open(TestOpgenWorkflow.LIB, 'w', encoding='utf-8') as f: | ||
| 241 | + f.write('this is a test so') | ||
| 242 | + | ||
| 243 | + a = np.random.uniform(1, 100, [2, 32]).astype(np.float16) | ||
| 244 | + b = np.random.uniform(1, 100, [2, 32]).astype(np.float16) | ||
| 245 | + c = np.zeros([2, 32]).astype(np.float16) | ||
| 246 | + inputs_info = [ | ||
| 247 | + {"shape": [2, 32], "dtype": "float16", "format": "ND"}, | ||
| 248 | + {"shape": [2, 32], "dtype": "float16", "format": "ND"}, | ||
| 249 | + ] | ||
| 250 | + outputs_info = [{"shape": [2, 32], "dtype": "float16", "format": "ND"}] | ||
| 251 | + try: | ||
| 252 | + output = tiling_func( | ||
| 253 | + op_type="AddCustom", | ||
| 254 | + inputs_info=inputs_info, | ||
| 255 | + outputs_info=outputs_info, | ||
| 256 | + inputs=[a, b], | ||
| 257 | + outputs=[c], | ||
| 258 | + lib_path=TestOpgenWorkflow.LIB, | ||
| 259 | + workspace=ws, | ||
| 260 | + ) | ||
| 261 | + mock_compile_tiling.assert_called_once() | ||
| 262 | + self.assertEqual(output.blockdim, TilingOutput(tiling_dict).blockdim) | ||
| 263 | + gen_cpp = os.path.join(TMP_FOLDER, f'_mskl_gen_tiling.{opgen_workflow.TILING_FUNC_CNT}.cpp') | ||
| 264 | + with open(gen_cpp, 'r', encoding='utf-8') as f: | ||
| 265 | + gen_src = f.read() | ||
| 266 | + self.assertIn(os.path.realpath(TestOpgenWorkflow.LIB), gen_src) | ||
| 267 | + self.assertNotIn('liboptiling.so', gen_src) | ||
| 268 | + finally: | ||
| 269 | + context.workspace = None | ||
| 270 | + if os.path.exists(ws): | ||
| 271 | + shutil.rmtree(ws, ignore_errors=True) | ||
| 272 | + | ||
| 273 | + | ||
| 274 | + def test_get_kernel_from_binary_auto_search_single_o(self, mock_compile_kernel_binary): | ||
| 275 | + with patch('mskl.utils.launcher_utils.check_runtime_impl', return_value=True): | ||
| 276 | + with patch('mskl.utils.launcher_utils.get_cann_path', return_value=""): | ||
| 277 | + mock_compile_kernel_binary.return_value = None | ||
| 278 | + ws = _build_workspace(with_tiling_so=False, kernel_o_files=['TestOpType_1.o']) | ||
| 279 | + context.tiling_output = TilingOutput(tiling_dict) | ||
| 280 | + context.op_type = 'TestOpType' | ||
| 281 | + context.workspace = os.path.abspath(ws) | ||
| 282 | + try: | ||
| 283 | + kernel = get_kernel_from_binary(None, 'mix', 1) | ||
| 284 | + mock_compile_kernel_binary.assert_called_once() | ||
| 285 | + self.assertEqual(kernel, None) | ||
| 286 | + gen_cpp = os.path.join( | ||
| 287 | + TMP_FOLDER, f'_mskl_gen_binary_launch.{opgen_workflow.GET_KERNEL_FROM_BINARY_CNT}.cpp' | ||
| 288 | + ) | ||
| 289 | + with open(gen_cpp, 'r', encoding='utf-8') as f: | ||
| 290 | + self.assertIn('TestOpType_1.o', f.read()) | ||
| 291 | + finally: | ||
| 292 | + context.workspace = None | ||
| 293 | + if os.path.exists(ws): | ||
| 294 | + shutil.rmtree(ws, ignore_errors=True) | ||
| 295 | + | ||
| 296 | + def test_get_kernel_from_binary_auto_search_select_by_soc(self): | ||
| 297 | + # 多个soc目录下存在同名同tiling_key的kernel,应优先选择与设备soc匹配的 | ||
| 298 | + with patch('mskl.launcher.opgen_workflow.compile_kernel_binary') as mock_compile_kernel_binary: | ||
| 299 | + with patch('mskl.utils.launcher_utils.check_runtime_impl', return_value=True): | ||
| 300 | + with patch('mskl.utils.launcher_utils.get_cann_path', return_value=""): | ||
| 301 | + with patch('mskl.launcher.opgen_workflow.get_device_soc_name', return_value='Ascend950PR_9579'): | ||
| 302 | + mock_compile_kernel_binary.return_value = None | ||
| 303 | + ws = _build_workspace( | ||
| 304 | + with_tiling_so=False, kernel_o_files=['TestOpType_1.o'], kernel_soc='ascend910_93' | ||
| 305 | + ) | ||
| 306 | + _build_workspace_extra = os.path.join( | ||
| 307 | + os.getcwd(), | ||
| 308 | + '_test_mskl_ws', | ||
| 309 | + 'build_out/_CPack_Packages/Linux/External/custom.run/packages/vendors/customize/' | ||
| 310 | + 'op_impl/ai_core/tbe/kernel', | ||
| 311 | + ) | ||
| 312 | + for soc in ('ascend950', 'ascend910b'): | ||
| 313 | + os.makedirs(os.path.join(_build_workspace_extra, soc, 'custom_op'), exist_ok=True) | ||
| 314 | + _touch(os.path.join(_build_workspace_extra, soc, 'custom_op', 'TestOpType_1.o')) | ||
| 315 | + context.tiling_output = TilingOutput(tiling_dict) | ||
| 316 | + context.op_type = 'TestOpType' | ||
| 317 | + context.workspace = os.path.abspath(ws) | ||
| 318 | + try: | ||
| 319 | + kernel = get_kernel_from_binary(None, 'mix', 1) | ||
| 320 | + mock_compile_kernel_binary.assert_called_once() | ||
| 321 | + self.assertEqual(kernel, None) | ||
| 322 | + gen_cpp = os.path.join( | ||
| 323 | + TMP_FOLDER, | ||
| 324 | + f'_mskl_gen_binary_launch.{opgen_workflow.GET_KERNEL_FROM_BINARY_CNT}.cpp', | ||
| 325 | + ) | ||
| 326 | + with open(gen_cpp, 'r', encoding='utf-8') as f: | ||
| 327 | + self.assertIn('ascend950/custom_op/TestOpType_1.o', f.read()) | ||
| 328 | + self.assertNotIn('ascend910_93', f.read()) | ||
| 329 | + finally: | ||
| 330 | + context.workspace = None | ||
| 331 | + if os.path.exists(ws): | ||
| 332 | + shutil.rmtree(ws, ignore_errors=True) | ||
| 333 | + | ||
| 334 | + def test_build_io_info_from_tensors(self): | ||
| 335 | + x = np.zeros([8, 2048], dtype=np.float16) | ||
| 336 | + y = np.zeros([8, 2048], dtype=np.float16) | ||
| 337 | + z = np.zeros([8, 2048], dtype=np.float16) | ||
| 338 | + info = _build_io_info([x, y], [z], None, None) | ||
| 339 | + self.assertEqual(info['inputs'][0]['dtype'], 'float16') | ||
| 340 | + self.assertEqual(info['inputs'][0]['format'], 'ND') | ||
| 341 | + self.assertEqual(info['inputs'][0]['shape'], [8, 2048]) | ||
| 342 | + self.assertEqual(len(info['inputs']), 2) | ||
| 343 | + self.assertEqual(info['outputs'][0]['dtype'], 'float16') | ||
| 344 | + | ||
| 345 | + | ||
| 346 | + def test_get_kernel_from_binary_auto_search_select_by_json(self, mock_compile_kernel_binary): | ||
| 347 | + # 多个soc相同、同算子名的kernel,根据同目录json中的dtype区分 | ||
| 348 | + with patch('mskl.utils.launcher_utils.check_runtime_impl', return_value=True): | ||
| 349 | + with patch('mskl.utils.launcher_utils.get_cann_path', return_value=""): | ||
| 350 | + with patch('mskl.launcher.opgen_workflow.get_device_soc_name', return_value='Ascend950PR_9579'): | ||
| 351 | + mock_compile_kernel_binary.return_value = None | ||
| 352 | + ws = _build_workspace( | ||
| 353 | + with_tiling_so=False, | ||
| 354 | + kernel_soc='ascend950', | ||
| 355 | + kernel_o_files=['TestOpType_f16.o', 'TestOpType_f32.o'], | ||
| 356 | + ) | ||
| 357 | + # 为两个 .o 生成描述不同dtype的json | ||
| 358 | + base = os.path.join( | ||
| 359 | + ws, | ||
| 360 | + 'build_out/_CPack_Packages/Linux/External/custom.run/packages/vendors/customize/' | ||
| 361 | + 'op_impl/ai_core/tbe/kernel/ascend950/custom_op', | ||
| 362 | + ) | ||
| 363 | + for o_name, dtype in (('TestOpType_f16.o', 'float16'), ('TestOpType_f32.o', 'float32')): | ||
| 364 | + meta = { | ||
| 365 | + 'supportInfo': { | ||
| 366 | + 'inputs': [{'dtype': dtype, 'format': 'ND', 'shape': [-2]}], | ||
| 367 | + 'outputs': [{'dtype': dtype, 'format': 'ND', 'shape': [-2]}], | ||
| 368 | + } | ||
| 369 | + } | ||
| 370 | + with open(os.path.join(base, o_name[:-1] + 'json'), 'w', encoding='utf-8') as f: | ||
| 371 | + import json | ||
| 372 | + | ||
| 373 | + json.dump(meta, f) | ||
| 374 | + context.tiling_output = TilingOutput(tiling_dict) | ||
| 375 | + context.op_type = 'TestOpType' | ||
| 376 | + context.workspace = os.path.abspath(ws) | ||
| 377 | + context.io_info = { | ||
| 378 | + 'inputs': [{'dtype': 'float16', 'format': 'ND', 'shape': [8, 2048]}], | ||
| 379 | + 'outputs': [{'dtype': 'float16', 'format': 'ND', 'shape': [8, 2048]}], | ||
| 380 | + } | ||
| 381 | + try: | ||
| 382 | + kernel = get_kernel_from_binary(None, 'mix', 1) | ||
| 383 | + mock_compile_kernel_binary.assert_called_once() | ||
| 384 | + self.assertEqual(kernel, None) | ||
| 385 | + gen_cpp = os.path.join( | ||
| 386 | + TMP_FOLDER, f'_mskl_gen_binary_launch.{opgen_workflow.GET_KERNEL_FROM_BINARY_CNT}.cpp' | ||
| 387 | + ) | ||
| 388 | + with open(gen_cpp, 'r', encoding='utf-8') as f: | ||
| 389 | + self.assertIn('TestOpType_f16.o', f.read()) | ||
| 390 | + finally: | ||
| 391 | + context.workspace = None | ||
| 392 | + context.io_info = None | ||
| 393 | + if os.path.exists(ws): | ||
| 394 | + shutil.rmtree(ws, ignore_errors=True) | ||
| 395 | + | ||
| 396 | + def test_get_kernel_from_binary_auto_search_no_o_found(self): | ||
| 397 | + ws = _build_workspace(with_tiling_so=False) | ||
| 398 | + context.tiling_output = TilingOutput(tiling_dict) | ||
| 399 | + context.op_type = 'TestOpType' | ||
| 400 | + context.workspace = os.path.abspath(ws) | ||
| 401 | + try: | ||
| 402 | + self.assertRaises(FileNotFoundError, get_kernel_from_binary, None, 'mix', 1) | ||
| 403 | + finally: | ||
| 404 | + context.workspace = None | ||
| 405 | + if os.path.exists(ws): | ||
| 406 | + shutil.rmtree(ws, ignore_errors=True) | ||