diff --git a/mindspore-lite/src/common/common.h b/mindspore-lite/src/common/common.h index cd229acd9ececf71819882bd1c7cde51ba60f708..8d79bbdb7476041880d9f95ef6531ad7ef848a26 100644 --- a/mindspore-lite/src/common/common.h +++ b/mindspore-lite/src/common/common.h @@ -143,6 +143,12 @@ static const char *const kAclInitOptionParam = "acl_init_options"; static const char *const kAclBuildOptionParam = "acl_build_options"; static const char *const kSplitGraph = "SplitGraph"; +// experimental section +static const char *const kExperimentalSection = "experimental"; +static const char *const kCompileGraphParallel = "compile_graph_parallel"; +static const char *const kEnableValue = "on"; +static const char *const kDisableValue = "off"; + static const char *const kNameAttrWeightDir = "weight_dir"; static const char *const kOutputShapes = "outputs_shape"; diff --git a/mindspore-lite/src/extendrt/session/delegate_session.cc b/mindspore-lite/src/extendrt/session/delegate_session.cc index 579efa3a9b923da4148d9a3aa77920c9b340b485..eeff504929b3d83a7d80e2aa591234516830dfc2 100644 --- a/mindspore-lite/src/extendrt/session/delegate_session.cc +++ b/mindspore-lite/src/extendrt/session/delegate_session.cc @@ -50,9 +50,25 @@ Status GraphSinkSession::Init(const std::shared_ptr &context, const Con return kSuccess; } +bool CheckCompileGraphParallel(const std::string &option) { + if (option.empty() || option == lite::kDisableValue) { + return false; + } + if (option == lite::kEnableValue) { + return true; + } + MS_LOG(WARNING) << "compile_graph_parallel=off is default, please set " + "compile_graph_parallel=on to enable compile graph in parallel between different threads. got: " + << option << ", fallback to default."; + return false; +} + Status GraphSinkSession::CompileGraph(const void *model_data, size_t data_size, uint32_t *graph_id) { // This lock can be removed when LiteRT supports concurrent multithreading compilation. - std::lock_guard lock(g_build_graph_mutex); + std::unique_lock lock(g_build_graph_mutex); + if (CheckCompileGraphParallel(GetConfigOption(lite::kExperimentalSection, lite::kCompileGraphParallel))) { + lock.unlock(); + } auto ret = graph_executor_->CompileGraph(model_data, data_size, options_, graph_id); if (!ret) { MS_LOG(ERROR) << "GraphSinkSession::CompileGraph compile graph failed"; @@ -72,7 +88,7 @@ Status GraphSinkSession::CompileGraph(const void *model_data, size_t data_size, Status GraphSinkSession::CompileGraph(FuncGraphPtr graph, const void *data, size_t size, uint32_t *graph_id) { MS_LOG(INFO) << "GraphSinkSession::CompileGraph"; // This lock can be removed when LiteRT supports concurrent multithreading compilation. - std::lock_guard lock(g_build_graph_mutex); + std::unique_lock lock(g_build_graph_mutex); // kernel graph will be removed from GraphSinkSession, and this code will be moved to TensorRT plugin if (context_ && !context_->MutableDeviceInfo().empty()) { auto device_info = context_->MutableDeviceInfo()[0]; @@ -84,6 +100,11 @@ Status GraphSinkSession::CompileGraph(FuncGraphPtr graph, const void *data, size graph->set_attr(kIsAdapted, MakeValue(true)); } } + // Ensure always protect AscendGeExecutorPlugin. It's a singleton without lock. + // The following parts can be parallelized. + if (CheckCompileGraphParallel(GetConfigOption(lite::kExperimentalSection, lite::kCompileGraphParallel))) { + lock.unlock(); + } DelegateGraphInfo graph_info; // the funcgraph constructed by flowgraph has no inputs and outputs. auto status = InitGraphInputsOutputs(graph, &graph_info); diff --git a/mindspore-lite/test/st/python/pytest/conftest.py b/mindspore-lite/test/st/python/pytest/conftest.py new file mode 100644 index 0000000000000000000000000000000000000000..e053c46a4d417c4d0a84a0f54c673fc968dd65ff --- /dev/null +++ b/mindspore-lite/test/st/python/pytest/conftest.py @@ -0,0 +1,44 @@ +# Copyright 2025 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ +""" +Pytest ST configuration +""" +def pytest_addoption(parser): + parser.addoption( + "--backend", + action="store", + nargs="+", + default=[], + choices=("arm_ascend310_cloud", "arm_ascend310_ge_cloud"), + help="only test specified backend testcases. example: --backend arm_ascend310_cloud arm_ascend310_ge_cloud", + ) + + +def pytest_collection_modifyitems(config, items): + backend_types = config.getoption("--backend") + if not backend_types: + return + + selected = [] + for item in items: + marker = item.get_closest_marker("backend") + if marker: + supported_backends = marker.args + if not supported_backends: + raise ValueError(f"item {item} marked with backend but no backend specified.") + if "all" in supported_backends or any([backend in supported_backends for backend in backend_types]): + selected.append(item) + + items[:] = selected diff --git a/mindspore-lite/test/st/python/pytest/pytest.ini b/mindspore-lite/test/st/python/pytest/pytest.ini new file mode 100644 index 0000000000000000000000000000000000000000..756c7f22abd463e3d16f96ea3276038270140388 --- /dev/null +++ b/mindspore-lite/test/st/python/pytest/pytest.ini @@ -0,0 +1,3 @@ +[pytest] +markers = + backend(targets...): testcase can run on targets backend. Example: @pytest.mark.backend("arm_ascend310_cloud", "arm_ascend310_ge_cloud") diff --git a/mindspore-lite/test/st/python/pytest/test_compile_graph_parallel.py b/mindspore-lite/test/st/python/pytest/test_compile_graph_parallel.py new file mode 100644 index 0000000000000000000000000000000000000000..9ab16d2171710a782579f6d7f84efd836bae2a80 --- /dev/null +++ b/mindspore-lite/test/st/python/pytest/test_compile_graph_parallel.py @@ -0,0 +1,103 @@ +# Copyright 2025 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ +""" +Test lite python API. +""" +import os +import time +from concurrent.futures import ThreadPoolExecutor, as_completed +import pytest +import mindspore_lite as mslite + + +class ScopeTimeRecord: + """ + time record + """ + + def __enter__(self): + self.start_time = time.perf_counter() + return self + + def __exit__(self, *args): + self.finish_time = time.perf_counter() + self.record_time = (self.finish_time - self.start_time) * 1000 + + +MODEL_BASE_PATH = "ms_models" + +PARALLEL_CONFIG_DICT = { + "experimental": {"compile_graph_parallel": "on"}, +} + + +def build_model(model_path, context, model, parallel_enable=True): + """ + build model + """ + try: + model.build_from_file( + model_path, + mslite.ModelType.MINDIR, + context, + config_dict=PARALLEL_CONFIG_DICT if parallel_enable else None, + ) + except (RuntimeError, TypeError) as e: + print(e) + return False + return True + + +@pytest.mark.parametrize( + "model_name", + ["sar_resnet31_sequential-decoder_5e_st-sub_mj-sub_sa_real.onnx.mindir"], +) +@pytest.mark.backend("arm_ascend310_cloud") +def test_compile_graph_parallel(model_name, parallel=2, thread_pool_timeout=300, time_threshold=0.8): + """ + test config compile_graph_parallel + """ + + def create_context_and_model(): + context = [mslite.Context() for _ in range(parallel)] + model = [mslite.Model() for _ in range(parallel)] + for i in range(parallel): + context[i].target = ["ascend"] + return context, model + + model_path = os.path.join(MODEL_BASE_PATH, model_name) + + context, model = create_context_and_model() + record_disable_parallel = ScopeTimeRecord() + pool = ThreadPoolExecutor(max_workers=parallel) + with record_disable_parallel: + tasks = [ + pool.submit(build_model, model_path, context[i], model[i], False) + for i in range(parallel) + ] + assert all([task.result() for task in as_completed(tasks, timeout=thread_pool_timeout)]) + + context, model = create_context_and_model() + record_enable_parallel = ScopeTimeRecord() + with record_enable_parallel: + tasks = [ + pool.submit(build_model, model_path, context[i], model[i], True) + for i in range(parallel) + ] + assert all([task.result() for task in as_completed(tasks, timeout=thread_pool_timeout)]) + + print(f"build time disable compile_graph_parallel: {record_disable_parallel.record_time} ms") + print(f"build time enable compile_graph_parallel: {record_enable_parallel.record_time} ms") + assert record_enable_parallel.record_time < record_disable_parallel.record_time * time_threshold diff --git a/mindspore-lite/test/st/scripts/ascend/run_ascend.sh b/mindspore-lite/test/st/scripts/ascend/run_ascend.sh index cc6becaaa2894bbbd7fdca6a9bdb064ecd1735c9..fd047797d8ba5db91ba6bf9a3203e57f55faee38 100644 --- a/mindspore-lite/test/st/scripts/ascend/run_ascend.sh +++ b/mindspore-lite/test/st/scripts/ascend/run_ascend.sh @@ -210,6 +210,20 @@ else scp ${user_name}@${device_ip}:${benchmark_test_path}/run_benchmark_log.txt ${run_benchmark_log_file} || exit 1 scp ${user_name}@${device_ip}:${benchmark_test_path}/run_benchmark_result.txt ${run_benchmark_result_file} || exit 1 fi + + +echo "${backend} run pytest at $(pwd)" +ls -l "$(pwd)/python/pytest" +cat "$(pwd)/python/pytest/pytest.ini" +pytest "$(pwd)/python/pytest" -s -c "$(pwd)/python/pytest/pytest.ini" --backend ${backend} -v +RET=$? +if [ ${RET} -ne 0 ] && [ ${RET} -ne 5 ]; then # 5 means no test collected, ignore it + echo "Failed to run pytest st" + exit ${RET} +fi + +echo "Run pytest st success" + echo "Run in ${backend} ended" cat ${run_ascend_result_file} exit ${Run_ascend_status}