已合并
MQSMLA pytest support only run case in excel #8650
郑文惠创建于 7月13日
MQSMLA pytest support only run case in excel #8650
已合并
共 7 个文件变更+193-82
Mattention/mixed_quant_sparse_flash_mla/tests/pytest/batch/mixed_quant_sparse_flash_mla_process.py+9-7
| @@ -28,7 +28,7 @@ class Network(torch.nn.Module): | |||
| 28 | 28 | ||
| 29 | def forward(self, q, ori_kv, cmp_kv, cmp_sparse_indices, ori_block_table, | 29 | def forward(self, q, ori_kv, cmp_kv, cmp_sparse_indices, ori_block_table, |
| 30 | cmp_block_table, cu_seqlens_q, seqused_q, cu_seqlens_ori_kv, cu_seqlens_cmp_kv, | 30 | cmp_block_table, cu_seqlens_q, seqused_q, cu_seqlens_ori_kv, cu_seqlens_cmp_kv, |
| 31 | - seqused_ori_kv, seqused_cmp_kv, cmp_residual_kv, sinks, metadata, quant_mode, rope_head_dim, | 31 | + seqused_ori_kv, seqused_cmp_kv, cmp_residual_kv, ori_topk_length, cmp_topk_length, sinks, metadata, quant_mode, rope_head_dim, |
| 32 | softmax_scale, cmp_ratio, ori_mask_mode, cmp_mask_mode, ori_win_left, ori_win_right, layout_q, layout_kv, | 32 | softmax_scale, cmp_ratio, ori_mask_mode, cmp_mask_mode, ori_win_left, ori_win_right, layout_q, layout_kv, |
| 33 | topk_value_mode, return_softmax_lse): | 33 | topk_value_mode, return_softmax_lse): |
| 34 | npu_result, _ = torch.ops.cann_ops_transformer.mixed_quant_sparse_flash_mla( | 34 | npu_result, _ = torch.ops.cann_ops_transformer.mixed_quant_sparse_flash_mla( |
| @@ -40,14 +40,14 @@ class Network(torch.nn.Module): | |||
| 40 | ori_block_table=ori_block_table, | 40 | ori_block_table=ori_block_table, |
| 41 | cmp_block_table=cmp_block_table, | 41 | cmp_block_table=cmp_block_table, |
| 42 | cu_seqlens_q=cu_seqlens_q, | 42 | cu_seqlens_q=cu_seqlens_q, |
| 43 | - cu_seqlens_ori_kv=op_input['cu_seqlens_ori_kv'].npu() if op_input['cu_seqlens_ori_kv'] is not None else None, | 43 | + cu_seqlens_ori_kv=cu_seqlens_ori_kv, |
| 44 | - cu_seqlens_cmp_kv=op_input['cu_seqlens_cmp_kv'].npu() if op_input['cu_seqlens_cmp_kv'] is not None else None, | 44 | + cu_seqlens_cmp_kv=cu_seqlens_cmp_kv, |
| 45 | seqused_q=seqused_q, | 45 | seqused_q=seqused_q, |
| 46 | seqused_ori_kv=seqused_ori_kv, | 46 | seqused_ori_kv=seqused_ori_kv, |
| 47 | seqused_cmp_kv=seqused_cmp_kv, | 47 | seqused_cmp_kv=seqused_cmp_kv, |
| 48 | cmp_residual_kv=cmp_residual_kv, | 48 | cmp_residual_kv=cmp_residual_kv, |
| 49 | - ori_topk_length=None, | 49 | + ori_topk_length=ori_topk_length, |
| 50 | - cmp_topk_length=None, | 50 | + cmp_topk_length=cmp_topk_length, |
| 51 | sinks=sinks, | 51 | sinks=sinks, |
| 52 | metadata=metadata, | 52 | metadata=metadata, |
| 53 | quant_mode=quant_mode, | 53 | quant_mode=quant_mode, |
| @@ -66,7 +66,7 @@ class Network(torch.nn.Module): | |||
| 66 | value_dtype=None) | 66 | value_dtype=None) |
| 67 | return npu_result | 67 | return npu_result |
| 68 | 68 | ||
| 69 | -def test_qsmla_quant_process_graph(test_data, device_id=0): | 69 | +def test_mqsmla_quant_process_graph(test_data, device_id=0): |
| 70 | params = test_data['params'] | 70 | params = test_data['params'] |
| 71 | metadata_input = test_data['metadata_input'] | 71 | metadata_input = test_data['metadata_input'] |
| 72 | op_input = test_data['op_input'] | 72 | op_input = test_data['op_input'] |
| @@ -132,6 +132,8 @@ def test_qsmla_quant_process_graph(test_data, device_id=0): | |||
| 132 | cmp_block_table=op_input['cmp_block_table'].npu() if op_input['cmp_block_table'] is not None else None, | 132 | cmp_block_table=op_input['cmp_block_table'].npu() if op_input['cmp_block_table'] is not None else None, |
| 133 | cu_seqlens_q=op_input['cu_seqlens_q'].npu() if op_input['cu_seqlens_q'] is not None else None, | 133 | cu_seqlens_q=op_input['cu_seqlens_q'].npu() if op_input['cu_seqlens_q'] is not None else None, |
| 134 | seqused_q=op_input['seqused_q'].npu() if op_input['seqused_q'] is not None else torch.tensor([]).npu(), | 134 | seqused_q=op_input['seqused_q'].npu() if op_input['seqused_q'] is not None else torch.tensor([]).npu(), |
| 135 | + cu_seqlens_ori_kv=op_input['cu_seqlens_ori_kv'].npu() if op_input['cu_seqlens_ori_kv'] is not None else None, | ||
| 136 | + cu_seqlens_cmp_kv=op_input['cu_seqlens_cmp_kv'].npu() if op_input['cu_seqlens_cmp_kv'] is not None else None, | ||
| 135 | seqused_ori_kv=op_input['seqused_ori_kv'].npu() if op_input['seqused_ori_kv'] is not None else None, | 137 | seqused_ori_kv=op_input['seqused_ori_kv'].npu() if op_input['seqused_ori_kv'] is not None else None, |
| 136 | seqused_cmp_kv=op_input['seqused_cmp_kv'].npu() if op_input['seqused_cmp_kv'] is not None else None, | 138 | seqused_cmp_kv=op_input['seqused_cmp_kv'].npu() if op_input['seqused_cmp_kv'] is not None else None, |
| 137 | cmp_residual_kv=op_input['cmp_residual_kv'].npu() if op_input['cmp_residual_kv'] is not None else None, | 139 | cmp_residual_kv=op_input['cmp_residual_kv'].npu() if op_input['cmp_residual_kv'] is not None else None, |
| @@ -155,7 +157,7 @@ def test_qsmla_quant_process_graph(test_data, device_id=0): | |||
| 155 | torch.npu.synchronize() | 157 | torch.npu.synchronize() |
| 156 | return npu_result, cpu_output | 158 | return npu_result, cpu_output |
| 157 | 159 | ||
| 158 | -def test_qsmla_quant_process_ci(test_data, device_id=0): | 160 | +def test_mqsmla_quant_process_ci(test_data, device_id=0): |
| 159 | params = test_data['params'] | 161 | params = test_data['params'] |
| 160 | metadata_input = test_data['metadata_input'] | 162 | metadata_input = test_data['metadata_input'] |
| 161 | op_input = test_data['op_input'] | 163 | op_input = test_data['op_input'] |
Mattention/mixed_quant_sparse_flash_mla/tests/pytest/batch/test_mixed_quant_sparse_flash_mla_pt_save.py+6-6
| @@ -26,9 +26,9 @@ import argparse | |||
| 26 | import concurrent.futures | 26 | import concurrent.futures |
| 27 | 27 | ||
| 28 | # 读取表格(支持通过环境变量传入) | 28 | # 读取表格(支持通过环境变量传入) |
| 29 | -save_path = os.environ.get("QSMLA_PT_DIR", "qsmla_testcase") | 29 | +save_path = os.environ.get("MQSMLA_PT_DIR", "mqsmla_testcase") |
| 30 | -excel_file = os.environ.get("QSMLA_EXCEL", "./excel/example.xlsx") | 30 | +excel_file = os.environ.get("MQSMLA_EXCEL", "./excel/example.xlsx") |
| 31 | -sheet_name = os.environ.get("QSMLA_SHEET", "decode") | 31 | +sheet_name = os.environ.get("MQSMLA_SHEET", "decode") |
| 32 | ENABLED_PARAMS = utils.load_excel_test_cases(excel_file, sheet_name) | 32 | ENABLED_PARAMS = utils.load_excel_test_cases(excel_file, sheet_name) |
| 33 | 33 | ||
| 34 | param_combinations = [] | 34 | param_combinations = [] |
| @@ -108,7 +108,7 @@ for _, params in enumerate(ENABLED_PARAMS): | |||
| 108 | print(param_combinations) | 108 | print(param_combinations) |
| 109 | 109 | ||
| 110 | case_id = 0 | 110 | case_id = 0 |
| 111 | -def qsmla(param_combinations): | 111 | +def mqsmla(param_combinations): |
| 112 | global case_id | 112 | global case_id |
| 113 | params = utils.fill_none_params(param_combinations) | 113 | params = utils.fill_none_params(param_combinations) |
| 114 | 114 | ||
| @@ -118,7 +118,7 @@ def qsmla(param_combinations): | |||
| 118 | q_type_str = "BF16" if params['q_type'] == torch.bfloat16 else "FP16" | 118 | q_type_str = "BF16" if params['q_type'] == torch.bfloat16 else "FP16" |
| 119 | kv_type_str = "HIF8" if params['ori_kv_type'] == torch.uint8 else "FP8_E4M3FN" | 119 | kv_type_str = "HIF8" if params['ori_kv_type'] == torch.uint8 else "FP8_E4M3FN" |
| 120 | prefix_part = f"{param_combinations['tc_prefix']}_"if param_combinations.get('tc_prefix', '') else "" | 120 | prefix_part = f"{param_combinations['tc_prefix']}_"if param_combinations.get('tc_prefix', '') else "" |
| 121 | - Testcase_Name = f"QSMLA_{prefix_part}{params['template_run_mode']}_{ops_mode}_{params['layout_q']}_{q_type_str}_{params['layout_kv']}_{kv_type_str}_{params['B']}_{params['N1']}_{params['N2']}_{params['S1']}_{params['S2']}_{params['D']}_{params['K']}_{params['rope_head_dim']}_{case_id:06d}" | 121 | + Testcase_Name = f"MQSMLA_{prefix_part}{params['template_run_mode']}_{ops_mode}_{params['layout_q']}_{q_type_str}_{params['layout_kv']}_{kv_type_str}_{params['B']}_{params['N1']}_{params['N2']}_{params['S1']}_{params['S2']}_{params['D']}_{params['K']}_{params['rope_head_dim']}_{case_id:06d}" |
| 122 | params['Testcase_Name'] = Testcase_Name | 122 | params['Testcase_Name'] = Testcase_Name |
| 123 | print("input_params:", params) | 123 | print("input_params:", params) |
| 124 | 124 | ||
| @@ -137,7 +137,7 @@ def qsmla(param_combinations): | |||
| 137 | def test_mixed_quant_sparse_flash_mla(param_combinations): # 初始化参数和tensor | 137 | def test_mixed_quant_sparse_flash_mla(param_combinations): # 初始化参数和tensor |
| 138 | # 线程池 | 138 | # 线程池 |
| 139 | with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor: | 139 | with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor: |
| 140 | - futures = executor.submit(qsmla, param_combinations) | 140 | + futures = executor.submit(mqsmla, param_combinations) |
| 141 | # 等待并获取结果 | 141 | # 等待并获取结果 |
| 142 | for future in concurrent.futures.as_completed([futures]): | 142 | for future in concurrent.futures.as_completed([futures]): |
| 143 | try: | 143 | try: |
| @@ -41,7 +41,7 @@ TEST_PARAMS = { | |||
| 41 | "softmax_scale": [0.04419417], | 41 | "softmax_scale": [0.04419417], |
| 42 | "cmp_ratio": [1], | 42 | "cmp_ratio": [1], |
| 43 | "ori_mask_mode": [4], | 43 | "ori_mask_mode": [4], |
| 44 | - "cmp_mask_mode": [3], | 44 | + "cmp_mask_mode": [0], |
| 45 | "ori_win_left": [127], | 45 | "ori_win_left": [127], |
| 46 | "ori_win_right": [0], | 46 | "ori_win_right": [0], |
| 47 | "quant_mode": [1], | 47 | "quant_mode": [1], |
Mattention/mixed_quant_sparse_flash_mla/tests/pytest/test_mixed_quant_sparse_flash_mla_batch.py+35-14
| @@ -26,28 +26,49 @@ import check_valid_param | |||
| 26 | from batch import mixed_quant_sparse_flash_mla_process | 26 | from batch import mixed_quant_sparse_flash_mla_process |
| 27 | import utils | 27 | import utils |
| 28 | 28 | ||
| 29 | -testcase_path = os.environ.get("QSMLA_PT_DIR", "qsmla_testcase") | 29 | +testcase_path = os.environ.get("MQSMLA_PT_DIR", "mqsmla_testcase") |
| 30 | -result_path = Path('result.xlsx') | 30 | +batch_test_mode = int(os.environ.get("MQSMLA_BATCH_TEST_MODE", 0)) # 0:路径下全量批跑,1:按表格中case批跑 |
| 31 | -device_id=0 | 31 | +excel_path = os.environ.get("MQSMLA_EXCEL_PATH", os.path.join(os.path.dirname(__file__), "excel", "testcase.xlsx")) |
| 32 | +result_path = os.environ.get("MQSMLA_RESULT_SAVE_PATH", './mqsmla_result.xlsx') | ||
| 33 | +device_id = int(os.environ.get("MQSMLA_DEVICE_ID", 0)) | ||
| 32 | 34 | ||
| 33 | testcase_files = [] | 35 | testcase_files = [] |
| 34 | if os.path.isdir(testcase_path): | 36 | if os.path.isdir(testcase_path): |
| 35 | - pt_files = [f for f in os.listdir(testcase_path) if f.endswith('.pt')] | 37 | + if batch_test_mode == 1: |
| 36 | - if not pt_files: | 38 | + df = pd.read_excel(excel_path) |
| 37 | - print(f"错误: 目录中没有找到.pt文件: {testcase_path}") | 39 | + target_names = [str(name) for name in df['Testcase_Name'].dropna().tolist() if str(name) != 'None'] |
| 40 | + if not target_names: | ||
| 41 | + print(f"错误: 表格中没有有效的Testcase_Name: {excel_path}") | ||
| 42 | + else: | ||
| 43 | + print(f"从表格中读取到 {len(target_names)} 个目标用例名") | ||
| 44 | + pt_files = [f for f in os.listdir(testcase_path) if f.endswith('.pt')] | ||
| 45 | + for target_name in target_names: | ||
| 46 | + matched = [f for f in pt_files if target_name in f] | ||
| 47 | + if matched: | ||
| 48 | + for f in matched: | ||
| 49 | + filepath = os.path.join(testcase_path, f) | ||
| 50 | + if filepath not in testcase_files: | ||
| 51 | + testcase_files.append(filepath) | ||
| 52 | + else: | ||
| 53 | + print(f"警告: 用例名 '{target_name}' 未匹配到任何.pt文件") | ||
| 54 | + print(f"按表格筛选后共 {len(testcase_files)} 个测试用例文件") | ||
| 38 | else: | 55 | else: |
| 39 | - print(f"找到 {len(pt_files)} 个测试用例文件") | 56 | + pt_files = [f for f in os.listdir(testcase_path) if f.endswith('.pt')] |
| 40 | - for pt_file in pt_files: | 57 | + if not pt_files: |
| 41 | - filepath = os.path.join(testcase_path, pt_file) | 58 | + print(f"错误: 目录中没有找到.pt文件: {testcase_path}") |
| 42 | - testcase_files.append(filepath) | 59 | + else: |
| 60 | + print(f"找到 {len(pt_files)} 个测试用例文件") | ||
| 61 | + for pt_file in pt_files: | ||
| 62 | + filepath = os.path.join(testcase_path, pt_file) | ||
| 63 | + testcase_files.append(filepath) | ||
| 43 | else: | 64 | else: |
| 44 | print(f"错误: 输出目录不存在: {testcase_path}") | 65 | print(f"错误: 输出目录不存在: {testcase_path}") |
| 45 | 66 | ||
| 46 | -def qsmla(testcase_files): | 67 | +def mqsmla(testcase_files): |
| 47 | test_data = torch.load(testcase_files, map_location="cpu") | 68 | test_data = torch.load(testcase_files, map_location="cpu") |
| 48 | npu_error_msg = None | 69 | npu_error_msg = None |
| 49 | try: | 70 | try: |
| 50 | - npu_result, cpu_quant_result = mixed_quant_sparse_flash_mla_process.test_qsmla_quant_process_ci( | 71 | + npu_result, cpu_quant_result = mixed_quant_sparse_flash_mla_process.test_mqsmla_quant_process_ci( |
| 51 | test_data, device_id=device_id) | 72 | test_data, device_id=device_id) |
| 52 | result, fulfill_percent = result_compare_method.check_result(cpu_quant_result, npu_result) | 73 | result, fulfill_percent = result_compare_method.check_result(cpu_quant_result, npu_result) |
| 53 | except Exception as e: | 74 | except Exception as e: |
| @@ -56,7 +77,7 @@ def qsmla(testcase_files): | |||
| 56 | result = "NPU ERROR" | 77 | result = "NPU ERROR" |
| 57 | fulfill_percent = 0 | 78 | fulfill_percent = 0 |
| 58 | 79 | ||
| 59 | - utils.save_result(test_data['params'], result, fulfill_percent, result_path) | 80 | + utils.save_result(test_data['params'], result, fulfill_percent, Path(result_path)) |
| 60 | 81 | ||
| 61 | if result == "Failed": | 82 | if result == "Failed": |
| 62 | pytest.fail(f"用例精度失败:{test_data['Testcase_Name']} 精度:{fulfill_percent:.2f}%") | 83 | pytest.fail(f"用例精度失败:{test_data['Testcase_Name']} 精度:{fulfill_percent:.2f}%") |
| @@ -70,7 +91,7 @@ testcase_ids = [os.path.splitext(os.path.basename(f))[0] for f in testcase_files | |||
| 70 | def test_mixed_quant_sparse_flash_mla(testcase_files): | 91 | def test_mixed_quant_sparse_flash_mla(testcase_files): |
| 71 | # 线程池 | 92 | # 线程池 |
| 72 | with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor: | 93 | with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor: |
| 73 | - futures = executor.submit(qsmla, testcase_files) | 94 | + futures = executor.submit(mqsmla, testcase_files) |
| 74 | # 等待并获取结果 | 95 | # 等待并获取结果 |
| 75 | for future in concurrent.futures.as_completed([futures]): | 96 | for future in concurrent.futures.as_completed([futures]): |
| 76 | try: | 97 | try: |
Mattention/mixed_quant_sparse_flash_mla/tests/pytest/test_mixed_quant_sparse_flash_mla_batch_graph.py+35-14
| @@ -26,28 +26,49 @@ import check_valid_param | |||
| 26 | from batch import mixed_quant_sparse_flash_mla_process | 26 | from batch import mixed_quant_sparse_flash_mla_process |
| 27 | import utils | 27 | import utils |
| 28 | 28 | ||
| 29 | -testcase_path = "qsmla_testcase" | 29 | +testcase_path = os.environ.get("MQSMLA_PT_DIR", "mqsmla_testcase") |
| 30 | -result_path = Path('result.xlsx') # 或使用传入的result_path | 30 | +batch_test_mode = int(os.environ.get("MQSMLA_BATCH_TEST_MODE", 0)) # 0:路径下全量批跑,1:按表格中case批跑 |
| 31 | -device_id=0 | 31 | +excel_path = os.environ.get("MQSMLA_EXCEL_PATH", os.path.join(os.path.dirname(__file__), "excel", "testcase.xlsx")) |
| 32 | +result_path = os.environ.get("MQSMLA_RESULT_SAVE_PATH", './mqsmla_result.xlsx') | ||
| 33 | +device_id = int(os.environ.get("MQSMLA_DEVICE_ID", 0)) | ||
| 32 | 34 | ||
| 33 | locals()["testcase_files"] = [] | 35 | locals()["testcase_files"] = [] |
| 34 | if os.path.isdir(testcase_path): | 36 | if os.path.isdir(testcase_path): |
| 35 | - pt_files = [f for f in os.listdir(testcase_path) if f.endswith('.pt')] | 37 | + if batch_test_mode == 1: |
| 36 | - if not pt_files: | 38 | + df = pd.read_excel(excel_path) |
| 37 | - print(f"错误: 目录中没有找到.pt文件: {testcase_path}") | 39 | + target_names = [str(name) for name in df['Testcase_Name'].dropna().tolist() if str(name) != 'None'] |
| 40 | + if not target_names: | ||
| 41 | + print(f"错误: 表格中没有有效的Testcase_Name: {excel_path}") | ||
| 42 | + else: | ||
| 43 | + print(f"从表格中读取到 {len(target_names)} 个目标用例名") | ||
| 44 | + pt_files = [f for f in os.listdir(testcase_path) if f.endswith('.pt')] | ||
| 45 | + for target_name in target_names: | ||
| 46 | + matched = [f for f in pt_files if target_name in f] | ||
| 47 | + if matched: | ||
| 48 | + for f in matched: | ||
| 49 | + filepath = os.path.join(testcase_path, f) | ||
| 50 | + if filepath not in locals()["testcase_files"]: | ||
| 51 | + locals()["testcase_files"].append(filepath) | ||
| 52 | + else: | ||
| 53 | + print(f"警告: 用例名 '{target_name}' 未匹配到任何.pt文件") | ||
| 54 | + print(f"按表格筛选后共 {len(locals()['testcase_files'])} 个测试用例文件") | ||
| 38 | else: | 55 | else: |
| 39 | - print(f"找到 {len(pt_files)} 个测试用例文件") | 56 | + pt_files = [f for f in os.listdir(testcase_path) if f.endswith('.pt')] |
| 40 | - for pt_file in pt_files: | 57 | + if not pt_files: |
| 41 | - filepath = os.path.join(testcase_path, pt_file) | 58 | + print(f"错误: 目录中没有找到.pt文件: {testcase_path}") |
| 42 | - locals()["testcase_files"].append(filepath) | 59 | + else: |
| 60 | + print(f"找到 {len(pt_files)} 个测试用例文件") | ||
| 61 | + for pt_file in pt_files: | ||
| 62 | + filepath = os.path.join(testcase_path, pt_file) | ||
| 63 | + locals()["testcase_files"].append(filepath) | ||
| 43 | else: | 64 | else: |
| 44 | print(f"错误: 输出目录不存在: {testcase_path}") | 65 | print(f"错误: 输出目录不存在: {testcase_path}") |
| 45 | 66 | ||
| 46 | -def qsmla_aclgraph(testcase_files): | 67 | +def mqsmla_aclgraph(testcase_files): |
| 47 | test_data = torch.load(testcase_files, map_location="cpu") | 68 | test_data = torch.load(testcase_files, map_location="cpu") |
| 48 | npu_error_msg = None | 69 | npu_error_msg = None |
| 49 | try: | 70 | try: |
| 50 | - npu_result, cpu_quant_result = mixed_quant_sparse_flash_mla_process.test_qsmla_quant_process_graph( | 71 | + npu_result, cpu_quant_result = mixed_quant_sparse_flash_mla_process.test_mqsmla_quant_process_graph( |
| 51 | test_data, device_id=device_id) | 72 | test_data, device_id=device_id) |
| 52 | result, fulfill_percent = result_compare_method.check_result(cpu_quant_result, npu_result) | 73 | result, fulfill_percent = result_compare_method.check_result(cpu_quant_result, npu_result) |
| 53 | except Exception as e: | 74 | except Exception as e: |
| @@ -56,7 +77,7 @@ def qsmla_aclgraph(testcase_files): | |||
| 56 | result = "NPU ERROR" | 77 | result = "NPU ERROR" |
| 57 | fulfill_percent = 0 | 78 | fulfill_percent = 0 |
| 58 | 79 | ||
| 59 | - utils.save_result(test_data['params'], result, fulfill_percent, result_path) | 80 | + utils.save_result(test_data['params'], result, fulfill_percent, Path(result_path)) |
| 60 | 81 | ||
| 61 | if result == "Failed": | 82 | if result == "Failed": |
| 62 | pytest.fail(f"用例精度失败:{test_data['Testcase_Name']} 精度:{fulfill_percent:.2f}%") | 83 | pytest.fail(f"用例精度失败:{test_data['Testcase_Name']} 精度:{fulfill_percent:.2f}%") |
| @@ -70,7 +91,7 @@ testcase_ids = [os.path.splitext(os.path.basename(f))[0] for f in locals()["test | |||
| 70 | def test_mixed_quant_sparse_flash_mla(testcase_files): | 91 | def test_mixed_quant_sparse_flash_mla(testcase_files): |
| 71 | # 线程池 | 92 | # 线程池 |
| 72 | with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor: | 93 | with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor: |
| 73 | - futures = executor.submit(qsmla_aclgraph, testcase_files) | 94 | + futures = executor.submit(mqsmla_aclgraph, testcase_files) |
| 74 | # 等待并获取结果 | 95 | # 等待并获取结果 |
| 75 | for future in concurrent.futures.as_completed([futures]): | 96 | for future in concurrent.futures.as_completed([futures]): |
| 76 | try: | 97 | try: |
Mattention/mixed_quant_sparse_flash_mla/tests/pytest/test_mixed_quant_sparse_flash_mla_single.py+4-4
| @@ -26,7 +26,7 @@ import multiprocessing as mp | |||
| 26 | import concurrent.futures | 26 | import concurrent.futures |
| 27 | import utils | 27 | import utils |
| 28 | 28 | ||
| 29 | -pt_save_path = "qsmla_testcase" | 29 | +pt_save_path = "mqsmla_testcase" |
| 30 | device_id = 0 | 30 | device_id = 0 |
| 31 | save_pt = False | 31 | save_pt = False |
| 32 | result_path = Path('result.xlsx') | 32 | result_path = Path('result.xlsx') |
| @@ -80,7 +80,7 @@ for params in ENABLED_PARAMS: | |||
| 80 | param_combinations.append(combination) | 80 | param_combinations.append(combination) |
| 81 | 81 | ||
| 82 | case_id = 0 | 82 | case_id = 0 |
| 83 | -def qsmla(param_combinations): | 83 | +def mqsmla(param_combinations): |
| 84 | global case_id | 84 | global case_id |
| 85 | 85 | ||
| 86 | # 填充None参数的默认值 | 86 | # 填充None参数的默认值 |
| @@ -108,7 +108,7 @@ def qsmla(param_combinations): | |||
| 108 | # 获得cpu结果(真值)和算子结果(测试值) | 108 | # 获得cpu结果(真值)和算子结果(测试值) |
| 109 | npu_error_msg = None | 109 | npu_error_msg = None |
| 110 | try: | 110 | try: |
| 111 | - npu_result, cpu_quant_result = mixed_quant_sparse_flash_mla_process.test_qsmla_quant_process_ci( | 111 | + npu_result, cpu_quant_result = mixed_quant_sparse_flash_mla_process.test_mqsmla_quant_process_ci( |
| 112 | test_data, device_id=device_id) | 112 | test_data, device_id=device_id) |
| 113 | result, fulfill_percent = result_compare_method.check_result(cpu_quant_result, npu_result) | 113 | result, fulfill_percent = result_compare_method.check_result(cpu_quant_result, npu_result) |
| 114 | except Exception as e: | 114 | except Exception as e: |
| @@ -142,7 +142,7 @@ testcase_ids = [_gen_testcase_id(p, i) for i, p in enumerate(param_combinations) | |||
| 142 | def test_mixed_quant_sparse_flash_mla(param_combinations): | 142 | def test_mixed_quant_sparse_flash_mla(param_combinations): |
| 143 | # 线程池 | 143 | # 线程池 |
| 144 | with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor: | 144 | with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor: |
| 145 | - futures = executor.submit(qsmla, param_combinations) | 145 | + futures = executor.submit(mqsmla, param_combinations) |
| 146 | # 等待并获取结果 | 146 | # 等待并获取结果 |
| 147 | for future in concurrent.futures.as_completed([futures]): | 147 | for future in concurrent.futures.as_completed([futures]): |
| 148 | try: | 148 | try: |
| @@ -11,34 +11,34 @@ | |||
| 11 | 11 | ||
| 12 | set -euo pipefail | 12 | set -euo pipefail |
| 13 | 13 | ||
| 14 | -# 脚本路径 | ||
| 15 | QSMLA_PT_SAVE_SCRIPT="./batch/test_mixed_quant_sparse_flash_mla_pt_save.py" | 14 | QSMLA_PT_SAVE_SCRIPT="./batch/test_mixed_quant_sparse_flash_mla_pt_save.py" |
| 16 | TEST_QSMLA_PT_BATCH_SCRIPT="test_mixed_quant_sparse_flash_mla_batch.py" | 15 | TEST_QSMLA_PT_BATCH_SCRIPT="test_mixed_quant_sparse_flash_mla_batch.py" |
| 16 | +TEST_QSMLA_PT_BATCH_GRAPH_SCRIPT="test_mixed_quant_sparse_flash_mla_batch_graph.py" | ||
| 17 | TEST_QSMLA_SINGLE_SCRIPT="test_mixed_quant_sparse_flash_mla_single.py" | 17 | TEST_QSMLA_SINGLE_SCRIPT="test_mixed_quant_sparse_flash_mla_single.py" |
| 18 | 18 | ||
| 19 | -# 默认参数 | 19 | +PT_SAVE_DIR="mqsmla_testcase" |
| 20 | -PT_SAVE_DIR="qsmla_testcase" | ||
| 21 | EXCEL_FILE="./excel/example.xlsx" | 20 | EXCEL_FILE="./excel/example.xlsx" |
| 22 | SHEET_NAME="decode" | 21 | SHEET_NAME="decode" |
| 23 | KEEP_PT=false | 22 | KEEP_PT=false |
| 23 | +BATCH_TEST_MODE=1 | ||
| 24 | +RESULT_PATH="./mqsmla_result.xlsx" | ||
| 25 | +DEVICE_ID=0 | ||
| 24 | 26 | ||
| 25 | # ====================== 执行区 ====================== | 27 | # ====================== 执行区 ====================== |
| 26 | 28 | ||
| 27 | -# 单用例算子调测 | ||
| 28 | run_single() { | 29 | run_single() { |
| 29 | echo "===== 执行单用例算子调测 =====" | 30 | echo "===== 执行单用例算子调测 =====" |
| 30 | python3 -m pytest -rA -s $TEST_QSMLA_SINGLE_SCRIPT -v -m ci -W ignore::UserWarning -W ignore::DeprecationWarning | 31 | python3 -m pytest -rA -s $TEST_QSMLA_SINGLE_SCRIPT -v -m ci -W ignore::UserWarning -W ignore::DeprecationWarning |
| 31 | } | 32 | } |
| 32 | 33 | ||
| 33 | -# 批量从excel读取用例、golden计算并保存pt文件 | ||
| 34 | run_batch_save() { | 34 | run_batch_save() { |
| 35 | echo "===== 执行batch_save:从excel读取用例并保存pt文件 =====" | 35 | echo "===== 执行batch_save:从excel读取用例并保存pt文件 =====" |
| 36 | echo " excel: $EXCEL_FILE" | 36 | echo " excel: $EXCEL_FILE" |
| 37 | echo " sheet: $SHEET_NAME" | 37 | echo " sheet: $SHEET_NAME" |
| 38 | echo " pt目录: $PT_SAVE_DIR" | 38 | echo " pt目录: $PT_SAVE_DIR" |
| 39 | - export QSMLA_EXCEL="$EXCEL_FILE" | 39 | + export MQSMLA_EXCEL="$EXCEL_FILE" |
| 40 | - export QSMLA_SHEET="$SHEET_NAME" | 40 | + export MQSMLA_SHEET="$SHEET_NAME" |
| 41 | - export QSMLA_PT_DIR="$PT_SAVE_DIR" | 41 | + export MQSMLA_PT_DIR="$PT_SAVE_DIR" |
| 42 | python3 -m pytest -rA -s $QSMLA_PT_SAVE_SCRIPT -v -m ci -W ignore::UserWarning -W ignore::DeprecationWarning | 42 | python3 -m pytest -rA -s $QSMLA_PT_SAVE_SCRIPT -v -m ci -W ignore::UserWarning -W ignore::DeprecationWarning |
| 43 | if [ $? -ne 0 ]; then | 43 | if [ $? -ne 0 ]; then |
| 44 | echo "batch_save 执行失败,退出" | 44 | echo "batch_save 执行失败,退出" |
| @@ -47,7 +47,6 @@ run_batch_save() { | |||
| 47 | echo -e "\n===== batch_save 完成!pt文件保存在 $PT_SAVE_DIR 目录 =====" | 47 | echo -e "\n===== batch_save 完成!pt文件保存在 $PT_SAVE_DIR 目录 =====" |
| 48 | } | 48 | } |
| 49 | 49 | ||
| 50 | -# 批量读取pt文件并执行NPU测试 | ||
| 51 | run_batch_exec() { | 50 | run_batch_exec() { |
| 52 | echo "===== 执行batch_exec:读取pt文件并执行NPU测试 =====" | 51 | echo "===== 执行batch_exec:读取pt文件并执行NPU测试 =====" |
| 53 | 52 | ||
| @@ -63,7 +62,11 @@ run_batch_exec() { | |||
| 63 | fi | 62 | fi |
| 64 | 63 | ||
| 65 | echo "找到 $pt_count 个pt文件,开始执行NPU测试" | 64 | echo "找到 $pt_count 个pt文件,开始执行NPU测试" |
| 66 | - export QSMLA_PT_DIR="$PT_SAVE_DIR" | 65 | + export MQSMLA_PT_DIR="$PT_SAVE_DIR" |
| 66 | + export MQSMLA_BATCH_TEST_MODE="$BATCH_TEST_MODE" | ||
| 67 | + export MQSMLA_EXCEL_PATH="$EXCEL_FILE" | ||
| 68 | + export MQSMLA_RESULT_SAVE_PATH="$RESULT_PATH" | ||
| 69 | + export MQSMLA_DEVICE_ID="$DEVICE_ID" | ||
| 67 | python3 -m pytest -rA -s $TEST_QSMLA_PT_BATCH_SCRIPT -v -m ci -W ignore::UserWarning -W ignore::DeprecationWarning | 70 | python3 -m pytest -rA -s $TEST_QSMLA_PT_BATCH_SCRIPT -v -m ci -W ignore::UserWarning -W ignore::DeprecationWarning |
| 68 | if [ $? -ne 0 ]; then | 71 | if [ $? -ne 0 ]; then |
| 69 | echo "batch_exec 执行失败" | 72 | echo "batch_exec 执行失败" |
| @@ -73,7 +76,35 @@ run_batch_exec() { | |||
| 73 | echo -e "\n===== batch_exec 完成!=====" | 76 | echo -e "\n===== batch_exec 完成!=====" |
| 74 | } | 77 | } |
| 75 | 78 | ||
| 76 | -# 批量从excel读取用例、生成pt文件并执行NPU测试 | 79 | +run_batch_exec_graph() { |
| 80 | + echo "===== 执行batch_exec_graph:读取pt文件并执行Graph模式NPU测试 =====" | ||
| 81 | + | ||
| 82 | + if [ ! -d "$PT_SAVE_DIR" ]; then | ||
| 83 | + echo "错误: pt文件目录不存在: $PT_SAVE_DIR,请先执行 batch_save 生成pt文件" | ||
| 84 | + exit 1 | ||
| 85 | + fi | ||
| 86 | + | ||
| 87 | + pt_count=$(ls -1 $PT_SAVE_DIR/*.pt 2>/dev/null | wc -l) | ||
| 88 | + if [ $pt_count -eq 0 ]; then | ||
| 89 | + echo "错误: 目录中没有找到.pt文件: $PT_SAVE_DIR,请先执行 batch_save 生成pt文件" | ||
| 90 | + exit 1 | ||
| 91 | + fi | ||
| 92 | + | ||
| 93 | + echo "找到 $pt_count 个pt文件,开始执行Graph模式NPU测试" | ||
| 94 | + export MQSMLA_PT_DIR="$PT_SAVE_DIR" | ||
| 95 | + export MQSMLA_BATCH_TEST_MODE="$BATCH_TEST_MODE" | ||
| 96 | + export MQSMLA_EXCEL_PATH="$EXCEL_FILE" | ||
| 97 | + export MQSMLA_RESULT_SAVE_PATH="$RESULT_PATH" | ||
| 98 | + export MQSMLA_DEVICE_ID="$DEVICE_ID" | ||
| 99 | + python3 -m pytest -rA -s $TEST_QSMLA_PT_BATCH_GRAPH_SCRIPT -v -m graph -W ignore::UserWarning -W ignore::DeprecationWarning | ||
| 100 | + if [ $? -ne 0 ]; then | ||
| 101 | + echo "batch_exec_graph 执行失败" | ||
| 102 | + exit 1 | ||
| 103 | + fi | ||
| 104 | + | ||
| 105 | + echo -e "\n===== batch_exec_graph 完成!=====" | ||
| 106 | +} | ||
| 107 | + | ||
| 77 | run_batch() { | 108 | run_batch() { |
| 78 | echo "===== 执行batch:从excel批量生成pt并执行NPU测试 =====" | 109 | echo "===== 执行batch:从excel批量生成pt并执行NPU测试 =====" |
| 79 | 110 | ||
| @@ -89,10 +120,9 @@ run_batch() { | |||
| 89 | exit 1 | 120 | exit 1 |
| 90 | fi | 121 | fi |
| 91 | 122 | ||
| 92 | - # 根据KEEP_PT决定是否清理pt文件 | ||
| 93 | if [ "$KEEP_PT" = false ]; then | 123 | if [ "$KEEP_PT" = false ]; then |
| 94 | echo -e "\n===== 清理pt文件(KEEP_PT=false) =====" | 124 | echo -e "\n===== 清理pt文件(KEEP_PT=false) =====" |
| 95 | - [ -n "$PT_SAVE_DIR"] && rm -rf $PT_SAVE_DIR | 125 | + [ -n "$PT_SAVE_DIR" ] && rm -rf $PT_SAVE_DIR |
| 96 | echo "pt文件已清理" | 126 | echo "pt文件已清理" |
| 97 | else | 127 | else |
| 98 | echo -e "\n===== 保留pt文件(KEEP_PT=true)保存在 $PT_SAVE_DIR =====" | 128 | echo -e "\n===== 保留pt文件(KEEP_PT=true)保存在 $PT_SAVE_DIR =====" |
| @@ -101,37 +131,49 @@ run_batch() { | |||
| 101 | echo -e "\n===== batch 全流程执行完成!=====" | 131 | echo -e "\n===== batch 全流程执行完成!=====" |
| 102 | } | 132 | } |
| 103 | 133 | ||
| 104 | -# 显示帮助信息 | ||
| 105 | show_help() { | 134 | show_help() { |
| 106 | echo "用法: $0 <命令> [选项]" | 135 | echo "用法: $0 <命令> [选项]" |
| 107 | echo "" | 136 | echo "" |
| 108 | echo "命令说明:" | 137 | echo "命令说明:" |
| 109 | - echo " single 执行单算子用例调测" | 138 | + echo " single 执行单算子用例调测" |
| 110 | - echo " batch_save 从excel读取用例,golden计算并保存pt文件" | 139 | + echo " batch_save 从excel读取用例,golden计算并保存pt文件" |
| 111 | - echo " batch_exec 批量读取pt文件并执行NPU测试" | 140 | + echo " batch_exec 批量读取pt文件并执行NPU测试(CI模式)" |
| 112 | - echo " batch 全流程:从excel生成pt + NPU测试" | 141 | + echo " batch_exec_graph 批量读取pt文件并执行Graph模式NPU测试" |
| 113 | - echo " help 显示本帮助信息" | 142 | + echo " batch 全流程:从excel生成pt + NPU测试" |
| 143 | + echo " help 显示本帮助信息" | ||
| 114 | echo "" | 144 | echo "" |
| 115 | - echo "选项(batch_save/batch_exec/batch 命令支持):" | 145 | + echo "选项(batch_save/batch_exec/batch_exec_graph/batch 命令支持):" |
| 116 | - echo " --excel <路径> 指定excel文件路径(默认: ./excel/example.xlsx)" | 146 | + echo " --excel <路径> 指定excel文件路径(默认: ./excel/example.xlsx)" |
| 117 | - echo " --sheet <名称> 指定excel sheet名(默认: decode)" | 147 | + echo " --sheet <名称> 指定excel sheet名(默认: decode)" |
| 118 | - echo " --pt-dir <目录> 指定pt文件保存/读取目录(默认: qsmla_testcase)" | 148 | + echo " --pt-dir <目录> 指定pt文件保存/读取目录(默认: mqsmla_testcase)" |
| 119 | - echo " --keep-pt 执行完成后保留pt文件(默认清理,仅batch命令)" | 149 | + echo " --result <路径> 指定结果保存路径(默认: ./mqsmla_result.xlsx)" |
| 150 | + echo " --device-id <id> 指定NPU设备ID(默认: 0)" | ||
| 151 | + echo " --mode <0|1> 批跑模式: 0=全量批跑(默认), 1=按表格中case批跑" | ||
| 152 | + echo " --keep-pt 执行完成后保留pt文件(默认清理,仅batch命令)" | ||
| 153 | + echo "" | ||
| 154 | + echo "环境变量(.py文件读取):" | ||
| 155 | + echo " MQSMLA_PT_DIR pt文件目录(batch_save/batch_exec/batch_exec_graph)" | ||
| 156 | + echo " MQSMLA_EXCEL excel文件路径(batch_save)" | ||
| 157 | + echo " MQSMLA_SHEET excel sheet名(batch_save)" | ||
| 158 | + echo " MQSMLA_EXCEL_PATH excel文件路径(batch_exec/batch_exec_graph,mode=1时使用)" | ||
| 159 | + echo " MQSMLA_BATCH_TEST_MODE 批跑模式: 0=全量, 1=按表格(batch_exec/batch_exec_graph)" | ||
| 160 | + echo " MQSMLA_RESULT_SAVE_PATH 结果保存路径(batch_exec/batch_exec_graph)" | ||
| 161 | + echo " MQSMLA_DEVICE_ID NPU设备ID(batch_exec/batch_exec_graph)" | ||
| 120 | echo "" | 162 | echo "" |
| 121 | echo "示例:" | 163 | echo "示例:" |
| 122 | - echo " $0 single # 执行single模式" | 164 | + echo " $0 single" |
| 123 | - echo " $0 batch_save # 用默认参数生成pt文件" | 165 | + echo " $0 batch_save" |
| 124 | - echo " $0 batch_save --excel my.xlsx --sheet prefill --pt-dir my_pt" | 166 | + echo " $0 batch_save --excel my.xlsx --sheet decode --pt-dir my_pt" |
| 125 | - echo " $0 batch_exec # 用默认pt目录执行NPU测试" | 167 | + echo " $0 batch_exec # 全量批跑" |
| 126 | - echo " $0 batch_exec --pt-dir my_pt # 从指定目录读取pt文件" | 168 | + echo " $0 batch_exec --mode 1 --excel ./excel/testcase.xlsx # 按表格批跑" |
| 127 | - echo " $0 batch # 全流程,完成后清理pt文件" | 169 | + echo " $0 batch_exec --pt-dir my_pt --result ./my_result.xlsx" |
| 128 | - echo " $0 batch --keep-pt # 全流程,完成后保留pt文件" | 170 | + echo " $0 batch_exec_graph --mode 1 # Graph模式按表格批跑" |
| 129 | - echo " $0 batch --excel my.xlsx --sheet prefill --pt-dir my_pt --keep-pt" | 171 | + echo " $0 batch --keep-pt" |
| 172 | + echo " $0 batch --excel my.xlsx --sheet decode --pt-dir my_pt --mode 1 --keep-pt" | ||
| 130 | } | 173 | } |
| 131 | 174 | ||
| 132 | # ====================== 主逻辑 ====================== | 175 | # ====================== 主逻辑 ====================== |
| 133 | 176 | ||
| 134 | -# 解析参数 | ||
| 135 | if [ $# -lt 1 ]; then | 177 | if [ $# -lt 1 ]; then |
| 136 | echo "错误:必须传入至少一个命令参数" | 178 | echo "错误:必须传入至少一个命令参数" |
| 137 | show_help | 179 | show_help |
| @@ -141,8 +183,7 @@ fi | |||
| 141 | COMMAND="$1" | 183 | COMMAND="$1" |
| 142 | shift | 184 | shift |
| 143 | 185 | ||
| 144 | -# 解析选项(batch_save/batch_exec/batch 共用) | 186 | +if [ "$COMMAND" = "batch_save" ] || [ "$COMMAND" = "batch_exec" ] || [ "$COMMAND" = "batch_exec_graph" ] || [ "$COMMAND" = "batch" ]; then |
| 145 | -if [ "$COMMAND" = "batch_save" ] || [ "$COMMAND" = "batch_exec" ] || [ "$COMMAND" = "batch" ]; then | ||
| 146 | while [ $# -gt 0 ]; do | 187 | while [ $# -gt 0 ]; do |
| 147 | case "$1" in | 188 | case "$1" in |
| 148 | --excel) | 189 | --excel) |
| @@ -169,6 +210,30 @@ if [ "$COMMAND" = "batch_save" ] || [ "$COMMAND" = "batch_exec" ] || [ "$COMMAND | |||
| 169 | PT_SAVE_DIR="$2" | 210 | PT_SAVE_DIR="$2" |
| 170 | shift 2 | 211 | shift 2 |
| 171 | ;; | 212 | ;; |
| 213 | + --result) | ||
| 214 | + if [ $# -lt 2 ]; then | ||
| 215 | + echo "错误:--result 需要参数值" | ||
| 216 | + exit 1 | ||
| 217 | + fi | ||
| 218 | + RESULT_PATH="$2" | ||
| 219 | + shift 2 | ||
| 220 | + ;; | ||
| 221 | + --device-id) | ||
| 222 | + if [ $# -lt 2 ]; then | ||
| 223 | + echo "错误:--device-id 需要参数值" | ||
| 224 | + exit 1 | ||
| 225 | + fi | ||
| 226 | + DEVICE_ID="$2" | ||
| 227 | + shift 2 | ||
| 228 | + ;; | ||
| 229 | + --mode) | ||
| 230 | + if [ $# -lt 2 ]; then | ||
| 231 | + echo "错误:--mode 需要参数值" | ||
| 232 | + exit 1 | ||
| 233 | + fi | ||
| 234 | + BATCH_TEST_MODE="$2" | ||
| 235 | + shift 2 | ||
| 236 | + ;; | ||
| 172 | --keep-pt) | 237 | --keep-pt) |
| 173 | if [ "$COMMAND" != "batch" ]; then | 238 | if [ "$COMMAND" != "batch" ]; then |
| 174 | echo "错误:--keep-pt 仅适用于 batch 命令" | 239 | echo "错误:--keep-pt 仅适用于 batch 命令" |
| @@ -186,7 +251,6 @@ if [ "$COMMAND" = "batch_save" ] || [ "$COMMAND" = "batch_exec" ] || [ "$COMMAND | |||
| 186 | done | 251 | done |
| 187 | fi | 252 | fi |
| 188 | 253 | ||
| 189 | -# 根据命令执行对应函数 | ||
| 190 | case "$COMMAND" in | 254 | case "$COMMAND" in |
| 191 | single) | 255 | single) |
| 192 | run_single | 256 | run_single |
| @@ -197,6 +261,9 @@ case "$COMMAND" in | |||
| 197 | batch_exec) | 261 | batch_exec) |
| 198 | run_batch_exec | 262 | run_batch_exec |
| 199 | ;; | 263 | ;; |
| 264 | + batch_exec_graph) | ||
| 265 | + run_batch_exec_graph | ||
| 266 | + ;; | ||
| 200 | batch) | 267 | batch) |
| 201 | run_batch | 268 | run_batch |
| 202 | ;; | 269 | ;; |