diff --git a/cmake/deps.txt b/cmake/deps.txt index 026988ffbd520..a7662e443cf67 100644 --- a/cmake/deps.txt +++ b/cmake/deps.txt @@ -50,7 +50,7 @@ protoc_mac_universal;https://github.com/protocolbuffers/protobuf/releases/downlo psimd;https://github.com/Maratyszcza/psimd/archive/072586a71b55b7f8c584153d223e95687148a900.zip;1f5454b01f06f9656b77e4a5e2e31d7422487013 pthreadpool;https://github.com/google/pthreadpool/archive/dcc9f28589066af0dbd4555579281230abbf74dd.zip;533a77943203ef15ca608bcd9dbe2c94da7451d2 pybind11;https://github.com/pybind/pybind11/archive/refs/tags/v3.0.2.zip;a064e663b4d7a337ac291d1bef7337ef4e60a1ae -pytorch_cpuinfo;https://github.com/pytorch/cpuinfo/archive/4628dc060ce4e82345dc166bbac875609db4ff69.zip;e58d4b47c16a982111c897e669ae4f1821a393d7 +pytorch_cpuinfo;https://github.com/pytorch/cpuinfo/archive/66ee79c038d70dad9f08705b2c9b3e58f6d8f512.zip;6e2be17c110e4f4184b059303c722b3c5bd080a6 re2;https://github.com/google/re2/archive/refs/tags/2024-07-02.zip;646e1728269cde7fcef990bf4a8e87b047882e88 safeint;https://github.com/dcleblanc/SafeInt/archive/refs/tags/3.0.28.zip;23f252040ff6cb9f1fd18575b32fa8fb5928daac tensorboard;https://github.com/tensorflow/tensorboard/archive/373eb09e4c5d2b3cc2493f0949dc4be6b6a45e81.zip;67b833913605a4f3f499894ab11528a702c2b381 diff --git a/cmake/external/onnxruntime_external_deps.cmake b/cmake/external/onnxruntime_external_deps.cmake index 117af707d2ebf..9ce7a3424c4b1 100644 --- a/cmake/external/onnxruntime_external_deps.cmake +++ b/cmake/external/onnxruntime_external_deps.cmake @@ -363,42 +363,52 @@ if (CPUINFO_SUPPORTED) set(CPUINFO_BUILD_UNIT_TESTS OFF CACHE INTERNAL "") set(CPUINFO_BUILD_MOCK_TESTS OFF CACHE INTERNAL "") set(CPUINFO_BUILD_BENCHMARKS OFF CACHE INTERNAL "") - if (onnxruntime_target_platform STREQUAL "ARM64EC" OR onnxruntime_target_platform STREQUAL "ARM64") - message(STATUS "Applying patches for Windows ARM64/ARM64EC in cpuinfo") - onnxruntime_fetchcontent_declare( - pytorch_cpuinfo - URL ${DEP_URL_pytorch_cpuinfo} - URL_HASH SHA1=${DEP_SHA1_pytorch_cpuinfo} - EXCLUDE_FROM_ALL - PATCH_COMMAND - ${Patch_EXECUTABLE} -p1 < ${PROJECT_SOURCE_DIR}/patches/cpuinfo/patch_cpuinfo_h_for_arm64ec.patch && - # https://github.com/pytorch/cpuinfo/pull/324 - ${Patch_EXECUTABLE} -p1 < ${PROJECT_SOURCE_DIR}/patches/cpuinfo/patch_vcpkg_arm64ec_support.patch - FIND_PACKAGE_ARGS NAMES cpuinfo - ) - elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") - message(STATUS "Applying sysfs fallback patch for cpuinfo on Linux") - onnxruntime_fetchcontent_declare( - pytorch_cpuinfo - URL ${DEP_URL_pytorch_cpuinfo} - URL_HASH SHA1=${DEP_SHA1_pytorch_cpuinfo} - EXCLUDE_FROM_ALL - PATCH_COMMAND - # https://github.com/microsoft/onnxruntime/issues/10038 - ${Patch_EXECUTABLE} -p1 < ${PROJECT_SOURCE_DIR}/patches/cpuinfo/fix_missing_sysfs_fallback.patch - FIND_PACKAGE_ARGS NAMES cpuinfo - ) + if(onnxruntime_USE_VCPKG AND NOT APPLE) + find_package(cpuinfo CONFIG REQUIRED) else() - onnxruntime_fetchcontent_declare( - pytorch_cpuinfo - URL ${DEP_URL_pytorch_cpuinfo} - URL_HASH SHA1=${DEP_SHA1_pytorch_cpuinfo} - EXCLUDE_FROM_ALL - FIND_PACKAGE_ARGS NAMES cpuinfo - ) + if (onnxruntime_target_platform STREQUAL "ARM64EC" OR onnxruntime_target_platform STREQUAL "ARM64") + message(STATUS "Applying patches for Windows ARM64/ARM64EC in cpuinfo") + onnxruntime_fetchcontent_declare( + pytorch_cpuinfo + URL ${DEP_URL_pytorch_cpuinfo} + URL_HASH SHA1=${DEP_SHA1_pytorch_cpuinfo} + EXCLUDE_FROM_ALL + PATCH_COMMAND + ${Patch_EXECUTABLE} -p1 < ${PROJECT_SOURCE_DIR}/patches/cpuinfo/patch_cpuinfo_h_for_arm64ec.patch && + # https://github.com/pytorch/cpuinfo/pull/324 + ${Patch_EXECUTABLE} -p1 < ${PROJECT_SOURCE_DIR}/patches/cpuinfo/patch_vcpkg_arm64ec_support.patch && + # https://github.com/pytorch/cpuinfo/pull/400 + ${Patch_EXECUTABLE} --binary --ignore-whitespace -p1 < + ${PROJECT_SOURCE_DIR}/patches/cpuinfo/enable_deinit_refcounting.patch + ) + elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") + message(STATUS "Applying sysfs fallback patch for cpuinfo on Linux") + onnxruntime_fetchcontent_declare( + pytorch_cpuinfo + URL ${DEP_URL_pytorch_cpuinfo} + URL_HASH SHA1=${DEP_SHA1_pytorch_cpuinfo} + EXCLUDE_FROM_ALL + PATCH_COMMAND + # https://github.com/microsoft/onnxruntime/issues/10038 + ${Patch_EXECUTABLE} -p1 < ${PROJECT_SOURCE_DIR}/patches/cpuinfo/fix_missing_sysfs_fallback.patch && + # https://github.com/pytorch/cpuinfo/pull/400 + ${Patch_EXECUTABLE} --binary --ignore-whitespace -p1 < + ${PROJECT_SOURCE_DIR}/patches/cpuinfo/enable_deinit_refcounting.patch + ) + else() + onnxruntime_fetchcontent_declare( + pytorch_cpuinfo + URL ${DEP_URL_pytorch_cpuinfo} + URL_HASH SHA1=${DEP_SHA1_pytorch_cpuinfo} + EXCLUDE_FROM_ALL + PATCH_COMMAND + # https://github.com/pytorch/cpuinfo/pull/400 + ${Patch_EXECUTABLE} --binary --ignore-whitespace -p1 < + ${PROJECT_SOURCE_DIR}/patches/cpuinfo/enable_deinit_refcounting.patch + ) + endif() + onnxruntime_fetchcontent_makeavailable(pytorch_cpuinfo) endif() - set(ONNXRUNTIME_CPUINFO_PROJ pytorch_cpuinfo) - onnxruntime_fetchcontent_makeavailable(${ONNXRUNTIME_CPUINFO_PROJ}) if(TARGET cpuinfo::cpuinfo AND NOT TARGET cpuinfo) message(STATUS "Aliasing cpuinfo::cpuinfo to cpuinfo") add_library(cpuinfo ALIAS cpuinfo::cpuinfo) diff --git a/cmake/external/xnnpack.cmake b/cmake/external/xnnpack.cmake index 571283c33c713..59cc26316a770 100644 --- a/cmake/external/xnnpack.cmake +++ b/cmake/external/xnnpack.cmake @@ -84,9 +84,9 @@ endif() onnxruntime_fetchcontent_declare(googlexnnpack URL ${DEP_URL_googlexnnpack} URL_HASH SHA1=${DEP_SHA1_googlexnnpack} - PATCH_COMMAND ${Patch_EXECUTABLE} --binary --ignore-whitespace -p1 < ${PROJECT_SOURCE_DIR}/patches/xnnpack/AddEmscriptenAndIosSupport.patch + PATCH_COMMAND ${Patch_EXECUTABLE} --binary --ignore-whitespace -p1 < ${PROJECT_SOURCE_DIR}/patches/xnnpack/AddEmscriptenAndIosSupport.patch && + ${Patch_EXECUTABLE} --ignore-whitespace -p1 < ${PROJECT_SOURCE_DIR}/patches/xnnpack/release_cpuinfo_after_hardware_config.patch EXCLUDE_FROM_ALL - FIND_PACKAGE_ARGS NAMES xnnpack ) onnxruntime_fetchcontent_makeavailable(googlexnnpack) set(XNNPACK_DIR ${googlexnnpack_SOURCE_DIR}) diff --git a/cmake/onnxruntime_unittests.cmake b/cmake/onnxruntime_unittests.cmake index a64661c58aee5..f222bf63dd246 100644 --- a/cmake/onnxruntime_unittests.cmake +++ b/cmake/onnxruntime_unittests.cmake @@ -1986,6 +1986,43 @@ endif() set_target_properties(ep_weight_sharing_ctx_gen PROPERTIES FOLDER "ONNXRuntimeTest") endif() + if (CPUINFO_SUPPORTED AND NOT CMAKE_SYSTEM_NAME STREQUAL "Emscripten") + set(onnxruntime_cpuinfo_test_library cpuinfo) + get_target_property(onnxruntime_cpuinfo_aliased_target cpuinfo ALIASED_TARGET) + if(onnxruntime_cpuinfo_aliased_target) + set(onnxruntime_cpuinfo_test_library ${onnxruntime_cpuinfo_aliased_target}) + endif() + get_target_property(onnxruntime_cpuinfo_library_type ${onnxruntime_cpuinfo_test_library} TYPE) + if(onnxruntime_cpuinfo_library_type STREQUAL "STATIC_LIBRARY") + onnxruntime_add_executable( + onnxruntime_cpuinfo_refcount_test + ${ONNXRUNTIME_SHARED_LIB_TEST_SRC_DIR}/cpuinfo_refcount_test.cc) + target_compile_definitions( + onnxruntime_cpuinfo_refcount_test + PRIVATE ORT_CPUINFO_TEST_HAS_INTERNAL_STATE) + if(onnxruntime_USE_XNNPACK) + target_compile_definitions( + onnxruntime_cpuinfo_refcount_test + PRIVATE ORT_CPUINFO_TEST_USE_XNNPACK) + if(onnxruntime_USE_VCPKG) + target_include_directories(onnxruntime_cpuinfo_refcount_test PRIVATE ${XNNPACK_HDR}) + else() + target_include_directories(onnxruntime_cpuinfo_refcount_test PRIVATE ${XNNPACK_INCLUDE_DIR}) + endif() + target_link_libraries( + onnxruntime_cpuinfo_refcount_test + PRIVATE ${onnxruntime_EXTERNAL_LIBRARIES_XNNPACK}) + endif() + target_link_libraries(onnxruntime_cpuinfo_refcount_test PRIVATE cpuinfo Threads::Threads) + add_test( + NAME onnxruntime_cpuinfo_refcount_test + COMMAND onnxruntime_cpuinfo_refcount_test) + set_target_properties( + onnxruntime_cpuinfo_refcount_test + PROPERTIES FOLDER "ONNXRuntimeTest") + endif() + endif() + # shared lib if (onnxruntime_BUILD_SHARED_LIB) if(WIN32) @@ -1994,6 +2031,38 @@ endif() add_test(NAME onnxruntime_shared_lib_dlopen_test COMMAND onnxruntime_shared_lib_dlopen_test WORKING_DIRECTORY $) set_target_properties(onnxruntime_shared_lib_dlopen_test PROPERTIES FOLDER "ONNXRuntimeTest") + if(onnxruntime_cpuinfo_library_type STREQUAL "STATIC_LIBRARY") + onnxruntime_add_shared_library( + onnxruntime_cpuinfo_dlopen_test_library + ${ONNXRUNTIME_SHARED_LIB_TEST_SRC_DIR}/cpuinfo_dlopen_test_library.cc + ${ONNXRUNTIME_SHARED_LIB_TEST_SRC_DIR}/cpuinfo_dlopen_test_library.def) + target_include_directories( + onnxruntime_cpuinfo_dlopen_test_library + PRIVATE ${ONNXRUNTIME_ROOT}) + target_link_libraries( + onnxruntime_cpuinfo_dlopen_test_library + PRIVATE onnxruntime_common cpuinfo) + + onnxruntime_add_executable( + onnxruntime_shared_lib_cpuinfo_dlopen_test + ${ONNXRUNTIME_SHARED_LIB_TEST_SRC_DIR}/cpuinfo_dlopen_test.cc) + add_dependencies( + onnxruntime_shared_lib_cpuinfo_dlopen_test + onnxruntime_cpuinfo_dlopen_test_library) + target_compile_definitions( + onnxruntime_shared_lib_cpuinfo_dlopen_test + PRIVATE + ORT_CPUINFO_DLOPEN_TEST_LIBRARY=L"$") + add_test( + NAME onnxruntime_shared_lib_cpuinfo_dlopen_test + COMMAND onnxruntime_shared_lib_cpuinfo_dlopen_test + WORKING_DIRECTORY $) + set_target_properties( + onnxruntime_cpuinfo_dlopen_test_library + onnxruntime_shared_lib_cpuinfo_dlopen_test + PROPERTIES FOLDER "ONNXRuntimeTest") + endif() + if (MSVC) # set VS debugger working directory to the test program's directory set_target_properties(onnxruntime_shared_lib_dlopen_test PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY $) diff --git a/cmake/patches/.gitattributes b/cmake/patches/.gitattributes index 9812ceb1ffd9b..24d5f860ef2a8 100644 --- a/cmake/patches/.gitattributes +++ b/cmake/patches/.gitattributes @@ -1 +1,4 @@ *.patch text eol=lf + +# cpuinfo contains both LF and CRLF source files, so this patch must preserve both. +cpuinfo/enable_deinit_refcounting.patch -text diff --git a/cmake/patches/cpuinfo/enable_deinit_refcounting.patch b/cmake/patches/cpuinfo/enable_deinit_refcounting.patch new file mode 100644 index 0000000000000..960cfc6df58fa --- /dev/null +++ b/cmake/patches/cpuinfo/enable_deinit_refcounting.patch @@ -0,0 +1,650 @@ +diff --git a/src/arm/linux/init.c b/src/arm/linux/init.c +index 48c9f4a050412220b5c817814b643fc4850c7626..cb3170f279474ee344dd61480eab2831f2d027b9 100644 +--- a/src/arm/linux/init.c ++++ b/src/arm/linux/init.c +@@ -1001,3 +1001,43 @@ cleanup: + free(linux_cpu_to_core_map); + free(linux_cpu_to_uarch_index_map); + } ++ ++void cpuinfo_arm_linux_deinit(void) { ++ free(cpuinfo_processors); ++ cpuinfo_processors = NULL; ++ cpuinfo_processors_count = 0; ++ ++ free(cpuinfo_cores); ++ cpuinfo_cores = NULL; ++ cpuinfo_cores_count = 0; ++ ++ free(cpuinfo_clusters); ++ cpuinfo_clusters = NULL; ++ cpuinfo_clusters_count = 0; ++ ++ /* cpuinfo_packages points to static storage (&package) — do not free */ ++ cpuinfo_packages = NULL; ++ cpuinfo_packages_count = 0; ++ ++ free(cpuinfo_uarchs); ++ cpuinfo_uarchs = NULL; ++ cpuinfo_uarchs_count = 0; ++ ++ for (int lvl = 0; lvl < cpuinfo_cache_level_max; ++lvl) { ++ free(cpuinfo_cache[lvl]); ++ cpuinfo_cache[lvl] = NULL; ++ cpuinfo_cache_count[lvl] = 0; ++ } ++ cpuinfo_max_cache_size = 0; ++ ++ free(cpuinfo_linux_cpu_to_processor_map); ++ cpuinfo_linux_cpu_to_processor_map = NULL; ++ ++ free(cpuinfo_linux_cpu_to_core_map); ++ cpuinfo_linux_cpu_to_core_map = NULL; ++ ++ free((void*)cpuinfo_linux_cpu_to_uarch_index_map); ++ cpuinfo_linux_cpu_to_uarch_index_map = NULL; ++ ++ cpuinfo_linux_cpu_max = 0; ++} +diff --git a/src/arm/mach/init.c b/src/arm/mach/init.c +index 76224b9d40eb5cd047f1353e81b5999196d3b881..9a9fb4276c531687bf7984e2bd41152ed91e94a6 100644 +--- a/src/arm/mach/init.c ++++ b/src/arm/mach/init.c +@@ -873,3 +873,32 @@ cleanup: + free(l2); + free(l3); + } ++ ++void cpuinfo_arm_mach_deinit(void) { ++ free(cpuinfo_processors); ++ cpuinfo_processors = NULL; ++ cpuinfo_processors_count = 0; ++ ++ free(cpuinfo_cores); ++ cpuinfo_cores = NULL; ++ cpuinfo_cores_count = 0; ++ ++ free(cpuinfo_clusters); ++ cpuinfo_clusters = NULL; ++ cpuinfo_clusters_count = 0; ++ ++ free(cpuinfo_packages); ++ cpuinfo_packages = NULL; ++ cpuinfo_packages_count = 0; ++ ++ free(cpuinfo_uarchs); ++ cpuinfo_uarchs = NULL; ++ cpuinfo_uarchs_count = 0; ++ ++ for (int lvl = 0; lvl < cpuinfo_cache_level_max; ++lvl) { ++ free(cpuinfo_cache[lvl]); ++ cpuinfo_cache[lvl] = NULL; ++ cpuinfo_cache_count[lvl] = 0; ++ } ++ cpuinfo_max_cache_size = 0; ++} +diff --git a/src/arm/windows/init-by-logical-sys-info.c b/src/arm/windows/init-by-logical-sys-info.c +index 815ecb770a5bc5c48c7dff049f50b111b427954a..f0d66223f0d189776659248f525200ebdcda0116 100644 +--- a/src/arm/windows/init-by-logical-sys-info.c ++++ b/src/arm/windows/init-by-logical-sys-info.c +@@ -14,6 +14,8 @@ + + #define MAX_NR_OF_CACHES (cpuinfo_cache_level_max - 1) + ++static uint32_t cache_counters[MAX_NR_OF_CACHES]; ++ + /* Call chain: + * cpu_info_init_by_logical_sys_info + * read_packages_for_processors +@@ -126,6 +128,8 @@ bool cpu_info_init_by_logical_sys_info(const struct woa_chip_info* chip_info, co + uint32_t nr_of_uarchs = 0; + bool result = false; + ++ memset(cache_counters, 0, sizeof(cache_counters)); ++ + HANDLE heap = GetProcessHeap(); + + /* 1. Count available logical processor groups and processors */ +@@ -372,6 +376,40 @@ clean_up: + return result; + } + ++BOOL CALLBACK cpuinfo_arm_windows_deinit(PINIT_ONCE init_once, PVOID parameter, PVOID* context) { ++ HANDLE heap = GetProcessHeap(); ++ ++ HeapFree(heap, 0, cpuinfo_processors); ++ cpuinfo_processors = NULL; ++ cpuinfo_processors_count = 0; ++ ++ HeapFree(heap, 0, cpuinfo_packages); ++ cpuinfo_packages = NULL; ++ cpuinfo_packages_count = 0; ++ ++ HeapFree(heap, 0, cpuinfo_clusters); ++ cpuinfo_clusters = NULL; ++ cpuinfo_clusters_count = 0; ++ ++ HeapFree(heap, 0, cpuinfo_cores); ++ cpuinfo_cores = NULL; ++ cpuinfo_cores_count = 0; ++ ++ HeapFree(heap, 0, cpuinfo_uarchs); ++ cpuinfo_uarchs = NULL; ++ cpuinfo_uarchs_count = 0; ++ ++ /* Caches are allocated as a single contiguous block starting at l1i */ ++ HeapFree(heap, 0, cpuinfo_cache[cpuinfo_cache_level_1i]); ++ for (int lvl = 0; lvl < cpuinfo_cache_level_max; ++lvl) { ++ cpuinfo_cache[lvl] = NULL; ++ cpuinfo_cache_count[lvl] = 0; ++ } ++ cpuinfo_max_cache_size = 0; ++ ++ return TRUE; ++} ++ + static uint32_t count_logical_processors(const uint32_t max_group_count, uint32_t* global_proc_index_per_group) { + uint32_t nr_of_processors = 0; + +@@ -606,11 +644,6 @@ static bool parse_relation_cache_info( + uint32_t* numbers_of_caches, + const uint32_t* global_proc_index_per_group, + PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX info) { +- static uint32_t l1i_counter = 0; +- static uint32_t l1d_counter = 0; +- static uint32_t l2_counter = 0; +- static uint32_t l3_counter = 0; +- + /* Count cache types for allocation at first. */ + if (caches == NULL) { + switch (info->Cache.Level) { +@@ -665,12 +698,12 @@ static bool parse_relation_cache_info( + case 1: + switch (info->Cache.Type) { + case CacheInstruction: +- current_cache = l1i_base + l1i_counter; +- l1i_counter++; ++ current_cache = l1i_base + cache_counters[cpuinfo_cache_level_1i]; ++ cache_counters[cpuinfo_cache_level_1i]++; + break; + case CacheData: +- current_cache = l1d_base + l1d_counter; +- l1d_counter++; ++ current_cache = l1d_base + cache_counters[cpuinfo_cache_level_1d]; ++ cache_counters[cpuinfo_cache_level_1d]++; + break; + case CacheUnified: + break; +@@ -681,12 +714,12 @@ static bool parse_relation_cache_info( + } + break; + case 2: +- current_cache = l2_base + l2_counter; +- l2_counter++; ++ current_cache = l2_base + cache_counters[cpuinfo_cache_level_2]; ++ cache_counters[cpuinfo_cache_level_2]++; + break; + case 3: +- current_cache = l3_base + l3_counter; +- l3_counter++; ++ current_cache = l3_base + cache_counters[cpuinfo_cache_level_3]; ++ cache_counters[cpuinfo_cache_level_3]++; + break; + } + current_cache->size = info->Cache.CacheSize; +diff --git a/src/arm/windows/init.c b/src/arm/windows/init.c +index 528ae3279f5e979409ac07de6cee833691e034cd..fcf219df153419249211be4fdf014f50fc5198da 100644 +--- a/src/arm/windows/init.c ++++ b/src/arm/windows/init.c +@@ -25,12 +25,18 @@ BOOL CALLBACK cpuinfo_arm_windows_init(PINIT_ONCE init_once, PVOID parameter, PV + set_cpuinfo_isa_fields(); + + chip_info = get_system_info_from_registry(); ++ const bool chip_info_from_registry = chip_info != NULL; + if (chip_info == NULL) { + chip_info = &woa_chip_unknown; + } + + cpuinfo_is_initialized = cpu_info_init_by_logical_sys_info(chip_info, chip_info->uarchs[0].vendor); + ++ if (chip_info_from_registry) { ++ HeapFree(GetProcessHeap(), 0, chip_info->chip_name_string); ++ HeapFree(GetProcessHeap(), 0, chip_info); ++ } ++ + return true; + } + +@@ -177,6 +183,7 @@ static struct woa_chip_info* get_system_info_from_registry(void) { + uint64_t midr_qword = (uint32_t)read_registry_qword(cpu0_subkey, chip_midr_value); + if (midr_qword == 0) { + cpuinfo_log_error("Registry read error for MIDR value"); ++ HeapFree(GetProcessHeap(), 0, text_buffer); + return NULL; + } + // MIDR is only 32 bits, so we need to cast it to uint32_t +@@ -187,6 +194,7 @@ static struct woa_chip_info* get_system_info_from_registry(void) { + uint64_t frequency_mhz = read_registry_dword(cpu0_subkey, chip_mhz_value); + if (frequency_mhz == 0) { + cpuinfo_log_error("Registry read error for frequency value"); ++ HeapFree(GetProcessHeap(), 0, text_buffer); + return NULL; + } + // Convert MHz to Hz +@@ -196,11 +204,13 @@ static struct woa_chip_info* get_system_info_from_registry(void) { + chip_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct woa_chip_info)); + if (chip_info == NULL) { + cpuinfo_log_error("Heap allocation error for chip_info"); ++ HeapFree(GetProcessHeap(), 0, text_buffer); + return NULL; + } + + // set chip_info fields + chip_info->chip_name_string = wcsndup(text_buffer, CPUINFO_PACKAGE_NAME_MAX - 1); ++ HeapFree(GetProcessHeap(), 0, text_buffer); + chip_info->uarchs[0] = get_core_info_from_midr(midr_value, frequency_hz); + + cpuinfo_log_debug("detected chip model name: %ls", chip_info->chip_name_string); +diff --git a/src/cpuinfo/internal-api.h b/src/cpuinfo/internal-api.h +index d84b26a89cfc38e726216688e490f50799f31455..9c1588872da413535f2fb68fb96d6106b420ac49 100644 +--- a/src/cpuinfo/internal-api.h ++++ b/src/cpuinfo/internal-api.h +@@ -45,6 +45,9 @@ extern CPUINFO_INTERNAL struct cpuinfo_uarch_info cpuinfo_global_uarch; + extern CPUINFO_INTERNAL uint32_t cpuinfo_linux_cpu_max; + extern CPUINFO_INTERNAL const struct cpuinfo_processor** cpuinfo_linux_cpu_to_processor_map; + extern CPUINFO_INTERNAL const struct cpuinfo_core** cpuinfo_linux_cpu_to_core_map; ++#if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 || CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64 ++extern CPUINFO_INTERNAL const uint32_t* cpuinfo_linux_cpu_to_uarch_index_map; ++#endif + #endif + + CPUINFO_PRIVATE void cpuinfo_x86_mach_init(void); +@@ -62,6 +65,23 @@ CPUINFO_PRIVATE void cpuinfo_arm_linux_init(void); + CPUINFO_PRIVATE void cpuinfo_riscv_linux_init(void); + CPUINFO_PRIVATE void cpuinfo_emscripten_init(void); + ++/* Platform-specific deinitialization functions. ++ * Each frees the globals allocated by its corresponding init function. */ ++CPUINFO_PRIVATE void cpuinfo_x86_mach_deinit(void); ++CPUINFO_PRIVATE void cpuinfo_x86_linux_deinit(void); ++CPUINFO_PRIVATE void cpuinfo_x86_freebsd_deinit(void); ++#if defined(_WIN32) || defined(__CYGWIN__) ++#if CPUINFO_ARCH_ARM64 ++CPUINFO_PRIVATE BOOL CALLBACK cpuinfo_arm_windows_deinit(PINIT_ONCE init_once, PVOID parameter, PVOID* context); ++#else ++CPUINFO_PRIVATE BOOL CALLBACK cpuinfo_x86_windows_deinit(PINIT_ONCE init_once, PVOID parameter, PVOID* context); ++#endif ++#endif ++CPUINFO_PRIVATE void cpuinfo_arm_mach_deinit(void); ++CPUINFO_PRIVATE void cpuinfo_arm_linux_deinit(void); ++CPUINFO_PRIVATE void cpuinfo_riscv_linux_deinit(void); ++CPUINFO_PRIVATE void cpuinfo_emscripten_deinit(void); ++ + CPUINFO_PRIVATE uint32_t cpuinfo_compute_max_cache_size(const struct cpuinfo_processor* processor); + + typedef void (*cpuinfo_processor_callback)(uint32_t); +diff --git a/src/emscripten/init.c b/src/emscripten/init.c +index c2393243755816fb341f892b7b092a58891603c4..6c8210f2c23ddc0f0efde954d80fcf974aba74fb 100644 +--- a/src/emscripten/init.c ++++ b/src/emscripten/init.c +@@ -286,3 +286,35 @@ cleanup: + free(l1d); + free(l2); + } ++ ++void cpuinfo_emscripten_deinit(void) { ++ free(cpuinfo_processors); ++ cpuinfo_processors = NULL; ++ cpuinfo_processors_count = 0; ++ ++ free(cpuinfo_cores); ++ cpuinfo_cores = NULL; ++ cpuinfo_cores_count = 0; ++ ++ free(cpuinfo_clusters); ++ cpuinfo_clusters = NULL; ++ cpuinfo_clusters_count = 0; ++ ++ /* cpuinfo_packages points to static storage (&static_package) — do not free */ ++ cpuinfo_packages = NULL; ++ cpuinfo_packages_count = 0; ++ ++ /* L1i, L1d, L2 are dynamically allocated; L3 may point to static ++ * storage (&static_x86_l3) so only free levels that were calloc'd */ ++ free(cpuinfo_cache[cpuinfo_cache_level_1i]); ++ free(cpuinfo_cache[cpuinfo_cache_level_1d]); ++ free(cpuinfo_cache[cpuinfo_cache_level_2]); ++ /* cpuinfo_cache[cpuinfo_cache_level_3] may be &static_x86_l3 — do not free */ ++ for (int lvl = 0; lvl < cpuinfo_cache_level_max; ++lvl) { ++ cpuinfo_cache[lvl] = NULL; ++ cpuinfo_cache_count[lvl] = 0; ++ } ++ cpuinfo_max_cache_size = 0; ++ ++ cpuinfo_global_uarch = (struct cpuinfo_uarch_info){0}; ++} +diff --git a/src/init.c b/src/init.c +index 81d5721c446f363cefcbb39d52507a2c07298c35..e3cd9217a9acfc9615f97a1aa6bb66d96f373910 100644 +--- a/src/init.c ++++ b/src/init.c +@@ -13,55 +13,98 @@ + #endif + + #if defined(_WIN32) || defined(__CYGWIN__) +-static INIT_ONCE init_guard = INIT_ONCE_STATIC_INIT; ++static SRWLOCK cpuinfo_lifecycle_lock = SRWLOCK_INIT; ++#define CPUINFO_LIFECYCLE_LOCK() AcquireSRWLockExclusive(&cpuinfo_lifecycle_lock) ++#define CPUINFO_LIFECYCLE_UNLOCK() ReleaseSRWLockExclusive(&cpuinfo_lifecycle_lock) + #elif !defined(__EMSCRIPTEN__) || defined(__EMSCRIPTEN_PTHREADS__) +-static pthread_once_t init_guard = PTHREAD_ONCE_INIT; ++static pthread_mutex_t cpuinfo_lifecycle_lock = PTHREAD_MUTEX_INITIALIZER; ++#define CPUINFO_LIFECYCLE_LOCK() pthread_mutex_lock(&cpuinfo_lifecycle_lock) ++#define CPUINFO_LIFECYCLE_UNLOCK() pthread_mutex_unlock(&cpuinfo_lifecycle_lock) + #else +-static bool init_guard = false; ++/* Single-threaded build (e.g. Emscripten without pthreads): no lock needed. */ ++#define CPUINFO_LIFECYCLE_LOCK() \ ++ do { \ ++ } while (0) ++#define CPUINFO_LIFECYCLE_UNLOCK() \ ++ do { \ ++ } while (0) + #endif ++static unsigned int cpuinfo_init_refcount = 0; + + bool CPUINFO_ABI cpuinfo_initialize(void) { ++ CPUINFO_LIFECYCLE_LOCK(); ++ if (cpuinfo_init_refcount == 0) { + #if CPUINFO_ARCH_X86 || CPUINFO_ARCH_X86_64 + #if defined(__MACH__) && defined(__APPLE__) +- pthread_once(&init_guard, &cpuinfo_x86_mach_init); ++ cpuinfo_x86_mach_init(); + #elif defined(__FreeBSD__) +- pthread_once(&init_guard, &cpuinfo_x86_freebsd_init); ++ cpuinfo_x86_freebsd_init(); + #elif defined(__linux__) +- pthread_once(&init_guard, &cpuinfo_x86_linux_init); ++ cpuinfo_x86_linux_init(); + #elif defined(_WIN32) || defined(__CYGWIN__) +- InitOnceExecuteOnce(&init_guard, &cpuinfo_x86_windows_init, NULL, NULL); ++ cpuinfo_x86_windows_init(NULL, NULL, NULL); + #else +- cpuinfo_log_error("operating system is not supported in cpuinfo"); ++ cpuinfo_log_error("operating system is not supported in cpuinfo"); + #endif + #elif CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 + #if defined(__linux__) +- pthread_once(&init_guard, &cpuinfo_arm_linux_init); ++ cpuinfo_arm_linux_init(); + #elif defined(__MACH__) && defined(__APPLE__) +- pthread_once(&init_guard, &cpuinfo_arm_mach_init); ++ cpuinfo_arm_mach_init(); + #elif defined(_WIN32) +- InitOnceExecuteOnce(&init_guard, &cpuinfo_arm_windows_init, NULL, NULL); ++ cpuinfo_arm_windows_init(NULL, NULL, NULL); + #else +- cpuinfo_log_error("operating system is not supported in cpuinfo"); ++ cpuinfo_log_error("operating system is not supported in cpuinfo"); + #endif + #elif CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64 + #if defined(__linux__) +- pthread_once(&init_guard, &cpuinfo_riscv_linux_init); ++ cpuinfo_riscv_linux_init(); + #else +- cpuinfo_log_error("operating system is not supported in cpuinfo"); ++ cpuinfo_log_error("operating system is not supported in cpuinfo"); + #endif + #elif CPUINFO_ARCH_ASMJS || CPUINFO_ARCH_WASM || CPUINFO_ARCH_WASMSIMD +-#if defined(__EMSCRIPTEN_PTHREADS__) +- pthread_once(&init_guard, &cpuinfo_emscripten_init); +-#else +- if (!init_guard) { + cpuinfo_emscripten_init(); +- } +- init_guard = true; +-#endif + #else +- cpuinfo_log_error("processor architecture is not supported in cpuinfo"); ++ cpuinfo_log_error("processor architecture is not supported in cpuinfo"); + #endif +- return cpuinfo_is_initialized; ++ } ++ if (cpuinfo_is_initialized) { ++ cpuinfo_init_refcount++; ++ } ++ const bool initialized = cpuinfo_is_initialized; ++ CPUINFO_LIFECYCLE_UNLOCK(); ++ return initialized; + } + +-void CPUINFO_ABI cpuinfo_deinitialize(void) {} ++void CPUINFO_ABI cpuinfo_deinitialize(void) { ++ CPUINFO_LIFECYCLE_LOCK(); ++ if (cpuinfo_init_refcount != 0 && --cpuinfo_init_refcount == 0) { ++ cpuinfo_is_initialized = false; ++#if CPUINFO_ARCH_X86 || CPUINFO_ARCH_X86_64 ++#if defined(__MACH__) && defined(__APPLE__) ++ cpuinfo_x86_mach_deinit(); ++#elif defined(__FreeBSD__) ++ cpuinfo_x86_freebsd_deinit(); ++#elif defined(__linux__) ++ cpuinfo_x86_linux_deinit(); ++#elif defined(_WIN32) || defined(__CYGWIN__) ++ cpuinfo_x86_windows_deinit(NULL, NULL, NULL); ++#endif ++#elif CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 ++#if defined(__linux__) ++ cpuinfo_arm_linux_deinit(); ++#elif defined(__MACH__) && defined(__APPLE__) ++ cpuinfo_arm_mach_deinit(); ++#elif defined(_WIN32) ++ cpuinfo_arm_windows_deinit(NULL, NULL, NULL); ++#endif ++#elif CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64 ++#if defined(__linux__) ++ cpuinfo_riscv_linux_deinit(); ++#endif ++#elif CPUINFO_ARCH_ASMJS || CPUINFO_ARCH_WASM || CPUINFO_ARCH_WASMSIMD ++ cpuinfo_emscripten_deinit(); ++#endif ++ } ++ CPUINFO_LIFECYCLE_UNLOCK(); ++} +diff --git a/src/riscv/linux/init.c b/src/riscv/linux/init.c +index 45168c7e66b80e7f8a712a6afe7bab379a07b95b..3da8c3ffd59f02bebb0193b832d67ddf0841cab3 100644 +--- a/src/riscv/linux/init.c ++++ b/src/riscv/linux/init.c +@@ -618,3 +618,36 @@ cleanup: + free(linux_cpu_to_core_map); + free(linux_cpu_to_uarch_index_map); + } ++ ++void cpuinfo_riscv_linux_deinit(void) { ++ free(cpuinfo_processors); ++ cpuinfo_processors = NULL; ++ cpuinfo_processors_count = 0; ++ ++ free(cpuinfo_cores); ++ cpuinfo_cores = NULL; ++ cpuinfo_cores_count = 0; ++ ++ free(cpuinfo_clusters); ++ cpuinfo_clusters = NULL; ++ cpuinfo_clusters_count = 0; ++ ++ free(cpuinfo_packages); ++ cpuinfo_packages = NULL; ++ cpuinfo_packages_count = 0; ++ ++ free(cpuinfo_uarchs); ++ cpuinfo_uarchs = NULL; ++ cpuinfo_uarchs_count = 0; ++ ++ free(cpuinfo_linux_cpu_to_processor_map); ++ cpuinfo_linux_cpu_to_processor_map = NULL; ++ ++ free(cpuinfo_linux_cpu_to_core_map); ++ cpuinfo_linux_cpu_to_core_map = NULL; ++ ++ free((void*)cpuinfo_linux_cpu_to_uarch_index_map); ++ cpuinfo_linux_cpu_to_uarch_index_map = NULL; ++ ++ cpuinfo_linux_cpu_max = 0; ++} +diff --git a/src/x86/freebsd/init.c b/src/x86/freebsd/init.c +index 797fa24b95a55e0503459dd2fdaf8476f060a631..ba30a5d9c89c209115f6797d9b52263c7271e17f 100644 +--- a/src/x86/freebsd/init.c ++++ b/src/x86/freebsd/init.c +@@ -396,3 +396,30 @@ cleanup: + free(l3); + free(l4); + } ++ ++void cpuinfo_x86_freebsd_deinit(void) { ++ free(cpuinfo_processors); ++ cpuinfo_processors = NULL; ++ cpuinfo_processors_count = 0; ++ ++ free(cpuinfo_cores); ++ cpuinfo_cores = NULL; ++ cpuinfo_cores_count = 0; ++ ++ free(cpuinfo_clusters); ++ cpuinfo_clusters = NULL; ++ cpuinfo_clusters_count = 0; ++ ++ free(cpuinfo_packages); ++ cpuinfo_packages = NULL; ++ cpuinfo_packages_count = 0; ++ ++ for (int lvl = 0; lvl < cpuinfo_cache_level_max; ++lvl) { ++ free(cpuinfo_cache[lvl]); ++ cpuinfo_cache[lvl] = NULL; ++ cpuinfo_cache_count[lvl] = 0; ++ } ++ cpuinfo_max_cache_size = 0; ++ ++ cpuinfo_global_uarch = (struct cpuinfo_uarch_info){0}; ++} +diff --git a/src/x86/linux/init.c b/src/x86/linux/init.c +index d2b2d47514c8cc75b502c78b1d1176ae49fe3304..62e1187e0631b791c378d23963d63e10095a12be 100644 +--- a/src/x86/linux/init.c ++++ b/src/x86/linux/init.c +@@ -676,3 +676,38 @@ cleanup: + free(linux_cpu_to_processor_map); + free(linux_cpu_to_core_map); + } ++ ++void cpuinfo_x86_linux_deinit(void) { ++ free(cpuinfo_processors); ++ cpuinfo_processors = NULL; ++ cpuinfo_processors_count = 0; ++ ++ free(cpuinfo_cores); ++ cpuinfo_cores = NULL; ++ cpuinfo_cores_count = 0; ++ ++ free(cpuinfo_clusters); ++ cpuinfo_clusters = NULL; ++ cpuinfo_clusters_count = 0; ++ ++ free(cpuinfo_packages); ++ cpuinfo_packages = NULL; ++ cpuinfo_packages_count = 0; ++ ++ for (int lvl = 0; lvl < cpuinfo_cache_level_max; ++lvl) { ++ free(cpuinfo_cache[lvl]); ++ cpuinfo_cache[lvl] = NULL; ++ cpuinfo_cache_count[lvl] = 0; ++ } ++ cpuinfo_max_cache_size = 0; ++ ++ free(cpuinfo_linux_cpu_to_processor_map); ++ cpuinfo_linux_cpu_to_processor_map = NULL; ++ ++ free(cpuinfo_linux_cpu_to_core_map); ++ cpuinfo_linux_cpu_to_core_map = NULL; ++ ++ cpuinfo_linux_cpu_max = 0; ++ ++ cpuinfo_global_uarch = (struct cpuinfo_uarch_info){0}; ++} +diff --git a/src/x86/mach/init.c b/src/x86/mach/init.c +index b8ea6047ac043fc067190013e719188b50aca3f5..9009e830ab4ac15610af72e700266b800d88e0fc 100644 +--- a/src/x86/mach/init.c ++++ b/src/x86/mach/init.c +@@ -378,3 +378,30 @@ cleanup: + free(l3); + free(l4); + } ++ ++void cpuinfo_x86_mach_deinit(void) { ++ free(cpuinfo_processors); ++ cpuinfo_processors = NULL; ++ cpuinfo_processors_count = 0; ++ ++ free(cpuinfo_cores); ++ cpuinfo_cores = NULL; ++ cpuinfo_cores_count = 0; ++ ++ free(cpuinfo_clusters); ++ cpuinfo_clusters = NULL; ++ cpuinfo_clusters_count = 0; ++ ++ free(cpuinfo_packages); ++ cpuinfo_packages = NULL; ++ cpuinfo_packages_count = 0; ++ ++ for (int lvl = 0; lvl < cpuinfo_cache_level_max; ++lvl) { ++ free(cpuinfo_cache[lvl]); ++ cpuinfo_cache[lvl] = NULL; ++ cpuinfo_cache_count[lvl] = 0; ++ } ++ cpuinfo_max_cache_size = 0; ++ ++ cpuinfo_global_uarch = (struct cpuinfo_uarch_info){0}; ++} +diff --git a/src/x86/windows/init.c b/src/x86/windows/init.c +index 7380bb46d5f2ee96e98b77fd378be9d70de813ee..f1401beb60de989503402be98fdbac4c20909c1b 100644 +--- a/src/x86/windows/init.c ++++ b/src/x86/windows/init.c +@@ -665,5 +665,39 @@ cleanup: + if (l4 != NULL) { + HeapFree(heap, 0, l4); + } ++ if (processor_infos != NULL) { ++ HeapFree(heap, 0, processor_infos); ++ } ++ return TRUE; ++} ++ ++BOOL CALLBACK cpuinfo_x86_windows_deinit(PINIT_ONCE init_once, PVOID parameter, PVOID* context) { ++ HANDLE heap = GetProcessHeap(); ++ ++ HeapFree(heap, 0, cpuinfo_processors); ++ cpuinfo_processors = NULL; ++ cpuinfo_processors_count = 0; ++ ++ HeapFree(heap, 0, cpuinfo_cores); ++ cpuinfo_cores = NULL; ++ cpuinfo_cores_count = 0; ++ ++ HeapFree(heap, 0, cpuinfo_clusters); ++ cpuinfo_clusters = NULL; ++ cpuinfo_clusters_count = 0; ++ ++ HeapFree(heap, 0, cpuinfo_packages); ++ cpuinfo_packages = NULL; ++ cpuinfo_packages_count = 0; ++ ++ for (int lvl = 0; lvl < cpuinfo_cache_level_max; ++lvl) { ++ HeapFree(heap, 0, cpuinfo_cache[lvl]); ++ cpuinfo_cache[lvl] = NULL; ++ cpuinfo_cache_count[lvl] = 0; ++ } ++ cpuinfo_max_cache_size = 0; ++ ++ cpuinfo_global_uarch = (struct cpuinfo_uarch_info){0}; ++ + return TRUE; + } diff --git a/cmake/patches/xnnpack/release_cpuinfo_after_hardware_config.patch b/cmake/patches/xnnpack/release_cpuinfo_after_hardware_config.patch new file mode 100644 index 0000000000000..8aea6bfc4d156 --- /dev/null +++ b/cmake/patches/xnnpack/release_cpuinfo_after_hardware_config.patch @@ -0,0 +1,46 @@ +diff --git a/src/configs/hardware-config.c b/src/configs/hardware-config.c +index b4e942aa6955360e3ea85619d549410e65ab7a9c..6cf6e9a213b6f72c94940ff815e11f70a658c419 100644 +--- a/src/configs/hardware-config.c ++++ b/src/configs/hardware-config.c +@@ -343,6 +343,7 @@ static void init_hardware_config(void) { + xnn_log_info("cpu_get_uarch(%i): 0x%x", i, cpuinfo_get_uarch(i)->uarch); + } + #endif // XNN_MAX_UARCH_TYPES > 1 ++ cpuinfo_deinitialize(); + #else + xnn_log_warning("Unable to determine L1/L2 data cache properties."); + #endif // XNN_ENABLE_CPUINFO +@@ -358,21 +359,33 @@ const struct xnn_hardware_config* xnn_init_hardware_config() { + #if XNN_ARCH_ARM + if (!cpuinfo_has_arm_v6()) { + xnn_log_debug("unsupported hardware: ARMv6 not detected"); ++ #if !XNN_PLATFORM_WEB && XNN_ENABLE_CPUINFO ++ cpuinfo_deinitialize(); ++ #endif + return NULL; + } + + if (!cpuinfo_has_arm_vfpv2() && !cpuinfo_has_arm_vfpv3()) { + xnn_log_debug("unsupported hardware: VFP FPU not detected"); ++ #if !XNN_PLATFORM_WEB && XNN_ENABLE_CPUINFO ++ cpuinfo_deinitialize(); ++ #endif + return NULL; + } + #endif // XNN_ARCH_ARM + #if XNN_ARCH_X86 + if (!cpuinfo_has_x86_sse2()) { + xnn_log_debug("unsupported hardware: SSE2 not detected"); ++ #if !XNN_PLATFORM_WEB && XNN_ENABLE_CPUINFO ++ cpuinfo_deinitialize(); ++ #endif + return NULL; + } + #endif // XNN_ARCH_X86 + + XNN_INIT_ONCE(hardware); ++ #if !XNN_PLATFORM_WEB && !XNN_ARCH_RISCV && !XNN_ARCH_PPC64 && XNN_ENABLE_CPUINFO ++ cpuinfo_deinitialize(); ++ #endif + return &hardware_config; + } diff --git a/cmake/vcpkg-ports/cpuinfo/portfile.cmake b/cmake/vcpkg-ports/cpuinfo/portfile.cmake index 9140a233e2ccd..aa2205b699e07 100644 --- a/cmake/vcpkg-ports/cpuinfo/portfile.cmake +++ b/cmake/vcpkg-ports/cpuinfo/portfile.cmake @@ -6,12 +6,14 @@ endif() vcpkg_from_github( OUT_SOURCE_PATH SOURCE_PATH REPO pytorch/cpuinfo - REF 4628dc060ce4e82345dc166bbac875609db4ff69 - SHA512 db7a93279f2f6daaf825fbd8552935d8ed671d276b65ad614e11f722b6a6848e663850d65180d33b554d67ef1a36aae842feb368699f90be8f21172a1af1924e + REF 66ee79c038d70dad9f08705b2c9b3e58f6d8f512 + SHA512 ec1df3a03e52f50f221fc5f25251b7181316b59b32be61f11dec440ef245bc2070d0e16579ae8fd2748711f120c914f4479619ce1bd6b0bc9dbe462a8526298f HEAD_REF main PATCHES patch_cpuinfo_h_for_arm64ec.patch patch_vcpkg_arm64ec_support.patch # https://github.com/pytorch/cpuinfo/pull/324 + # https://github.com/pytorch/cpuinfo/pull/400 + "${CMAKE_CURRENT_LIST_DIR}/../../patches/cpuinfo/enable_deinit_refcounting.patch" ) vcpkg_check_features(OUT_FEATURE_OPTIONS FEATURE_OPTIONS diff --git a/cmake/vcpkg-ports/xnnpack/portfile.cmake b/cmake/vcpkg-ports/xnnpack/portfile.cmake index 60b3566629e10..6ccfd2909454f 100644 --- a/cmake/vcpkg-ports/xnnpack/portfile.cmake +++ b/cmake/vcpkg-ports/xnnpack/portfile.cmake @@ -11,6 +11,7 @@ vcpkg_from_github( PATCHES fix-build.patch disable_gcc_warning.patch + "${CMAKE_CURRENT_LIST_DIR}/../../patches/xnnpack/release_cpuinfo_after_hardware_config.patch" ) vcpkg_find_acquire_program(PYTHON3) diff --git a/onnxruntime/test/shared_lib/cpuinfo_dlopen_test.cc b/onnxruntime/test/shared_lib/cpuinfo_dlopen_test.cc new file mode 100644 index 0000000000000..e2f97769f0b32 --- /dev/null +++ b/onnxruntime/test/shared_lib/cpuinfo_dlopen_test.cc @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include + +#include +#include + +#ifndef ORT_CPUINFO_DLOPEN_TEST_LIBRARY +#error ORT_CPUINFO_DLOPEN_TEST_LIBRARY must name the test DLL. +#endif + +int wmain() { + HMODULE library = LoadLibraryW(ORT_CPUINFO_DLOPEN_TEST_LIBRARY); + if (library == nullptr) { + std::cerr << "LoadLibraryW failed with error " << GetLastError() << std::endl; + return EXIT_FAILURE; + } + + using GetCpuinfoAllocation = const void* (*)(); + const auto get_cpuinfo_allocation = reinterpret_cast( + GetProcAddress(library, "OrtGetCpuinfoAllocationForTesting")); + if (get_cpuinfo_allocation == nullptr) { + std::cerr << "GetProcAddress failed with error " << GetLastError() << std::endl; + FreeLibrary(library); + return EXIT_FAILURE; + } + + const void* allocation = get_cpuinfo_allocation(); + HANDLE process_heap = GetProcessHeap(); + if (allocation == nullptr || !HeapValidate(process_heap, 0, allocation)) { + std::cerr << "cpuinfo did not return a valid process-heap allocation" << std::endl; + FreeLibrary(library); + return EXIT_FAILURE; + } + + if (!FreeLibrary(library)) { + std::cerr << "FreeLibrary failed with error " << GetLastError() << std::endl; + return EXIT_FAILURE; + } + + if (HeapValidate(process_heap, 0, allocation)) { + std::cerr << "cpuinfo allocation remained valid after the CPUIDInfo test DLL was unloaded" << std::endl; + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/onnxruntime/test/shared_lib/cpuinfo_dlopen_test_library.cc b/onnxruntime/test/shared_lib/cpuinfo_dlopen_test_library.cc new file mode 100644 index 0000000000000..edad81c76388d --- /dev/null +++ b/onnxruntime/test/shared_lib/cpuinfo_dlopen_test_library.cc @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include + +#include "core/common/cpuid_info.h" + +extern "C" const void* OrtGetCpuinfoAllocationForTesting() { + static_cast(onnxruntime::CPUIDInfo::GetCPUIDInfo()); + return cpuinfo_get_processors(); +} diff --git a/onnxruntime/test/shared_lib/cpuinfo_dlopen_test_library.def b/onnxruntime/test/shared_lib/cpuinfo_dlopen_test_library.def new file mode 100644 index 0000000000000..ea4221dc1e5d3 --- /dev/null +++ b/onnxruntime/test/shared_lib/cpuinfo_dlopen_test_library.def @@ -0,0 +1,3 @@ +LIBRARY onnxruntime_cpuinfo_dlopen_test_library +EXPORTS + OrtGetCpuinfoAllocationForTesting diff --git a/onnxruntime/test/shared_lib/cpuinfo_refcount_test.cc b/onnxruntime/test/shared_lib/cpuinfo_refcount_test.cc new file mode 100644 index 0000000000000..7c91ce9f88afd --- /dev/null +++ b/onnxruntime/test/shared_lib/cpuinfo_refcount_test.cc @@ -0,0 +1,223 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include + +#include +#include +#include +#include +#include + +#if defined(ORT_CPUINFO_TEST_USE_XNNPACK) +#include +#endif + +#if defined(ORT_CPUINFO_TEST_HAS_INTERNAL_STATE) +constexpr int kCpuinfoCacheLevelCount = 5; + +extern "C" { +extern bool cpuinfo_is_initialized; +extern struct cpuinfo_processor* cpuinfo_processors; +extern struct cpuinfo_core* cpuinfo_cores; +extern struct cpuinfo_cluster* cpuinfo_clusters; +extern struct cpuinfo_package* cpuinfo_packages; +extern struct cpuinfo_cache* cpuinfo_cache[kCpuinfoCacheLevelCount]; +extern uint32_t cpuinfo_processors_count; +extern uint32_t cpuinfo_cores_count; +extern uint32_t cpuinfo_clusters_count; +extern uint32_t cpuinfo_packages_count; +extern uint32_t cpuinfo_cache_count[kCpuinfoCacheLevelCount]; +extern uint32_t cpuinfo_max_cache_size; +} +#endif + +namespace { + +bool HasValidCpuinfoState() { + return cpuinfo_get_processors_count() != 0 && + cpuinfo_get_processors() != nullptr && + cpuinfo_get_processor(0) != nullptr; +} + +bool IsCpuinfoDeinitialized() { +#if defined(ORT_CPUINFO_TEST_HAS_INTERNAL_STATE) + if (cpuinfo_is_initialized || + cpuinfo_processors != nullptr || + cpuinfo_cores != nullptr || + cpuinfo_clusters != nullptr || + cpuinfo_packages != nullptr || + cpuinfo_processors_count != 0 || + cpuinfo_cores_count != 0 || + cpuinfo_clusters_count != 0 || + cpuinfo_packages_count != 0 || + cpuinfo_max_cache_size != 0) { + return false; + } + + for (int level = 0; level < kCpuinfoCacheLevelCount; ++level) { + if (cpuinfo_cache[level] != nullptr || cpuinfo_cache_count[level] != 0) { + return false; + } + } + + return true; +#else + return true; +#endif +} + +bool TestSequentialConsumers() { + if (!cpuinfo_initialize()) { + std::cerr << "The first cpuinfo_initialize call failed" << std::endl; + return false; + } + + if (!cpuinfo_initialize()) { + std::cerr << "The second cpuinfo_initialize call failed" << std::endl; + cpuinfo_deinitialize(); + return false; + } + + const uint32_t processor_count = cpuinfo_get_processors_count(); + const cpuinfo_processor* processors = cpuinfo_get_processors(); + if (processor_count == 0 || processors == nullptr) { + std::cerr << "cpuinfo did not expose valid processor data" << std::endl; + cpuinfo_deinitialize(); + cpuinfo_deinitialize(); + return false; + } + + cpuinfo_deinitialize(); + + const bool first_consumer_release_preserved_state = + cpuinfo_get_processors_count() == processor_count && + cpuinfo_get_processors() == processors; + + cpuinfo_deinitialize(); + + if (!IsCpuinfoDeinitialized()) { + std::cerr << "cpuinfo remained initialized after the final consumer released it" << std::endl; + return false; + } + + if (!first_consumer_release_preserved_state) { + std::cerr << "cpuinfo released shared state while another consumer was still active" << std::endl; + return false; + } + + if (!cpuinfo_initialize()) { + std::cerr << "cpuinfo failed to reinitialize after the final consumer released it" << std::endl; + return false; + } + cpuinfo_deinitialize(); + + if (!IsCpuinfoDeinitialized()) { + std::cerr << "cpuinfo remained initialized after the reinitialized consumer released it" << std::endl; + return false; + } + + return true; +} + +bool TestConcurrentConsumers() { + constexpr size_t kThreadCount = 8; + constexpr size_t kIterations = 100; + std::atomic initialized_count{0}; + std::atomic validated_count{0}; + std::atomic deinitialized_count{0}; + std::atomic failed{false}; + + const auto consumer = [&]() { + for (size_t iteration = 0; iteration < kIterations; ++iteration) { + const bool initialized = cpuinfo_initialize(); + if (!initialized) { + failed = true; + } + + const size_t expected_count = (iteration + 1) * kThreadCount; + ++initialized_count; + while (initialized_count < expected_count) { + std::this_thread::yield(); + } + + if (initialized && !HasValidCpuinfoState()) { + failed = true; + } + + ++validated_count; + while (validated_count < expected_count) { + std::this_thread::yield(); + } + + if (initialized) { + cpuinfo_deinitialize(); + } + + ++deinitialized_count; + while (deinitialized_count < expected_count) { + std::this_thread::yield(); + } + } + }; + + std::vector consumers; + consumers.reserve(kThreadCount); + for (size_t thread = 0; thread < kThreadCount; ++thread) { + consumers.emplace_back(consumer); + } + for (std::thread& consumer_thread : consumers) { + consumer_thread.join(); + } + + if (failed) { + std::cerr << "cpuinfo failed during concurrent initialization and deinitialization" << std::endl; + return false; + } + + if (!cpuinfo_initialize() || !HasValidCpuinfoState()) { + std::cerr << "cpuinfo failed to reinitialize after concurrent use" << std::endl; + return false; + } + cpuinfo_deinitialize(); + + if (!IsCpuinfoDeinitialized()) { + std::cerr << "cpuinfo remained initialized after concurrent use" << std::endl; + return false; + } + + return true; +} + +#if defined(ORT_CPUINFO_TEST_USE_XNNPACK) +bool TestXnnpackReleasesCpuinfo() { + if (xnn_initialize(nullptr) != xnn_status_success) { + std::cerr << "XNNPACK initialization failed" << std::endl; + return false; + } + + if (!IsCpuinfoDeinitialized()) { + std::cerr << "XNNPACK retained a cpuinfo reference after hardware discovery" << std::endl; + return false; + } + + xnn_deinitialize(); + return true; +} +#endif + +} // namespace + +int main() { + if (!TestSequentialConsumers() || !TestConcurrentConsumers()) { + return EXIT_FAILURE; + } + +#if defined(ORT_CPUINFO_TEST_USE_XNNPACK) + if (!TestXnnpackReleasesCpuinfo()) { + return EXIT_FAILURE; + } +#endif + + return EXIT_SUCCESS; +}