umf/0000755000000000000000000000000014544400200006524 5ustar umf/.clang-format0000644000000000000000000000015114544400200011074 0ustar --- Language: Cpp BasedOnStyle: LLVM IndentWidth: 4 InsertBraces: true ReflowComments: false ... umf/.cmake-format0000644000000000000000000002240114544400200011072 0ustar # ---------------------------------- # Options affecting listfile parsing # ---------------------------------- with section("parse"): # Specify structure for custom cmake functions additional_commands = { 'add_umf_benchmark': { "pargs": 0, "flags": [], 'kwargs': { 'NAME': '*', 'SRCS': '*', 'LIBS': '*' , 'LIBDIRS': '*', 'TESTARGS': '*'}}, 'add_umf_executable': { "pargs": 0, "flags": [], 'kwargs': { 'NAME': '*', 'SRCS': '*', 'LIBS': '*'}}, 'add_umf_test': { "pargs": 0, "flags": [], 'kwargs': { 'NAME': '*', 'SRCS': '*', 'LIBS': '*', 'ENVS': '*'}}, 'add_umf_library': { "pargs": 0, "flags": [], 'kwargs': { 'NAME': '*', 'TYPE': '*', 'SRCS': '*', 'LIBS': '*', 'LINUX_MAP_FILE': '*', 'WINDOWS_DEF_FILE': '*'}}, 'set_tests_properties': { "pargs": 2, "flags": [], 'kwargs': { 'LABELS': '*', 'PASS_REGULAR_EXPRESSION': '*'}}, 'build_umf_test': { "pargs": 0, "flags": [], 'kwargs': { 'NAME': '*', 'SRCS': '*', 'LIBS': '*' }}, 'add_umf_ipc_test': { "pargs": 0, "flags": [], 'kwargs': { 'TEST': '*', 'SRC_DIR': '*'}}, } # Override configurations per-command where available override_spec = {} # Specify variable tags. vartags = [] # Specify property tags. proptags = [] # ----------------------------- # Options affecting formatting. # ----------------------------- with section("format"): # Disable formatting entirely, making cmake-format a no-op disable = False # How wide to allow formatted cmake files line_width = 80 # How many spaces to tab for indent tab_size = 4 # If true, lines are indented using tab characters (utf-8 0x09) instead of # space characters (utf-8 0x20). In cases where the layout would # require a fractional tab character, the behavior of the fractional # indentation is governed by use_tabchars = False # If is True, then the value of this variable indicates how # fractional indentions are handled during whitespace replacement. If set to # 'use-space', fractional indentation is left as spaces (utf-8 0x20). If set # to `round-up` fractional indentation is replaced with a single tab character # (utf-8 0x09) effectively shifting the column to the next tabstop fractional_tab_policy = 'use-space' # If an argument group contains more than this many sub-groups (parg or kwarg # groups) then force it to a vertical layout. max_subgroups_hwrap = 2 # If a positional argument group contains more than this many arguments, then # force it to a vertical layout. max_pargs_hwrap = 4 # If a cmdline positional group consumes more than this many lines without # nesting, then invalidate the layout (and nest) max_rows_cmdline = 2 # If true, separate flow control names from their parentheses with a space separate_ctrl_name_with_space = False # If true, separate function names from parentheses with a space separate_fn_name_with_space = False # If a statement is wrapped to more than one line, than dangle the closing # parenthesis on its own line. dangle_parens = False # If the trailing parenthesis must be 'dangled' on its on line, then align it # to this reference: `prefix`: the start of the statement, `prefix-indent`: # the start of the statement, plus one indentation level, `child`: align to # the column of the arguments dangle_align = 'prefix' # If the statement spelling length (including space and parenthesis) is # smaller than this amount, then force reject nested layouts. min_prefix_chars = 4 # If the statement spelling length (including space and parenthesis) is larger # than the tab width by more than this amount, then force reject un-nested # layouts. max_prefix_chars = 10 # If a candidate layout is wrapped horizontally but it exceeds this many # lines, then reject the layout. max_lines_hwrap = 2 # What style line endings to use in the output. line_ending = 'unix' # Format command names consistently as 'lower' or 'upper' case command_case = 'canonical' # Format keywords consistently as 'lower' or 'upper' case keyword_case = 'unchanged' # A list of command names which should always be wrapped always_wrap = [] # If true, the argument lists which are known to be sortable will be sorted # lexicographicall enable_sort = True # If true, the parsers may infer whether or not an argument list is sortable # (without annotation). autosort = False # By default, if cmake-format cannot successfully fit everything into the # desired linewidth it will apply the last, most aggressive attempt that it # made. If this flag is True, however, cmake-format will print error, exit # with non-zero status code, and write-out nothing require_valid_layout = False # A dictionary mapping layout nodes to a list of wrap decisions. See the # documentation for more information. layout_passes = {} # ------------------------------------------------ # Options affecting comment reflow and formatting. # ------------------------------------------------ with section("markup"): # What character to use for bulleted lists bullet_char = '*' # What character to use as punctuation after numerals in an enumerated list enum_char = '.' # If comment markup is enabled, don't reflow the first comment block in each # listfile. Use this to preserve formatting of your copyright/license # statements. first_comment_is_literal = True # If comment markup is enabled, don't reflow any comment block which matches # this (regex) pattern. Default is `None` (disabled). literal_comment_pattern = None # Regular expression to match preformat fences in comments default= # ``r'^\s*([`~]{3}[`~]*)(.*)$'`` fence_pattern = '^\\s*([`~]{3}[`~]*)(.*)$' # Regular expression to match rulers in comments default= # ``r'^\s*[^\w\s]{3}.*[^\w\s]{3}$'`` ruler_pattern = '^\\s*[^\\w\\s]{3}.*[^\\w\\s]{3}$' # If a comment line matches starts with this pattern then it is explicitly a # trailing comment for the preceding argument. Default is '#<' explicit_trailing_pattern = '#<' # If a comment line starts with at least this many consecutive hash # characters, then don't lstrip() them off. This allows for lazy hash rulers # where the first hash char is not separated by space hashruler_min_length = 10 # If true, then insert a space between the first hash char and remaining hash # chars in a hash ruler, and normalize its length to fill the column canonicalize_hashrulers = True # enable comment markup parsing and reflow enable_markup = True # ---------------------------- # Options affecting the linter # ---------------------------- with section("lint"): # a list of lint codes to disable disabled_codes = [] # regular expression pattern describing valid function names function_pattern = '[0-9a-z_]+' # regular expression pattern describing valid macro names macro_pattern = '[0-9A-Z_]+' # regular expression pattern describing valid names for variables with global # (cache) scope global_var_pattern = '[A-Z][0-9A-Z_]+' # regular expression pattern describing valid names for variables with global # scope (but internal semantic) internal_var_pattern = '_[A-Z][0-9A-Z_]+' # regular expression pattern describing valid names for variables with local # scope local_var_pattern = '[a-z][a-z0-9_]+' # regular expression pattern describing valid names for privatedirectory # variables private_var_pattern = '_[0-9a-z_]+' # regular expression pattern describing valid names for public directory # variables public_var_pattern = '[A-Z][0-9A-Z_]+' # regular expression pattern describing valid names for function/macro # arguments and loop variables. argument_var_pattern = '[a-z][a-z0-9_]+' # regular expression pattern describing valid names for keywords used in # functions or macros keyword_pattern = '[A-Z][0-9A-Z_]+' # In the heuristic for C0201, how many conditionals to match within a loop in # before considering the loop a parser. max_conditionals_custom_parser = 2 # Require at least this many newlines between statements min_statement_spacing = 1 # Require no more than this many newlines between statements max_statement_spacing = 2 max_returns = 6 max_branches = 12 max_arguments = 5 max_localvars = 15 max_statements = 50 # ------------------------------- # Options affecting file encoding # ------------------------------- with section("encode"): # If true, emit the unicode byte-order mark (BOM) at the start of the file emit_byteorder_mark = False # Specify the encoding of the input file. Defaults to utf-8 input_encoding = 'utf-8' # Specify the encoding of the output file. Defaults to utf-8. Note that cmake # only claims to support utf-8 so be careful when using anything else output_encoding = 'utf-8' # ------------------------------------- # Miscellaneous configurations options. # ------------------------------------- with section("misc"): # A dictionary containing any per-command configuration overrides. Currently # only `command_case` is supported. per_command = {} umf/.mailmap0000644000000000000000000000060614544400200010147 0ustar Rafał Rudnicki Igor Chorążewicz <83662296+szadam@users.noreply.github.com> Adam Szopiński Adam Szopiński Adam Adam Szopiński szadam Sergei Vinogradov umf/.trivyignore0000644000000000000000000000042314544400200011105 0ustar # Docs: https://aquasecurity.github.io/trivy/latest/docs/configuration/filtering/#trivyignore # In docker files: # HEALTHCHECK is not required for development, nor in CI (failed docker = failed CI). # We're not hosting any application with usage of the dockers. AVD-DS-0026 umf/CMakeLists.txt0000644000000000000000000011022314544400200011263 0ustar # Copyright (C) 2022-2025 Intel Corporation # Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception cmake_minimum_required(VERSION 3.14.0 FATAL_ERROR) # needed when UMF is used as an external project set(UMF_CMAKE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) list(APPEND CMAKE_MODULE_PATH "${UMF_CMAKE_SOURCE_DIR}/cmake") # Use full path of the helpers module (to omit potential conflicts with others) include(${UMF_CMAKE_SOURCE_DIR}/cmake/helpers.cmake) # --------------------------------------------------------------------------- # # Set UMF version variables, define project, and add basic modules # --------------------------------------------------------------------------- # # We use semver aligned version, set via git tags. We parse git output to # establish the version of UMF to be used in CMake, Win dll's, and within the # code (e.g. in logger). We have 3-component releases (e.g. 1.5.1) plus release # candidates and git info. Function below sets all variables related to version. set_version_variables() message(STATUS "UMF version: ${UMF_VERSION}") # version we set in CMake is abbreviated just to major.minor.patch project( umf VERSION ${UMF_CMAKE_VERSION} LANGUAGES C) if(UMF_CMAKE_VERSION VERSION_EQUAL "0.0.0") message( WARNING "UMF version is set to 0.0.0, which most likely is not expected! " "Please checkout the git tags to get a proper version.") endif() if(PROJECT_VERSION_PATCH GREATER 0) # set extra variable for Windows dll metadata set(UMF_VERSION_BUGFIX 1) endif() include(CTest) include(CMakePackageConfigHelpers) include(GNUInstallDirs) include(FetchContent) find_package(PkgConfig) # --------------------------------------------------------------------------- # # Set UMF build options (and CACHE variables) # --------------------------------------------------------------------------- # # Define a list to store the names of all options set(UMF_OPTIONS_LIST "") list(APPEND UMF_OPTIONS_LIST CMAKE_BUILD_TYPE) # Define a macro to wrap the option() command and track the options macro(umf_option) list(APPEND UMF_OPTIONS_LIST ${ARGV0}) option(${ARGV}) endmacro() # All CMake options have to be explicitly set in the build_umfd target's # configuration command umf_option(UMF_BUILD_SHARED_LIBRARY "Build UMF as shared library" OFF) umf_option(UMF_BUILD_LEVEL_ZERO_PROVIDER "Build Level Zero memory provider" ON) umf_option(UMF_BUILD_CUDA_PROVIDER "Build CUDA memory provider" ON) umf_option(UMF_BUILD_LIBUMF_POOL_JEMALLOC "Build the libumf_pool_jemalloc static library" OFF) umf_option(UMF_BUILD_TESTS "Build UMF tests" ON) umf_option(UMF_BUILD_GPU_TESTS "Build UMF GPU tests" OFF) umf_option(UMF_BUILD_BENCHMARKS "Build UMF benchmarks" OFF) umf_option(UMF_BUILD_BENCHMARKS_MT "Build UMF multithreaded benchmarks" OFF) umf_option(UMF_BUILD_EXAMPLES "Build UMF examples" ON) umf_option(UMF_BUILD_GPU_EXAMPLES "Build UMF GPU examples" OFF) umf_option(UMF_BUILD_FUZZTESTS "Build UMF fuzz tests" OFF) umf_option( UMF_DISABLE_HWLOC "Disable hwloc and UMF features requiring it (OS provider, memtargets, topology discovery)" OFF) umf_option( UMF_LINK_HWLOC_STATICALLY "Link UMF with HWLOC library statically (proxy library will be disabled on Windows+Debug build)" OFF) set(UMF_HWLOC_NAME "hwloc" CACHE STRING "Custom name for hwloc library w/o extension") list(APPEND UMF_OPTIONS_LIST UMF_HWLOC_NAME) set(UMF_INSTALL_RPATH "" CACHE STRING "Set the runtime search path to the directory with dependencies (e.g. hwloc)" ) umf_option(UMF_USE_DEBUG_POSTFIX "Add a 'd' postfix to Windows debug libraries" OFF) umf_option(UMF_DEVELOPER_MODE "Enable additional developer checks" OFF) umf_option( UMF_FORMAT_CODE_STYLE "Add clang, cmake, and black -format-check and -format-apply targets" OFF) # Only a part of skips is treated as a failure now. TODO: extend to all tests umf_option(UMF_TESTS_FAIL_ON_SKIP "Treat skips in tests as fail" OFF) umf_option(UMF_USE_ASAN "Enable AddressSanitizer checks" OFF) umf_option(UMF_USE_UBSAN "Enable UndefinedBehaviorSanitizer checks" OFF) umf_option(UMF_USE_TSAN "Enable ThreadSanitizer checks" OFF) umf_option(UMF_USE_MSAN "Enable MemorySanitizer checks" OFF) umf_option(UMF_USE_VALGRIND "Enable Valgrind instrumentation" OFF) umf_option(UMF_USE_COVERAGE "Build with coverage enabled (Linux only)" OFF) # set UMF_PROXY_LIB_BASED_ON_POOL to one of: SCALABLE or JEMALLOC set(KNOWN_PROXY_LIB_POOLS SCALABLE JEMALLOC) set(UMF_PROXY_LIB_BASED_ON_POOL SCALABLE CACHE STRING "A UMF pool the proxy library is based on (SCALABLE or JEMALLOC)") set_property(CACHE UMF_PROXY_LIB_BASED_ON_POOL PROPERTY STRINGS ${KNOWN_PROXY_LIB_POOLS}) list(APPEND UMF_OPTIONS_LIST UMF_PROXY_LIB_BASED_ON_POOL) # --------------------------------------------------------------------------- # # Setup required variables, definitions; fetch dependencies; include # sub_directories based on build options; set flags; etc. # --------------------------------------------------------------------------- # message(STATUS "CMAKE_GENERATOR: ${CMAKE_GENERATOR}") if(UMF_BUILD_TESTS AND DEFINED ENV{CI} AND NOT UMF_TESTS_FAIL_ON_SKIP) message( FATAL_ERROR "Env variable 'CI' is set, tests are enabled, but UMF_TESTS_FAIL_ON_SKIP is not. " "Please set UMF_TESTS_FAIL_ON_SKIP to ON in all CI workflows running tests." ) endif() if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") set(LINUX TRUE) set(OS_NAME "linux") elseif(${CMAKE_SYSTEM_NAME} MATCHES "Windows") set(WINDOWS TRUE) set(OS_NAME "windows") elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") set(MACOSX TRUE) set(OS_NAME "macosx") else() message(FATAL_ERROR "Unknown OS type") endif() if(UMF_DEVELOPER_MODE) set(UMF_COMMON_COMPILE_DEFINITIONS ${UMF_COMMON_COMPILE_DEFINITIONS} UMF_DEVELOPER_MODE=1) endif() message(STATUS "CMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH}") if(NOT UMF_BUILD_LIBUMF_POOL_JEMALLOC) set(UMF_POOL_JEMALLOC_ENABLED FALSE) set(JEMALLOC_FOUND FALSE) set(JEMALLOC_LIBRARIES FALSE) elseif(WINDOWS) pkg_check_modules(JEMALLOC jemalloc) if(NOT JEMALLOC_FOUND) find_package(JEMALLOC REQUIRED jemalloc) endif() else() if(NOT DEFINED UMF_JEMALLOC_REPO) set(UMF_JEMALLOC_REPO "https://github.com/jemalloc/jemalloc.git") endif() if(NOT DEFINED UMF_JEMALLOC_TAG) set(UMF_JEMALLOC_TAG 5.3.0) endif() message( STATUS "Will fetch jemalloc from ${UMF_JEMALLOC_REPO} (tag: ${UMF_JEMALLOC_TAG})" ) FetchContent_Declare( jemalloc_targ GIT_REPOSITORY ${UMF_JEMALLOC_REPO} GIT_TAG ${UMF_JEMALLOC_TAG}) FetchContent_MakeAvailable(jemalloc_targ) add_custom_command( COMMAND ./autogen.sh WORKING_DIRECTORY ${jemalloc_targ_SOURCE_DIR} OUTPUT ${jemalloc_targ_SOURCE_DIR}/configure) add_custom_command( # Custom jemalloc build. Non-default options used: # --with-jemalloc-prefix=je_ - add je_ prefix to all public APIs # --disable-cxx - Disable C++ integration. This will cause new and # delete operators implementations to be omitted. # --disable-initial-exec-tls - Disable the initial-exec TLS model for # jemalloc's internal thread-local storage (on those platforms that # support explicit settings). This can allow jemalloc to be dynamically # loaded after program startup (e.g. using dlopen). --disable-doc - # Disable building and installing the documentation. COMMAND ./configure --prefix=${jemalloc_targ_BINARY_DIR} --with-jemalloc-prefix=je_ --disable-cxx --disable-initial-exec-tls --disable-doc CFLAGS=-fPIC WORKING_DIRECTORY ${jemalloc_targ_SOURCE_DIR} OUTPUT ${jemalloc_targ_SOURCE_DIR}/Makefile DEPENDS ${jemalloc_targ_SOURCE_DIR}/configure) if(NOT UMF_QEMU_BUILD) set(MAKE_ARGUMENTS "-j$(nproc)") endif() add_custom_command( COMMAND make ${MAKE_ARGUMENTS} WORKING_DIRECTORY ${jemalloc_targ_SOURCE_DIR} OUTPUT ${jemalloc_targ_SOURCE_DIR}/lib/libjemalloc.a DEPENDS ${jemalloc_targ_SOURCE_DIR}/Makefile) add_custom_command( COMMAND make install WORKING_DIRECTORY ${jemalloc_targ_SOURCE_DIR} OUTPUT ${jemalloc_targ_BINARY_DIR}/lib/libjemalloc.a DEPENDS ${jemalloc_targ_SOURCE_DIR}/lib/libjemalloc.a) add_custom_target(jemalloc_prod DEPENDS ${jemalloc_targ_BINARY_DIR}/lib/libjemalloc.a) add_library(jemalloc INTERFACE) target_link_libraries( jemalloc INTERFACE ${jemalloc_targ_BINARY_DIR}/lib/libjemalloc.a) add_dependencies(jemalloc jemalloc_prod) set(JEMALLOC_LIBRARY_DIRS ${jemalloc_targ_BINARY_DIR}/lib) set(JEMALLOC_INCLUDE_DIRS ${jemalloc_targ_BINARY_DIR}/include) set(JEMALLOC_LIBRARIES ${jemalloc_targ_BINARY_DIR}/lib/libjemalloc.a) endif() if(JEMALLOC_FOUND OR JEMALLOC_LIBRARIES) set(UMF_POOL_JEMALLOC_ENABLED TRUE) # add PATH to DLL on Windows set(DLL_PATH_LIST "${DLL_PATH_LIST};PATH=path_list_append:${JEMALLOC_DLL_DIRS}") message(STATUS " JEMALLOC_LIBRARIES = ${JEMALLOC_LIBRARIES}") message(STATUS " JEMALLOC_INCLUDE_DIRS = ${JEMALLOC_INCLUDE_DIRS}") message(STATUS " JEMALLOC_LIBRARY_DIRS = ${JEMALLOC_LIBRARY_DIRS}") if(WINDOWS) message(STATUS " JEMALLOC_DLL_DIRS = ${JEMALLOC_DLL_DIRS}") endif() else() set(UMF_POOL_JEMALLOC_ENABLED FALSE) message( STATUS "Disabling the Jemalloc Pool and tests and benchmarks that use it because jemalloc was not built/found." ) endif() if(NOT UMF_DISABLE_HWLOC AND (NOT UMF_LINK_HWLOC_STATICALLY)) pkg_check_modules(LIBHWLOC hwloc>=2.3.0) if(NOT LIBHWLOC_FOUND) find_package(LIBHWLOC 2.3.0 COMPONENTS hwloc) if(LIBHWLOC_LIBRARIES) set(LIBHWLOC_AVAILABLE TRUE) endif() endif() if(LIBHWLOC_AVAILABLE OR LIBHWLOC_FOUND) # add PATH to DLL on Windows set(DLL_PATH_LIST "${DLL_PATH_LIST};PATH=path_list_append:${LIBHWLOC_DLL_DIRS}") else() set(UMF_LINK_HWLOC_STATICALLY ON) endif() endif() if(UMF_LINK_HWLOC_STATICALLY AND LINUX) find_program(AUTORECONF_EXECUTABLE autoreconf) if(NOT AUTORECONF_EXECUTABLE) message(WARNING "autoreconf is not installed. Disabling hwloc.") set(UMF_DISABLE_HWLOC ON) set(UMF_LINK_HWLOC_STATICALLY OFF) endif() endif() if(UMF_DISABLE_HWLOC) message(STATUS "hwloc is disabled, hence OS provider, memtargets, " "topology discovery, examples won't be available!") else() if(UMF_LINK_HWLOC_STATICALLY) if(NOT DEFINED UMF_HWLOC_REPO) set(UMF_HWLOC_REPO "https://github.com/open-mpi/hwloc.git") endif() if(NOT DEFINED UMF_HWLOC_TAG) set(UMF_HWLOC_TAG hwloc-2.10.0) endif() message( STATUS "Will fetch hwloc from ${UMF_HWLOC_REPO} (tag: ${UMF_HWLOC_TAG})" ) if(WINDOWS) set(HWLOC_ENABLE_TESTING OFF) set(HWLOC_SKIP_LSTOPO ON) set(HWLOC_SKIP_TOOLS ON) set(HWLOC_SKIP_INCLUDES ON) FetchContent_Declare( hwloc_targ GIT_REPOSITORY ${UMF_HWLOC_REPO} GIT_TAG ${UMF_HWLOC_TAG} SOURCE_SUBDIR contrib/windows-cmake/) FetchContent_MakeAvailable(hwloc_targ) set(HWLOC_LIB_PATH "") if(CMAKE_GENERATOR STREQUAL "NMake Makefiles") set(HWLOC_LIB_PATH "${hwloc_targ_BINARY_DIR}/hwloc.lib") else() set(HWLOC_LIB_PATH "${hwloc_targ_BINARY_DIR}/lib/hwloc.lib") endif() get_filename_component(LIBHWLOC_LIBRARY_DIRS ${HWLOC_LIB_PATH} DIRECTORY) set(LIBHWLOC_LIBRARIES ${HWLOC_LIB_PATH}) set(LIBHWLOC_INCLUDE_DIRS ${hwloc_targ_BINARY_DIR}/include) else() # not Windows FetchContent_Declare( hwloc_targ GIT_REPOSITORY ${UMF_HWLOC_REPO} GIT_TAG ${UMF_HWLOC_TAG}) FetchContent_MakeAvailable(hwloc_targ) add_custom_command( COMMAND ./autogen.sh WORKING_DIRECTORY ${hwloc_targ_SOURCE_DIR} OUTPUT ${hwloc_targ_SOURCE_DIR}/configure) add_custom_command( COMMAND ./configure --prefix=${hwloc_targ_BINARY_DIR} --enable-static=yes --enable-shared=no --disable-libxml2 --disable-pci --disable-levelzero --disable-opencl --disable-cuda --disable-nvml --disable-libudev --disable-rsmi CFLAGS=-fPIC CXXFLAGS=-fPIC WORKING_DIRECTORY ${hwloc_targ_SOURCE_DIR} OUTPUT ${hwloc_targ_SOURCE_DIR}/Makefile DEPENDS ${hwloc_targ_SOURCE_DIR}/configure) add_custom_command( COMMAND make WORKING_DIRECTORY ${hwloc_targ_SOURCE_DIR} OUTPUT ${hwloc_targ_SOURCE_DIR}/lib/libhwloc.la DEPENDS ${hwloc_targ_SOURCE_DIR}/Makefile) add_custom_command( COMMAND make install WORKING_DIRECTORY ${hwloc_targ_SOURCE_DIR} OUTPUT ${hwloc_targ_BINARY_DIR}/lib/libhwloc.a DEPENDS ${hwloc_targ_SOURCE_DIR}/lib/libhwloc.la) add_custom_target(hwloc_prod DEPENDS ${hwloc_targ_BINARY_DIR}/lib/libhwloc.a) add_library(hwloc INTERFACE) target_link_libraries( hwloc INTERFACE ${hwloc_targ_BINARY_DIR}/lib/libhwloc.a) add_dependencies(hwloc hwloc_prod) set(LIBHWLOC_LIBRARY_DIRS ${hwloc_targ_BINARY_DIR}/lib) set(LIBHWLOC_INCLUDE_DIRS ${hwloc_targ_BINARY_DIR}/include) set(LIBHWLOC_LIBRARIES ${hwloc_targ_BINARY_DIR}/lib/libhwloc.a) endif() endif() # UMF_LINK_HWLOC_STATICALLY message(STATUS " LIBHWLOC_LIBRARIES = ${LIBHWLOC_LIBRARIES}") message(STATUS " LIBHWLOC_INCLUDE_DIRS = ${LIBHWLOC_INCLUDE_DIRS}") message(STATUS " LIBHWLOC_LIBRARY_DIRS = ${LIBHWLOC_LIBRARY_DIRS}") message(STATUS " LIBHWLOC_API_VERSION = ${LIBHWLOC_API_VERSION}") if(WINDOWS) message(STATUS " LIBHWLOC_DLL_DIRS = ${LIBHWLOC_DLL_DIRS}") endif() endif() if(hwloc_targ_SOURCE_DIR) # Apply security patch for HWLOC execute_process( COMMAND git apply ${PROJECT_SOURCE_DIR}/cmake/fix_coverity_issues.patch WORKING_DIRECTORY ${hwloc_targ_SOURCE_DIR} OUTPUT_VARIABLE UMF_HWLOC_PATCH_OUTPUT ERROR_VARIABLE UMF_HWLOC_PATCH_ERROR) if(UMF_HWLOC_PATCH_OUTPUT) message(STATUS "HWLOC patch command output:\n${UMF_HWLOC_PATCH_OUTPUT}") endif() if(UMF_HWLOC_PATCH_ERROR) message(WARNING "HWLOC patch command output:\n${UMF_HWLOC_PATCH_ERROR}") endif() endif() # Fetch L0 loader only if needed i.e.: if building L0 provider is ON and L0 # headers are not provided by the user (via setting UMF_LEVEL_ZERO_INCLUDE_DIR). if(UMF_BUILD_LEVEL_ZERO_PROVIDER AND (NOT UMF_LEVEL_ZERO_INCLUDE_DIR)) set(LEVEL_ZERO_LOADER_REPO "https://github.com/oneapi-src/level-zero.git") set(LEVEL_ZERO_LOADER_TAG v1.20.2) message( STATUS "Fetching L0 loader (${LEVEL_ZERO_LOADER_TAG}) from ${LEVEL_ZERO_LOADER_REPO} ..." ) FetchContent_Declare( level-zero-loader GIT_REPOSITORY ${LEVEL_ZERO_LOADER_REPO} GIT_TAG ${LEVEL_ZERO_LOADER_TAG} EXCLUDE_FROM_ALL) FetchContent_MakeAvailable(level-zero-loader) set(LEVEL_ZERO_INCLUDE_DIRS ${level-zero-loader_SOURCE_DIR}/include CACHE PATH "Path to Level Zero Headers") message(STATUS "Level Zero include directory: ${LEVEL_ZERO_INCLUDE_DIRS}") elseif(UMF_BUILD_LEVEL_ZERO_PROVIDER) # Only header is needed to build UMF set(LEVEL_ZERO_INCLUDE_DIRS ${UMF_LEVEL_ZERO_INCLUDE_DIR}) message(STATUS "Level Zero include directory: ${LEVEL_ZERO_INCLUDE_DIRS}") endif() # Fetch CUDA only if needed i.e.: if building CUDA provider is ON and CUDA # headers are not provided by the user (via setting UMF_CUDA_INCLUDE_DIR). if(UMF_BUILD_CUDA_PROVIDER AND (NOT UMF_CUDA_INCLUDE_DIR)) set(CUDA_REPO "https://gitlab.com/nvidia/headers/cuda-individual/cudart.git") set(CUDA_TAG cuda-12.5.1) message(STATUS "Fetching CUDA ${CUDA_TAG} from ${CUDA_REPO} ...") FetchContent_Declare( cuda-headers GIT_REPOSITORY ${CUDA_REPO} GIT_TAG ${CUDA_TAG} EXCLUDE_FROM_ALL) FetchContent_MakeAvailable(cuda-headers) set(CUDA_INCLUDE_DIRS ${cuda-headers_SOURCE_DIR} CACHE PATH "Path to CUDA headers") message(STATUS "CUDA_INCLUDE_DIRS = ${CUDA_INCLUDE_DIRS}") elseif(UMF_BUILD_CUDA_PROVIDER) # Only header is needed to build UMF set(CUDA_INCLUDE_DIRS ${UMF_CUDA_INCLUDE_DIR}) message(STATUS "CUDA_INCLUDE_DIRS = ${CUDA_INCLUDE_DIRS}") endif() # Build the umfd target in a separate directory with Debug configuration if(WINDOWS AND UMF_USE_DEBUG_POSTFIX) # The build_umfd target's configuration command requires to have # CMAKE_PREFIX_PATH with semicolons escaped string(JOIN "\;" UMFD_CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH}) add_custom_target( build_umfd ALL COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" -S ${UMF_CMAKE_SOURCE_DIR} -B ${CMAKE_BINARY_DIR}/umfd_build -DCMAKE_BUILD_TYPE=Debug -DCMAKE_DEBUG_POSTFIX=d -DCMAKE_PREFIX_PATH="${UMFD_CMAKE_PREFIX_PATH}" -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} -DUMF_USE_DEBUG_POSTFIX=OFF -DUMF_BUILD_SHARED_LIBRARY=${UMF_BUILD_SHARED_LIBRARY} -DUMF_BUILD_LEVEL_ZERO_PROVIDER=${UMF_BUILD_LEVEL_ZERO_PROVIDER} -DUMF_BUILD_CUDA_PROVIDER=${UMF_BUILD_CUDA_PROVIDER} -DUMF_BUILD_LIBUMF_POOL_JEMALLOC=${UMF_BUILD_LIBUMF_POOL_JEMALLOC} -DUMF_BUILD_TESTS=OFF -DUMF_BUILD_GPU_TESTS=OFF -DUMF_BUILD_BENCHMARKS=OFF -DUMF_BUILD_BENCHMARKS_MT=OFF -DUMF_BUILD_EXAMPLES=OFF -DUMF_BUILD_GPU_EXAMPLES=OFF -DUMF_BUILD_FUZZTESTS=OFF -DUMF_DISABLE_HWLOC=${UMF_DISABLE_HWLOC} -DUMF_LINK_HWLOC_STATICALLY=${UMF_LINK_HWLOC_STATICALLY} -DUMF_HWLOC_NAME=${UMF_HWLOC_NAME} -DUMF_INSTALL_RPATH=${UMF_INSTALL_RPATH} -DUMF_DEVELOPER_MODE=OFF -DUMF_FORMAT_CODE_STYLE=OFF -DUMF_TESTS_FAIL_ON_SKIP=OFF -DUMF_USE_ASAN=OFF -DUMF_USE_UBSAN=OFF -DUMF_USE_TSAN=OFF -DUMF_USE_MSAN=OFF -DUMF_USE_VALGRIND=OFF -DUMF_USE_COVERAGE=OFF -DUMF_PROXY_LIB_BASED_ON_POOL=${UMF_PROXY_LIB_BASED_ON_POOL} COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR}/umfd_build --target umf --config Debug COMMENT "Configuring and building umfd.dll in a separate directory with Debug configuration" ) # Copy built UMF libraries to the main binary directory and remove # umfd_build if(CMAKE_CONFIGURATION_TYPES) # Multi-config generator (e.g., Visual Studio) set(UMFD_DLL_SRC "${CMAKE_BINARY_DIR}/umfd_build/bin/Debug/umfd.dll") set(UMFD_LIB_SRC "${CMAKE_BINARY_DIR}/umfd_build/lib/Debug/umfd.lib") set(UMFD_DLL "${CMAKE_BINARY_DIR}/bin/$/umfd.dll") set(UMFD_LIB "${CMAKE_BINARY_DIR}/lib/$/umfd.lib") else() # Single-config generator (e.g., Ninja) set(UMFD_DLL_SRC "${CMAKE_BINARY_DIR}/umfd_build/bin/umfd.dll") set(UMFD_LIB_SRC "${CMAKE_BINARY_DIR}/umfd_build/lib/umfd.lib") set(UMFD_DLL "${CMAKE_BINARY_DIR}/bin/umfd.dll") set(UMFD_LIB "${CMAKE_BINARY_DIR}/lib/umfd.lib") endif() if(UMF_BUILD_SHARED_LIBRARY) add_custom_command( TARGET build_umfd COMMAND ${CMAKE_COMMAND} -E copy_if_different ${UMFD_DLL_SRC} ${UMFD_DLL} COMMENT "Copying umfd.dll to the main binary directory") endif() add_custom_command( TARGET build_umfd COMMAND ${CMAKE_COMMAND} -E copy_if_different ${UMFD_LIB_SRC} ${UMFD_LIB} COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/umfd_build DEPENDS ${UMFD_DLL} COMMENT "Copying umfd.lib to the main library directory") endif() # This build type check is not possible on Windows when CMAKE_BUILD_TYPE is not # set, because in this case the build type is determined after a CMake # configuration is done (at the build time) if(NOT WINDOWS) set(KNOWN_BUILD_TYPES Release Debug RelWithDebInfo MinSizeRel) string(REPLACE ";" " " KNOWN_BUILD_TYPES_STR "${KNOWN_BUILD_TYPES}") if(NOT CMAKE_BUILD_TYPE) message( STATUS "No build type selected (CMAKE_BUILD_TYPE), defaulting to Release" ) set(CMAKE_BUILD_TYPE "Release") else() message(STATUS "CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}") if(NOT CMAKE_BUILD_TYPE IN_LIST KNOWN_BUILD_TYPES) message( WARNING "Unusual build type was set (${CMAKE_BUILD_TYPE}), please make sure it is a correct one. " "The following ones are supported by default: ${KNOWN_BUILD_TYPES_STR}." ) endif() endif() set(CMAKE_BUILD_TYPE "${CMAKE_BUILD_TYPE}" CACHE STRING "Choose the type of build, options are: ${KNOWN_BUILD_TYPES_STR} ..." FORCE) set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS ${KNOWN_BUILD_TYPES}) endif() # For using the options listed in the OPTIONS_REQUIRING_CXX variable a C++17 # compiler is required. Moreover, if these options are not set, CMake will set # up a strict C build, without C++ support. set(OPTIONS_REQUIRING_CXX "UMF_BUILD_TESTS" "UMF_BUILD_BENCHMARKS_MT" "UMF_BUILD_BENCHMARKS") foreach(option_name ${OPTIONS_REQUIRING_CXX}) if(${option_name}) enable_language(CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED YES) break() endif() endforeach() set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) set(CMAKE_UMF_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) if(CMAKE_GENERATOR MATCHES "Visual Studio" OR CMAKE_GENERATOR MATCHES "Ninja Multi-Config") set(CMAKE_UMF_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/$) endif() # Sanitizer flags if(UMF_USE_ASAN) add_sanitizer_flag(address) endif() if(UMF_USE_UBSAN) add_sanitizer_flag(undefined) endif() if(UMF_USE_TSAN) add_sanitizer_flag(thread) endif() if(UMF_USE_MSAN) message(WARNING "MemorySanitizer requires instrumented libraries to " "prevent reporting false-positives") add_sanitizer_flag(memory) endif() # Fuzzer instrumentation for the whole library if(UMF_BUILD_FUZZTESTS AND CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND LINUX) add_compile_options("-fsanitize=fuzzer-no-link") add_link_options("-fsanitize=fuzzer-no-link") endif() # A header-only lib to specify include directories in transitive dependencies add_library(umf_headers INTERFACE) # Alias target to support FetchContent. add_library(${PROJECT_NAME}::headers ALIAS umf_headers) target_include_directories( umf_headers INTERFACE $ $) if(WINDOWS) add_compile_definitions(_CRT_SECURE_NO_WARNINGS) # set PATH to DLLs on Windows set(DLL_PATH_LIST "${DLL_PATH_LIST};PATH=path_list_append:${PROJECT_BINARY_DIR}/bin/$" ) # add path to the proxy lib DLL set(DLL_PATH_LIST "${DLL_PATH_LIST};PATH=path_list_append:${PROJECT_BINARY_DIR}/src/proxy_lib" ) # MSVC implicitly adds $ to the output path set(DLL_PATH_LIST "${DLL_PATH_LIST};PATH=path_list_append:${PROJECT_BINARY_DIR}/src/proxy_lib/$" ) endif() pkg_check_modules(TBB tbb) if(NOT TBB_FOUND) find_package(TBB OPTIONAL_COMPONENTS tbb) endif() if(TBB_FOUND OR TBB_LIBRARY_DIRS) # add PATH to DLL on Windows set(DLL_PATH_LIST "${DLL_PATH_LIST};PATH=path_list_append:${TBB_DLL_DIRS}") set(UMF_POOL_SCALABLE_ENABLED TRUE) else() message( STATUS "Disabling tests and benchmarks that use the Scalable Pool because the TBB they require was not found." ) set(UMF_POOL_SCALABLE_ENABLED FALSE) endif() if(WINDOWS) # TODO: enable the proxy library in the Debug build on Windows # # In MSVC builds, there is no way to determine the actual build type during # the CMake configuration step. Therefore, this message is printed in all # MSVC builds. if(UMF_LINK_HWLOC_STATICALLY) message( STATUS "The proxy library will be disabled - static linkage with hwloc is not supported yet" ) else() message( STATUS "The proxy library will be built, however it is supported only in the Release build on Windows" ) endif() endif() # set UMF_PROXY_LIB_ENABLED if(UMF_DISABLE_HWLOC) message(STATUS "Disabling the proxy library, because HWLOC is disabled") elseif(NOT UMF_BUILD_SHARED_LIBRARY) # TODO enable this scenario message( STATUS "Disabling the proxy library, because UMF is built as static library" ) elseif(UMF_PROXY_LIB_BASED_ON_POOL STREQUAL SCALABLE) if(UMF_POOL_SCALABLE_ENABLED) set(UMF_PROXY_LIB_ENABLED ON) set(PROXY_LIB_USES_SCALABLE_POOL ON) set(PROXY_LIBS umf) else() message( STATUS "Disabling the proxy library, because UMF_PROXY_LIB_BASED_ON_POOL==SCALABLE but TBB was not found" ) endif() elseif(UMF_PROXY_LIB_BASED_ON_POOL STREQUAL JEMALLOC) if(UMF_POOL_JEMALLOC_ENABLED) set(UMF_PROXY_LIB_ENABLED ON) set(PROXY_LIB_USES_JEMALLOC_POOL ON) set(PROXY_LIBS umf) else() message( STATUS "Disabling the proxy library, because UMF_PROXY_LIB_BASED_ON_POOL==JEMALLOC but the jemalloc pool is disabled" ) endif() else() message( FATAL_ERROR "Proxy library: pool manager not chosen or set to a non-supported one (see UMF_PROXY_LIB_BASED_ON_POOL)" ) endif() if((UMF_BUILD_GPU_TESTS OR UMF_BUILD_GPU_EXAMPLES) AND UMF_BUILD_CUDA_PROVIDER) find_package(CUDA REQUIRED cuda) if(CUDA_LIBRARIES) set(UMF_CUDA_ENABLED TRUE) else() message( STATUS "Disabling tests and examples that use the CUDA provider " "because the CUDA libraries they require were not found.") endif() # TODO do the same for ze_loader endif() add_subdirectory(src) if(UMF_BUILD_TESTS) add_subdirectory(test) endif() if(UMF_BUILD_BENCHMARKS) add_subdirectory(benchmark) endif() if(UMF_BUILD_EXAMPLES AND NOT UMF_DISABLE_HWLOC) add_subdirectory(examples) endif() if(UMF_FORMAT_CODE_STYLE) find_program(CLANG_FORMAT NAMES clang-format-15 clang-format-15.0 clang-format) find_program(CMAKE_FORMAT NAMES cmake-format) find_program(BLACK NAMES black) set(CLANG_FORMAT_REQUIRED "15.0") set(CMAKE_FORMAT_REQUIRED "0.6") if(NOT CLANG_FORMAT AND NOT CMAKE_FORMAT AND NOT BLACK) message( FATAL_ERROR "UMF_FORMAT_CODE_STYLE=ON, but neither clang-format (required version: " "${CLANG_FORMAT_REQUIRED}), nor cmake-format (required version: " "${CMAKE_FORMAT_REQUIRED}), nor black was found.") endif() if(CLANG_FORMAT) get_program_version_major_minor(${CLANG_FORMAT} CLANG_FORMAT_VERSION) message(STATUS "Found clang-format: ${CLANG_FORMAT} " "(version: ${CLANG_FORMAT_VERSION})") # Check if clang-format (in correct version) is available for code # formatting. if(NOT (CLANG_FORMAT_VERSION VERSION_EQUAL CLANG_FORMAT_REQUIRED)) message(FATAL_ERROR "Required clang-format version is " "${CLANG_FORMAT_REQUIRED}") endif() # Obtain files for clang-format check set(format_clang_glob) foreach( DIR IN ITEMS benchmark examples include src test) list( APPEND format_clang_glob "${DIR}/*.h" "${DIR}/*.hpp" "${DIR}/*.c" "${DIR}/*.cpp" "${DIR}/**/*.h" "${DIR}/**/*.hpp" "${DIR}/**/*.c" "${DIR}/**/*.cpp") endforeach() file(GLOB_RECURSE format_list ${format_clang_glob}) message( STATUS "Adding 'clang-format-check' and 'clang-format-apply' targets") add_custom_target( clang-format-check COMMAND ${CLANG_FORMAT} --style=file --dry-run -Werror ${format_list} COMMENT "Check files formatting using clang-format") add_custom_target( clang-format-apply COMMAND ${CLANG_FORMAT} --style=file -i ${format_list} COMMENT "Format files using clang-format") endif() if(CMAKE_FORMAT) get_program_version_major_minor(${CMAKE_FORMAT} CMAKE_FORMAT_VERSION) message(STATUS "Found cmake-format: ${CMAKE_FORMAT} " "(version: ${CMAKE_FORMAT_VERSION})") # Check if cmake-format (in correct version) is available for cmake # files formatting. if(NOT (CMAKE_FORMAT_VERSION VERSION_EQUAL CMAKE_FORMAT_REQUIRED)) message(FATAL_ERROR "Required cmake-format version is" "${CMAKE_FORMAT_REQUIRED}") endif() # Obtain files for cmake-format check set(format_cmake_glob) foreach( DIR IN ITEMS cmake benchmark examples include src test) list( APPEND format_cmake_glob "${DIR}/CMakeLists.txt" "${DIR}/*.cmake" "${DIR}/**/CMakeLists.txt" "${DIR}/**/*.cmake") endforeach() file(GLOB_RECURSE format_cmake_list ${format_cmake_glob}) list(APPEND format_cmake_list "${PROJECT_SOURCE_DIR}/CMakeLists.txt") message( STATUS "Adding 'cmake-format-check' and 'cmake-format-apply' targets") add_custom_target( cmake-format-check COMMAND ${CMAKE_FORMAT} --check ${format_cmake_list} COMMENT "Check CMake files formatting using cmake-format") add_custom_target( cmake-format-apply COMMAND ${CMAKE_FORMAT} --in-place ${format_cmake_list} COMMENT "Format CMake files using cmake-format") endif() if(BLACK) # black should maintain backward compatibility, we don't have to require # a specific version get_program_version_major_minor(${BLACK} BLACK_VERSION) message(STATUS "Found black: ${BLACK} (version: ${BLACK_VERSION})") message( STATUS "Adding 'black-format-check' and 'black-format-apply' targets") add_custom_target( black-format-check COMMAND ${BLACK} --check --verbose ${UMF_CMAKE_SOURCE_DIR} COMMENT "Check Python files formatting using black formatter") add_custom_target( black-format-apply COMMAND ${BLACK} ${UMF_CMAKE_SOURCE_DIR} COMMENT "Format Python files using black formatter") endif() # Add a convenience target for running all tools at once - available only if # all are found. if(CLANG_FORMAT AND CMAKE_FORMAT AND BLACK) add_custom_target( format-check COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target clang-format-check COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target cmake-format-check COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target black-format-check COMMENT "Running all formatting checks") add_custom_target( format-apply COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target clang-format-apply COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target cmake-format-apply COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target black-format-apply COMMENT "Format C/C++, CMake, and Python files") message( STATUS " Adding convenience targets 'format-check' and 'format-apply'." ) else() message( STATUS " Convenience targets 'format-check' and 'format-apply' are " "not available. Use commands specific for found tools (see the log above)." ) endif() endif() find_package(Python3 3.8) if(Python3_FOUND) message(STATUS "Adding 'docs' target for creating a documentation.") add_custom_target( docs WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND UMF_VERSION=${UMF_CMAKE_VERSION} ${Python3_EXECUTABLE} ${UMF_CMAKE_SOURCE_DIR}/docs/generate_docs.py COMMENT "Generate HTML documentation using Doxygen") endif() # --------------------------------------------------------------------------- # # Configure make install/uninstall and packages # --------------------------------------------------------------------------- # # Install the umfd library files as part of the umfd component if(WINDOWS AND UMF_USE_DEBUG_POSTFIX) if(UMF_BUILD_SHARED_LIBRARY) install( FILES ${UMFD_DLL} DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT umfd) endif() install( FILES ${UMFD_LIB} DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT umfd) endif() install(FILES ${PROJECT_SOURCE_DIR}/LICENSE.TXT DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/doc/${PROJECT_NAME}/") install( FILES ${PROJECT_SOURCE_DIR}/licensing/third-party-programs.txt DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/doc/${PROJECT_NAME}/licensing/") install(DIRECTORY examples DESTINATION "${CMAKE_INSTALL_DOCDIR}") # Add the include directory and the headers target to the install. install(DIRECTORY "${PROJECT_SOURCE_DIR}/include/" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) install(TARGETS umf_headers EXPORT ${PROJECT_NAME}-targets) # Add the list of installed targets to the install. This includes the namespace # which all installed targets will be prefixed with, e.g. for the headers target # users will depend on ${PROJECT_NAME}::headers. install( EXPORT ${PROJECT_NAME}-targets FILE ${PROJECT_NAME}-targets.cmake NAMESPACE ${PROJECT_NAME}:: DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}) # Configure the package versions file for use in find_package when installed. write_basic_package_version_file( ${PROJECT_BINARY_DIR}/cmake/${PROJECT_NAME}-config-version.cmake COMPATIBILITY SameMajorVersion) # Configure the package file that is searched for by find_package when # installed. configure_package_config_file( ${PROJECT_SOURCE_DIR}/cmake/${PROJECT_NAME}-config.cmake.in ${PROJECT_BINARY_DIR}/cmake/${PROJECT_NAME}-config.cmake INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}) # Add the package files to the install. install(FILES ${PROJECT_BINARY_DIR}/cmake/${PROJECT_NAME}-config.cmake ${PROJECT_BINARY_DIR}/cmake/${PROJECT_NAME}-config-version.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}) # Configure uninstall commands configure_file("${PROJECT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in" "${PROJECT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY) add_custom_target(uninstall COMMAND ${CMAKE_COMMAND} -P ${PROJECT_BINARY_DIR}/cmake_uninstall.cmake) umf/CODE_OF_CONDUCT.md0000644000000000000000000001247514544400200011334 0ustar # Contributor Covenant Code of Conduct ## Our Pledge We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socioeconomic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation. We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. ## Our Standards Examples of behavior that contributes to a positive environment for our community include: * Demonstrating empathy and kindness toward other people * Being respectful of differing opinions, viewpoints, and experiences * Giving and gracefully accepting constructive feedback * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience * Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: * The use of sexualized language or imagery, and sexual attention or advances of any kind * Trolling, insulting or derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or email address, without their explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. ## Scope This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at CommunityCodeOfConduct AT intel DOT com. All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the reporter of any incident. ## Enforcement Guidelines Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: ### 1. Correction **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. ### 2. Warning **Community Impact**: A violation through a single incident or series of actions. **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. ### 3. Temporary Ban **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within the community. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.1, available at [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. For answers to common questions about this code of conduct, see the FAQ at [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at [https://www.contributor-covenant.org/translations][translations]. [homepage]: https://www.contributor-covenant.org [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html [Mozilla CoC]: https://github.com/mozilla/diversity [FAQ]: https://www.contributor-covenant.org/faq umf/CONTRIBUTING.md0000644000000000000000000002461014544400200010760 0ustar # Contributing to UMF (Unified Memory Framework) - [Contributing to UMF (Unified Memory Framework)](#contributing-to-umf-unified-memory-framework) - [Opening new issues](#opening-new-issues) - [Submitting Pull Requests](#submitting-pull-requests) - [Building and testing](#building-and-testing) - [Code style](#code-style) - [When my PR is merged?](#when-my-pr-is-merged) - [Extending public API](#extending-public-api) - [License](#license) - [Adding new dependency](#adding-new-dependency) - [Code coverage](#code-coverage) - [Debugging](#debugging) - [Checking UMF version and build options](#checking-umf-version-and-build-options) - [Linux](#linux) - [Windows](#windows) Below you'll find instructions on how to contribute to UMF, either with code changes or issues. All contributions are most welcome! ## Opening new issues Please log bugs or suggestions as [GitHub issues](https://github.com/oneapi-src/unified-memory-framework/issues). When reporting a bug remember about the details, at least: - version of UMF (hash of a commit or a tag), - OS and kernel, - compiler. When opening a new issue you can pick a predefined type of issue. Please follow the template and fill in all the information. If your query doesn't match any of the proposed types, just pick a general issue with no template. ## Submitting Pull Requests We take all code contributions to UMF through GitHub Pull Requests. You must first create your own fork of the project and submit your changes to a branch. You can then raise a Pull Request targeting `oneapi-src/unified-memory-framework:main`. When opening a new Pull Request, you'll be provided with a simple template to follow with the basic requirements for your changes to fulfill. A good practices, when opening a PR, are: - one PR should fix/enhance one thing, split large PR into a few smaller PRs, - keep commits neat and in order, - squash commits to include only relevant ones (no "fixes after review" or similar), - if a commit fixes an open issue, add in the commit message line: `Fixes #`, - if a commit only mention an open issue, add in the commit message, e.g. `Ref. #`. ### Building and testing Building commands can be found in the top-level Readme file - section ["Build"](./README.md#build). Before committing you should test locally if all tests and checks pass. When project is built, enter the build directory and execute: ```bash $ ctest --output-on-failure ``` Any test's failure will produce error log. To enable additional checks (including `-Werror` / `/WX` compilation flag), switch on CMake flag `UMF_DEVELOPER_MODE`. To read more about all available CMake options please see ["CMake standard options"](./README.md#cmake-standard-options) section in the top-level Readme. ### Code style We use `clang-format` to verify and apply code style changes to C/C++ source files. To see all rules we require, please take a look at `.clang-format` file in the root directory of this repository. Similarly, we use `cmake-format` tool and `.cmake-format` file to verify and apply code style changes to CMake files. For Python source files we use `black` tool. To enable code style checks and re-formatting, CMake option `UMF_FORMAT_CODE_STYLE` has to be switched on. You'll then have additional CMake targets available. To verify correct coding style of your changes execute (assuming `build` is your build directory): ```bash $ cmake -B build -DUMF_FORMAT_CODE_STYLE=ON $ cmake --build build --target format-check ``` We run these checks in our Continuous Integration (CI). So, if any issues were found, the Pull Request will be blocked from merging. To apply proper formatting (meaning, to fix the issues) you can use the convenience target provided for applying formats: ```bash $ cmake --build build --target format-apply # Remember to review introduced changes ``` **NOTE**: The `format-check` and `format-apply` targets are only available if all of `clang-format`, `cmake-format` and `black` are installed. Otherwise you can use them separately with: - `clang-format-check` and `clang-format-apply` for C/C++ source files - `cmake-format-check` and `cmake-format-apply` for CMake files - `black-format-check` and `black-format-apply` for Python source files **NOTE**: We use specific versions of formatting tools to ensure consistency across the project. The required versions are: - clang-format version **15.0**, which can be installed with the command: `python -m pip install clang-format==15.0.7`. - cmake-format version **0.6**, which can be installed with the command: `python -m pip install cmake-format==0.6.13`. - black (no specific version required), which can be installed with the command: `python -m pip install black`. Please ensure you have these specific versions installed before contributing to the project. ### When my PR is merged? Your Pull Request (PR) will be merged if you meet several requirements - the basic are: - The project builds properly, - All tests are executed, and no issues have been reported, - All checks pass (code style, spelling, etc.), - Additional requirements are fulfilled (e.g., see below, for a new public API function). While the most of these requirements are verified via automated scripts run in Continuous Integration (CI) they are also verified with human touch - **the review**! Code review has to be done by at least two UMF maintainers. The "maintainers team" is added to each PR by default, but you can also pick specific people to review your code. It may speed up a little the review process. If any issues are found by reviewers they should be fixed by the owner of the PR. Now, when all GitHub Actions jobs are green, all review discussions are resolved, and you got two approvals from reviewers - you're good to go - your PR will be merged soon! ### Extending public API When adding a new public function, you have to make sure to update: - documentation, - map files with debug symbols (both .def and .map - for Windows and Linux), - appropriate examples (to show usage), - tests. ### License Unified Memory Framework is licensed under the terms in [LICENSE](./LICENSE.TXT) file. By contributing to the project, you agree to the license and copyright terms therein and release your contribution under these terms. **NOTE:** >Each new file added to the repository has to contain the appropriate license header. To see what >such a header looks like, you can see an existing file, at best, with the same file extension >(each type of file may have slightly different formatting and/or comment convention). With your contributions to this repository you also certify the following: ``` Developer Certificate of Origin Version 1.1 Copyright (C) 2004, 2006 The Linux Foundation and its contributors. 660 York Street, Suite 102, San Francisco, CA 94110 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Developer's Certificate of Origin 1.1 By making a contribution to this project, I certify that: (a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or (c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it. (d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved. ``` In case of any doubt, the maintainer may ask you to certify the above in writing, i.e. via email or by including a `Signed-off-by:` line at the bottom of your commit message. Please, use your real name (sorry, no pseudonyms or anonymous contributions.), e.g.: Signed-off-by: Joe Smith If you set your `user.name` and `user.email` git configs, you can sign your commit automatically with `git commit -s`. ### Adding new dependency Adding each new dependency (including new docker image or a package) should be done in a separate commit. The commit message should be: ``` New dependency: dependency_name license: SPDX license tag origin: https://dependency_origin.com ``` ## Code coverage After adding a new functionality add tests and check coverage before and after the change. To do this, enable coverage instrumentation by turning on the UMF_USE_COVERAGE flag in CMake. Coverage instrumentation feature is supported only by GCC and Clang. An example flow might look like the following: ```bash $ cmake -B build -DUMF_USE_COVERAGE=1 -DCMAKE_BUILD_TYPE=Debug $ cmake --build build -j $ cd build $ ctest $ apt install lcov $ lcov --capture --directory . --output-file coverage.info $ genhtml -o html_report coverage.info ``` ## Debugging ### Checking UMF version and build options From an already built UMF shared library you can obtain UMF precise version and CMake variables/options it was built with. It's not only useful to verify what should be included within the library, but also for debugging. If you're filing an issue to UMF project, please include this information in your ticket. #### Linux Make sure the `binutils` package is installed in your system. Then, you can use the following grep command: ```bash $ strings libumf.so | grep "@(#)" @(#) Intel(R) UMF version: 0.11.0-dev.git66.g89e3831d @(#) Intel(R) UMF CMake variables: "CMAKE_BUILD_TYPE:Debug,... ``` Please note, that version available in the name of library file (e.g. `libumf.so.0.11.0`) may be not accurate - version coded inside of the library is far more precise. #### Windows On Windows, DLL's metadata can be accessed e.g. looking into *Properties* of the dll file in the explorer. Look into the *Details* tab for "Product version" and "File description" (it contains UMF's build options). umf/ChangeLog0000644000000000000000000000662014544400200010302 0ustar Thu Apr 17 2025 Łukasz Stolarczuk * Version 0.11.0 This release contains: - make disjoint pool a C structure #898 - add fixed provider #976 - remove the Coarse provider #934 and replace with internal coarse library #931, #932 - implement umfPool[Set/Get]Tag #962 - L0 provider: implement support for defer and blocking free #963 - add set/restore context in CUDA provider free() #1049 - L0 provider: implement min/recommended page size query #1059 - add support for CUDA allocation flags #1079 - increase refcount to ze_loader/CUDA libraries #1086 - implement size limit for the cache of opened IPC handles #998 - allow creating fixed provider based on allocations from another pool #1143 - multiple benchmark improvements - new tests and CI workflows, incl. backward compatibility checks (#1087, #1163) Fri Jan 10 2025 Łukasz Stolarczuk * Version 0.10.1 This patch release contains: - Set symbol versions 0.10 in def/map files (#1013) - Fix: remove incorrect assert in utils_align_ptr_up_size_down() (#977) - Add strings with UMF version and useful CMake options (#992) - Extended error messages, when providers are disabled (#1012) Mon Dec 09 2024 Łukasz Stolarczuk * Version 0.10.0 In this release we introduced updates in several areas, listed below. We still don't yet guarantee a fully stable API, though. With new parameters' API we broke the compatibility, as we no longer support direct access to UMF params via (now internal) structures. Significant updates: - updated Level Zero Provider - new API to handle UMF parameters (replacing previous struct's) - extended IPC API testing - new Memtarget and Memspace API Minor updates: - multiple fixes in the source code - extended code coverage reporting - improved CI and testing - new examples - extended logging - yet more fixes in the building system Tue Nov 12 2024 Łukasz Stolarczuk * Version 0.9.1 This patch release contains only 3 small fixes in build system of UMF. Thu Sep 12 2024 Łukasz Stolarczuk * Version 0.9.0 This release aims to be the first complete release of the UMF project. We don't yet guarantee a fully stable API, though. This release contains all the features listed in the version 0.1.0 plus, additionally: - IPC API - fixes in the building system - proper versioning - minor patches in the source code - improved CI and docs Thu Jul 04 2024 Łukasz Stolarczuk * Version 0.1.0 This is the first release of Unified Memory Framework (UMF) project. UMF is a library for constructing allocators and memory pools. It also contains broadly useful abstractions and utilities for memory management. UMF allows users to create and manage multiple memory pools characterized by different attributes, allowing certain allocation types to be isolated from others and allocated using different hardware resources as required. Documentation of UMF, including API description, architecture, and examples can be found: https://oneapi-src.github.io/unified-memory-framework Features: - Level Zero memory provider - OS memory provider - Jemalloc pool (based on jemalloc) - Scalable pool (based on oneTBB) - Disjoint pool - Proxy pool - Single-threaded micro benchmark based on ubench and custom multi-threaded benchmark - Examples umf/LICENSE.TXT0000644000000000000000000003160314544400200010212 0ustar ============================================================================== The Unified Memory Framework is under the Apache License v2.0 with LLVM Exceptions: ============================================================================== Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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. ---- LLVM Exceptions to the Apache 2.0 License ---- As an exception, if, as a result of your compiling your source code, portions of this Software are embedded into an Object form of such source code, you may redistribute such embedded portions in such Object form without complying with the conditions of Sections 4(a), 4(b) and 4(d) of the License. In addition, if you combine or link compiled forms of this Software with software that is licensed under the GPLv2 ("Combined Software") and if a court of competent jurisdiction determines that the patent provision (Section 3), the indemnity provision (Section 9) or other Section of the License conflicts with the conditions of the GPLv2, you may retroactively and prospectively choose to deem waived or otherwise exclude such Section(s) of the License, but only in their entirety and only with respect to the Combined Software. ============================================================================== Software from third parties included in the Unified Memory Framework: ============================================================================== The Unified Memory Framework contains third party software which is under different license terms. All such code will be identified clearly using at least one of two mechanisms: 1) It will be in a separate directory tree with its own `LICENSE.txt` or `LICENSE` file at the top containing the specific license and restrictions which apply to that software, or 2) It will contain specific license and restriction terms at the top of every file. umf/README.md0000644000000000000000000004406014544400200010007 0ustar # Unified Memory Framework [![PR/push](https://github.com/oneapi-src/unified-memory-framework/actions/workflows/pr_push.yml/badge.svg?branch=main&event=push)](https://github.com/oneapi-src/unified-memory-framework/actions/workflows/pr_push.yml) [![Coverage](https://gist.githubusercontent.com/bb-ur/3f66c77d7035df39aa75dda8a2ac75b3/raw/umf_coverage_badge.svg)](https://github.com/oneapi-src/unified-memory-framework/actions/workflows/pr_push.yml?query=branch%3Amain) [![GitHubPages](https://github.com/oneapi-src/unified-memory-framework/actions/workflows/docs.yml/badge.svg?branch=main)](https://github.com/oneapi-src/unified-memory-framework/actions/workflows/docs.yml) [![Nightly](https://github.com/oneapi-src/unified-memory-framework/actions/workflows/nightly.yml/badge.svg?branch=main)](https://github.com/oneapi-src/unified-memory-framework/actions/workflows/nightly.yml) [![Coverity build](https://github.com/oneapi-src/unified-memory-framework/actions/workflows/coverity.yml/badge.svg?branch=main)](https://github.com/oneapi-src/unified-memory-framework/actions/workflows/coverity.yml) [![Coverity report](https://scan.coverity.com/projects/29761/badge.svg?flat=0)](https://scan.coverity.com/projects/oneapi-src-unified-memory-framework) [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/oneapi-src/unified-memory-framework/badge)](https://securityscorecards.dev/viewer/?uri=github.com/oneapi-src/unified-memory-framework) ## Introduction The Unified Memory Framework (UMF) is a library for constructing allocators and memory pools. It also contains broadly useful abstractions and utilities for memory management. UMF allows users to manage multiple memory pools characterized by different attributes, allowing certain allocation types to be isolated from others and allocated using different hardware resources as required. ## Usage For a quick introduction to UMF usage, please see [examples](https://oneapi-src.github.io/unified-memory-framework/examples.html) documentation, which includes the code of the [basic example](https://github.com/oneapi-src/unified-memory-framework/blob/main/examples/basic/basic.c). The are also more advanced that allocates USM memory from the [Level Zero device](https://github.com/oneapi-src/unified-memory-framework/blob/main/examples/level_zero_shared_memory/level_zero_shared_memory.c) using the Level Zero API and UMF Level Zero memory provider and [CUDA device](https://github.com/oneapi-src/unified-memory-framework/blob/main/examples/cuda_shared_memory/cuda_shared_memory.c) using the CUDA API and UMF CUDA memory provider. ## Build ### Requirements Required packages: - libhwloc-dev >= 2.3.0 (Linux) / hwloc >= 2.3.0 (Windows) - C compiler - [CMake](https://cmake.org/) >= 3.14.0 For development and contributions: - clang-format-15.0 (can be installed with `python -m pip install clang-format==15.0.7`) - cmake-format-0.6 (can be installed with `python -m pip install cmake-format==0.6.13`) - black (can be installed with `python -m pip install black==24.3.0`) For building tests and multithreaded benchmarks: - C++ compiler with C++17 support For Level Zero memory provider tests: - Level Zero headers and libraries - compatible GPU with installed driver ### Linux Executable and binaries will be in **build/bin**. The `{build_config}` can be either `Debug` or `Release`. ```bash cmake -B build -DCMAKE_BUILD_TYPE={build_config} cmake --build build -j $(nproc) ``` ### Windows Generating Visual Studio Project. EXE and binaries will be in **build/bin/{build_config}**. The `{build_config}` can be either `Debug` or `Release`. ```bash cmake -B build -G "Visual Studio 15 2017 Win64" cmake --build build --config {build_config} -j $Env:NUMBER_OF_PROCESSORS ``` ### Benchmark UMF comes with a single-threaded micro benchmark based on [ubench](https://github.com/sheredom/ubench.h). In order to build the benchmark, the `UMF_BUILD_BENCHMARKS` CMake configuration flag has to be turned `ON`. UMF also provides multithreaded benchmarks that can be enabled by setting both `UMF_BUILD_BENCHMARKS` and `UMF_BUILD_BENCHMARKS_MT` CMake configuration flags to `ON`. Multithreaded benchmarks require a C++ support. The Scalable Pool requirements can be found in the relevant 'Memory Pool managers' section below. ### Sanitizers List of sanitizers available on Linux: - AddressSanitizer - UndefinedBehaviorSanitizer - ThreadSanitizer - Is mutually exclusive with other sanitizers. - MemorySanitizer - Requires linking against MSan-instrumented libraries to prevent false positive reports. More information [here](https://github.com/google/sanitizers/wiki/MemorySanitizerLibcxxHowTo). List of sanitizers available on Windows: - AddressSanitizer Listed sanitizers can be enabled with appropriate [CMake options](#cmake-standard-options). ### CMake standard options List of options provided by CMake: | Name | Description | Values | Default | | - | - | - | - | | UMF_BUILD_SHARED_LIBRARY | Build UMF as shared library | ON/OFF | OFF | | UMF_BUILD_LEVEL_ZERO_PROVIDER | Build Level Zero memory provider | ON/OFF | ON | | UMF_BUILD_CUDA_PROVIDER | Build CUDA memory provider | ON/OFF | ON | | UMF_BUILD_LIBUMF_POOL_JEMALLOC | Build the libumf_pool_jemalloc static library | ON/OFF | OFF | | UMF_BUILD_TESTS | Build UMF tests | ON/OFF | ON | | UMF_BUILD_GPU_TESTS | Build UMF GPU tests | ON/OFF | OFF | | UMF_BUILD_BENCHMARKS | Build UMF benchmarks | ON/OFF | OFF | | UMF_BUILD_EXAMPLES | Build UMF examples | ON/OFF | ON | | UMF_BUILD_FUZZTESTS | Build UMF fuzz tests | ON/OFF | OFF | | UMF_BUILD_GPU_EXAMPLES | Build UMF GPU examples | ON/OFF | OFF | | UMF_DEVELOPER_MODE | Enable additional developer checks | ON/OFF | OFF | | UMF_FORMAT_CODE_STYLE | Add clang, cmake, and black -format-check and -format-apply targets to make | ON/OFF | OFF | | UMF_TESTS_FAIL_ON_SKIP | Treat skips in tests as fail | ON/OFF | OFF | | UMF_USE_ASAN | Enable AddressSanitizer checks | ON/OFF | OFF | | UMF_USE_UBSAN | Enable UndefinedBehaviorSanitizer checks | ON/OFF | OFF | | UMF_USE_TSAN | Enable ThreadSanitizer checks | ON/OFF | OFF | | UMF_USE_MSAN | Enable MemorySanitizer checks | ON/OFF | OFF | | UMF_USE_VALGRIND | Enable Valgrind instrumentation | ON/OFF | OFF | | UMF_USE_COVERAGE | Build with coverage enabled (Linux only) | ON/OFF | OFF | | UMF_LINK_HWLOC_STATICALLY | Link UMF with HWLOC library statically (proxy library will be disabled on Windows+Debug build) | ON/OFF | OFF | | UMF_DISABLE_HWLOC | Disable features that requires hwloc (OS provider, memory targets, topology discovery) | ON/OFF | OFF | ## Architecture: memory pools and providers A UMF memory pool is a combination of a pool allocator and a memory provider. A memory provider is responsible for coarse-grained memory allocations and management of memory pages, while the pool allocator controls memory pooling and handles fine-grained memory allocations. Pool allocator can leverage existing allocators (e.g. jemalloc or tbbmalloc) or be written from scratch. UMF comes with predefined pool allocators (see [`include/umf/pools`](include/umf/pools)) and providers (see [`include/umf/providers`](include/umf/providers)). UMF can also work with user-defined pools and providers that implement a specific interface (see [`include/umf/memory_pool_ops.h`](include/umf/memory_pool_ops.h) and [`include/umf/memory_provider_ops.h`](include/umf/memory_provider_ops.h)). More detailed documentation is available here: ### Memory providers #### Fixed memory provider A memory provider that can provide memory from a given pre-allocated buffer. #### OS memory provider A memory provider that provides memory from an operating system. OS memory provider supports two types of memory mappings (set by the `visibility` parameter): 1) private memory mapping (`UMF_MEM_MAP_PRIVATE`) 2) shared memory mapping (`UMF_MEM_MAP_SHARED` - supported on Linux only yet) IPC API requires the `UMF_MEM_MAP_SHARED` memory `visibility` mode (`UMF_RESULT_ERROR_INVALID_ARGUMENT` is returned otherwise). IPC API uses file descriptor duplication, which requires the `pidfd_getfd(2)` system call to obtain a duplicate of another process's file descriptor. This system call is supported since Linux 5.6. Required permission ("restricted ptrace") is governed by the `PTRACE_MODE_ATTACH_REALCREDS` check (see `ptrace(2)`). To allow file descriptor duplication in a binary that opens IPC handle, you can call `prctl(PR_SET_PTRACER, ...)` in the producer binary that gets the IPC handle. Alternatively you can change the `ptrace_scope` globally in the system, e.g.: ```sh sudo bash -c "echo 0 > /proc/sys/kernel/yama/ptrace_scope" ``` There are available two mechanisms for the shared memory mapping: 1) a named shared memory object (used if the `shm_name` parameter is not NULL) or 2) an anonymous file descriptor (used if the `shm_name` parameter is NULL) The `shm_name` parameter should be a null-terminated string of up to NAME_MAX (i.e., 255) characters none of which are slashes. An anonymous file descriptor for the shared memory mapping will be created using: 1) `memfd_secret()` syscall - (if it is implemented and) if the `UMF_MEM_FD_FUNC` environment variable does not contain the "memfd_create" string or 2) `memfd_create()` syscall - otherwise (and if it is implemented). ##### Requirements IPC API on Linux requires the `PTRACE_MODE_ATTACH_REALCREDS` permission (see `ptrace(2)`) to duplicate another process's file descriptor (see above). Packages required for tests (Linux-only yet): - libnuma-dev #### Level Zero memory provider A memory provider that provides memory from L0 device. IPC API uses file descriptor duplication, which requires the `pidfd_getfd(2)` system call to obtain a duplicate of another process's file descriptor. This system call is supported since Linux 5.6. Required permission ("restricted ptrace") is governed by the `PTRACE_MODE_ATTACH_REALCREDS` check (see `ptrace(2)`). To allow file descriptor duplication in a binary that opens IPC handle, you can call `prctl(PR_SET_PTRACER, ...)` in the producer binary that gets the IPC handle. Alternatively you can change the `ptrace_scope` globally in the system, e.g.: ```sh sudo bash -c "echo 0 > /proc/sys/kernel/yama/ptrace_scope" ``` ##### Requirements 1) Linux or Windows OS 2) The `UMF_BUILD_LEVEL_ZERO_PROVIDER` option turned `ON` (by default) 3) IPC API on Linux requires the `PTRACE_MODE_ATTACH_REALCREDS` permission (see `ptrace(2)`) to duplicate another process's file descriptor (see above). Additionally, required for tests: 4) The `UMF_BUILD_GPU_TESTS` option turned `ON` 5) System with Level Zero compatible GPU 6) Required packages: - liblevel-zero-dev (Linux) or level-zero-sdk (Windows) #### DevDax memory provider (Linux only) A memory provider that provides memory from a device DAX (a character device file like `/dev/daxX.Y`). It can be used when large memory mappings are needed. ##### Requirements 1) Linux OS 2) A character device file /dev/daxX.Y created in the OS. #### File memory provider (Linux only yet) A memory provider that provides memory by mapping a regular, extendable file. IPC API requires the `UMF_MEM_MAP_SHARED` memory `visibility` mode (`UMF_RESULT_ERROR_INVALID_ARGUMENT` is returned otherwise). The memory visibility mode parameter must be set to `UMF_MEM_MAP_SHARED` in case of FSDAX. ##### Requirements 1) Linux OS 2) A length of a path of a file to be mapped can be `PATH_MAX` (4096) characters at most. #### CUDA memory provider A memory provider that provides memory from CUDA device. ##### Requirements 1) Linux or Windows OS 2) The `UMF_BUILD_CUDA_PROVIDER` option turned `ON` (by default) Additionally, required for tests: 3) The `UMF_BUILD_GPU_TESTS` option turned `ON` 4) System with CUDA compatible GPU 5) Required packages: - nvidia-cuda-dev (Linux) or cuda-sdk (Windows) ### Memory pool managers #### Proxy pool (part of libumf) This memory pool is distributed as part of libumf. It forwards all requests to the underlying memory provider. Currently umfPoolRealloc, umfPoolCalloc and umfPoolMallocUsableSize functions are not supported by the proxy pool. #### Disjoint pool (part of libumf) The Disjoint pool is designed to keep internal metadata separate from user data. This separation is particularly useful when user data needs to be placed in memory with relatively high latency, such as GPU memory or disk storage. #### Jemalloc pool Jemalloc pool is a [jemalloc](https://github.com/jemalloc/jemalloc)-based memory pool manager built as a separate static library: libjemalloc_pool.a on Linux and jemalloc_pool.lib on Windows. The `UMF_BUILD_LIBUMF_POOL_JEMALLOC` option has to be turned `ON` to build this library. [jemalloc](https://github.com/jemalloc/jemalloc) is required to build the jemalloc pool. In case of Linux OS jemalloc is built from the (fetched) sources with the following non-default options enabled: - `--with-jemalloc-prefix=je_` - adds the `je_` prefix to all public APIs, - `--disable-cxx` - disables C++ integration, it will cause the `new` and the `delete` operators implementations to be omitted. - `--disable-initial-exec-tls` - disables the initial-exec TLS model for jemalloc's internal thread-local storage (on those platforms that support explicit settings), it can allow jemalloc to be dynamically loaded after program startup (e.g. using `dlopen()`). The default jemalloc package is required on Windows. ##### Requirements 1) The `UMF_BUILD_LIBUMF_POOL_JEMALLOC` option turned `ON` 2) jemalloc is required: - on Linux and MacOS: jemalloc is fetched and built from sources (a custom build), - on Windows: the default jemalloc package is required #### Scalable Pool (part of libumf) Scalable Pool is a [oneTBB](https://github.com/oneapi-src/oneTBB)-based memory pool manager. It is distributed as part of libumf. To use this pool, TBB must be installed in the system. ##### Requirements Packages required for using this pool and executing tests/benchmarks (not required for build): - libtbb-dev (libtbbmalloc.so.2) on Linux or tbb (tbbmalloc.dll) on Windows ### Memspaces (Linux-only) TODO: Add general information about memspaces. #### Host all memspace Memspace backed by all available NUMA nodes discovered on the platform. Can be retrieved using umfMemspaceHostAllGet. #### Highest capacity memspace Memspace backed by all available NUMA nodes discovered on the platform sorted by capacity. Can be retrieved using umfMemspaceHighestCapacityGet. #### Highest bandwidth memspace Memspace backed by an aggregated list of NUMA nodes identified as highest bandwidth after selecting each available NUMA node as the initiator. Querying the bandwidth value requires HMAT support on the platform. Calling `umfMemspaceHighestBandwidthGet()` will return NULL if it's not supported. #### Lowest latency memspace Memspace backed by an aggregated list of NUMA nodes identified as lowest latency after selecting each available NUMA node as the initiator. Querying the latency value requires HMAT support on the platform. Calling `umfMemspaceLowestLatencyGet()` will return NULL if it's not supported. ### Proxy library UMF provides the UMF proxy library (`umf_proxy`) that makes it possible to override the default allocator in other programs in both Linux and Windows. To enable this feature, the `UMF_BUILD_SHARED_LIBRARY` option needs to be turned `ON`. #### Linux In case of Linux it can be done without any code changes using the `LD_PRELOAD` environment variable: ```sh LD_PRELOAD=/usr/lib/libumf_proxy.so myprogram ``` The memory used by the proxy memory allocator is mmap'ed: 1) with the `MAP_PRIVATE` flag by default or 2) with the `MAP_SHARED` flag if the `UMF_PROXY` environment variable contains one of two following strings: `page.disposition=shared-shm` or `page.disposition=shared-fd`. These two options differ in a mechanism used during IPC: - `page.disposition=shared-shm` - IPC uses the named shared memory. An SHM name is generated using the `umf_proxy_lib_shm_pid_$PID` pattern, where `$PID` is the PID of the process. It creates the `/dev/shm/umf_proxy_lib_shm_pid_$PID` file. - `page.disposition=shared-fd` - IPC API uses file descriptor duplication, which requires the `pidfd_getfd(2)` system call to obtain a duplicate of another process's file descriptor. This system call is supported since Linux 5.6. Required permission ("restricted ptrace") is governed by the `PTRACE_MODE_ATTACH_REALCREDS` check (see `ptrace(2)`). To allow file descriptor duplication in a binary that opens IPC handle, you can call `prctl(PR_SET_PTRACER, ...)` in the producer binary that gets the IPC handle. Alternatively you can change the `ptrace_scope` globally in the system, e.g.: `sudo bash -c "echo 0 > /proc/sys/kernel/yama/ptrace_scope"`. **Size threshold** The **size threshold** feature (Linux only) causes that all allocations of size less than the given threshold value go to the default system allocator instead of the proxy library. It can be enabled by adding the `size.threshold=` string to the `UMF_PROXY` environment variable (with `';'` as a separator), for example: `UMF_PROXY="page.disposition=shared-shm;size.threshold=64"`. **Remark:** changing a size of allocation (using `realloc()` ) does not change the allocator (`realloc(malloc(threshold - 1), threshold + 1)` still belongs to the default system allocator and `realloc(malloc(threshold + 1), threshold - 1)` still belongs to the proxy library pool allocator). #### Windows In case of Windows it requires: 1) explicitly linking your program dynamically with the `umf_proxy.dll` library 2) (C++ code only) including `proxy_lib_new_delete.h` in a single(!) source file in your project to override also the `new`/`delete` operations. ## Contributions All contributions to the UMF project are most welcome! Before submitting an issue or a Pull Request, please read [Contribution Guide](./CONTRIBUTING.md). ## Logging To enable logging in UMF source files please follow the guide in the [web documentation](https://oneapi-src.github.io/unified-memory-framework/introduction.html#logging). ## Notices The contents of this repository may have been developed with support from one or more Intel-operated generative artificial intelligence solutions. umf/RELEASE_STEPS.md0000644000000000000000000001112514544400200011104 0ustar ## UMF Release Steps This document contains all the steps required to make a new release of UMF. As a helper, we use in this guide these 2 variables: ```bash set $VERSION = new full version (e.g., 0.1.0-rc1) # -rc1 included just as an example set $VER = new major+minor only version (e.g., 0.1) ``` **Note:** > Before doing the final release, it's recommended to prepare a pre-release version - a "release candidate" > (or "rc" in short). This requires adding, e.g., `-rc1` to the VERSION string. When all tests and checks > end properly, you can follow up with the final release. If any fix is required, it should be included in > another rc version (e.g., `-rc2`). At the moment, UMF releases are aligned with oneAPI releases - at least one UMF version will be released for a oneAPI release. Once all changes planned for UMF release are accepted, we follow the process (described in more detail below): 1. Checkout the appropriate branch (`main` or "stable" `v$VER.x`). 2. Make changes for the release. 3. Create a new tag based on the latest commit - it takes the form `v..` (e.g., `v0.1.0`). 4. Push the tag and branch to the upstream. 5. Create a new GitHub release using the tag created in the previous step. 6. Update downstream projects to utilize the release tag. If any issues arise from integration, apply any necessary hot fixes to `v$VER.x` branch and go back to step 2 - to create a patch release. This step can also be tested using `rc` version, potentially followed by another `rc` tag. ## Make a release locally Do changes for a release: - Start of appropriate branch: - For patch release, do it from a stable branch: - `git checkout v$VER.x` (e.g., checkout `v0.1.x` if this is a `v0.1.1` patch) - If previously we decided not to create such branch, create it now, based on the appropriate minor or major tag - For major/minor release start from the `main` branch - Add an entry to ChangeLog, remember to change the day of the week in the release date - For major and minor (prior 1.0.0) releases mention API and ABI compatibility with the previous release - For major and minor releases, update `UMF_VERSION_CURRENT` in `include/umf/base.h` (the API version) - For changes in ops structures, update corresponding UMF_*_OPS_VERSION_CURRENT - For major and minor (prior 1.0.0) releases update ABI version in `.map` and `.def` files - These files are defined for all public libraries (`libumf` and `proxy_lib`, at the moment) - Commit these changes and tag the release: - `git commit -a -S -m "$VERSION release"` - `git tag -a -s -m "Version $VERSION" v$VERSION` - For major/minor release: - If stable branch for this release is required, create it: - `git checkout -b v$VER.x` - For some early versions (like `0.1.0`) we may omit creation of the branch - For major/minor release, when release is done, add an extra "dev" tag on the `main` branch: - `git tag -a -s -m "Development version $VERSION+1" v$VERSION+1-dev` - for example, when `v0.1.0` is released, the dev tag would be `v0.2.0-dev` - if needed, further in time, an extra dev tag can be introduced, e.g. `v0.2.0-dev1` - This way, the `main` branch will introduce itself as the next version ## Publish changes As patch releases should be done on the stable branches, pushing tags and branches differ a little. For patch release: - `git push upstream HEAD:v$VER.x v$VERSION` - push branch and tag For major/minor release: - Push main: - `git push upstream HEAD:main v$VERSION` - And, if stable branch was also created, push it as well: - `git checkout v$VER.x` - `git push upstream HEAD:v$VER.x` ## Announce release To make the release official: - Go to [GitHub's releases tab](https://github.com/oneapi-src/unified-memory-framework/releases/new): - Tag version: `v$VERSION`, release title: UMF $VERSION, description: copy entry from ChangeLog and format it with no tabs and no characters limit in line - Prior to version 1.0.0, check the *Set as a pre-release* tick box. - Announce the release, where needed ## More information To assure the community that the release is a valid package from UMF maintainers, it's recommended to sign the release commit and the tag (`-S`/`-s` parameters in commands above). If you require to generate a GPG key follow [these steps](https://docs.github.com/en/authentication/managing-commit-signature-verification/generating-a-new-gpg-key). After that you'd also have to add this new key to your GitHub account - please do the steps in [this guide](https://docs.github.com/en/authentication/managing-commit-signature-verification/telling-git-about-your-signing-key). umf/benchmark/0000755000000000000000000000000014544400200010456 5ustar umf/benchmark/CMakeLists.txt0000644000000000000000000001215214544400200013217 0ustar # Copyright (C) 2023-2025 Intel Corporation # Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception include(FetchContent) FetchContent_Declare( googlebenchmark GIT_REPOSITORY https://github.com/google/benchmark.git GIT_TAG v1.9.0) set(BENCHMARK_ENABLE_GTEST_TESTS OFF CACHE BOOL "" FORCE) set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "" FORCE) set(BENCHMARK_ENABLE_INSTALL OFF CACHE BOOL "" FORCE) FetchContent_MakeAvailable(googlebenchmark) # In MSVC builds, there is no way to determine the actual build type during the # CMake configuration step. Therefore, this message is printed in all MSVC # builds. if(WINDOWS OR NOT CMAKE_BUILD_TYPE STREQUAL "Release") message( STATUS "The benchmarks SHOULD NOT be run in the Debug build type! The benchmarks will be built, however their output is relevant only in the Release build!" ) endif() if(UMF_BUILD_BENCHMARKS_MT) include(FindThreads) endif() function(add_umf_benchmark) # Parameters: # # * NAME - a name of the benchmark # * SRCS - source files # * LIBS - libraries to be linked with # * LIBDIRS - directories of libraries to be linked with # * TESTARGS - additional arguments to be passed to the add_test set(oneValueArgs NAME) set(multiValueArgs SRCS LIBS LIBDIRS TESTARGS) cmake_parse_arguments( ARG "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) set(BENCH_NAME umf-${ARG_NAME}) set(BENCH_LIBS ${ARG_LIBS} umf umf_utils) add_umf_executable( NAME ${BENCH_NAME} SRCS ${ARG_SRCS} LIBS ${BENCH_LIBS}) target_include_directories( ${BENCH_NAME} PRIVATE ${UMF_CMAKE_SOURCE_DIR}/include ${UMF_CMAKE_SOURCE_DIR}/src/utils) target_link_directories(${BENCH_NAME} PRIVATE ${ARG_LIBDIRS}) add_test( NAME ${BENCH_NAME} COMMAND ${BENCH_NAME} ${ARG_TESTARGS} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) if("${BENCH_NAME}" STREQUAL "umf-ubench") # Benchmark passes if it prints "PASSED" in the output, because ubench # of scalable pool fails if the confidence interval exceeds maximum # permitted 2.5%. set_tests_properties( ${BENCH_NAME} PROPERTIES LABELS "benchmark" PASS_REGULAR_EXPRESSION "PASSED") else() set_tests_properties(${BENCH_NAME} PROPERTIES LABELS "benchmark") endif() if(WINDOWS) # append PATH to DLLs set_property(TEST ${BENCH_NAME} PROPERTY ENVIRONMENT_MODIFICATION "${DLL_PATH_LIST}") endif() if(LINUX) # prepend LD_LIBRARY_PATH with ${CMAKE_BINARY_DIR}/lib it is required # because ${CMAKE_BINARY_DIR}/lib contains libze_loader.so and tests # should use it instead of system one. set_property( TEST ${BENCH_NAME} PROPERTY ENVIRONMENT_MODIFICATION "LD_LIBRARY_PATH=path_list_prepend:${CMAKE_BINARY_DIR}/lib" ) endif() if(UMF_POOL_JEMALLOC_ENABLED) target_compile_definitions(${BENCH_NAME} PRIVATE UMF_POOL_JEMALLOC_ENABLED=1) endif() if(UMF_POOL_SCALABLE_ENABLED) target_compile_definitions(${BENCH_NAME} PRIVATE UMF_POOL_SCALABLE_ENABLED=1) endif() if(UMF_BUILD_LEVEL_ZERO_PROVIDER) target_compile_definitions(${BENCH_NAME} PRIVATE UMF_PROVIDER_LEVEL_ZERO_ENABLED=1) target_include_directories( ${BENCH_NAME} PRIVATE ${UMF_CMAKE_SOURCE_DIR}/test/common ${LEVEL_ZERO_INCLUDE_DIRS}) endif() if(UMF_BUILD_CUDA_PROVIDER) target_compile_definitions(${BENCH_NAME} PRIVATE UMF_BUILD_CUDA_PROVIDER=1) target_include_directories( ${BENCH_NAME} PRIVATE ${UMF_CMAKE_SOURCE_DIR}/test/common ${CUDA_INCLUDE_DIRS}) endif() if(UMF_BUILD_GPU_TESTS) target_compile_definitions(${BENCH_NAME} PRIVATE UMF_BUILD_GPU_TESTS=1) endif() endfunction() set(LIB_DIRS ${LIBHWLOC_LIBRARY_DIRS}) # optional libraries if(LINUX) set(LIBS_OPTIONAL ${LIBS_OPTIONAL} m) endif() if(UMF_BUILD_GPU_TESTS AND UMF_BUILD_LEVEL_ZERO_PROVIDER) set(SRCS_OPTIONAL ${SRCS_OPTIONAL} ../src/utils/utils_level_zero.cpp) set(LIBS_OPTIONAL ${LIBS_OPTIONAL} ze_loader) # TODO add CUDA endif() # BENCHMARKS add_umf_benchmark( NAME ubench SRCS ubench.c ${SRCS_OPTIONAL} LIBS ${LIBS_OPTIONAL} LIBDIRS ${LIB_DIRS}) add_umf_benchmark( NAME benchmark SRCS benchmark.cpp LIBS ${LIBS_OPTIONAL} benchmark::benchmark # limit running benchmarks in CI tests to single-threaded LIBDIRS ${LIB_DIRS} TESTARGS --benchmark_filter=threads:1$) if(UMF_BUILD_BENCHMARKS_MT) add_umf_benchmark( NAME multithreaded SRCS multithread.cpp LIBS ${LIBS_OPTIONAL} ${CMAKE_THREAD_LIBS_INIT} LIBDIRS ${LIB_DIRS}) endif() umf/benchmark/benchmark.cpp0000644000000000000000000002233014544400200013114 0ustar /* * Copyright (C) 2024-2025 Intel Corporation * * Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception * */ #include #include "benchmark.hpp" #define UMF_BENCHMARK_TEMPLATE_DEFINE(BaseClass, Method, ...) \ BENCHMARK_TEMPLATE_DEFINE_F(BaseClass, Method, __VA_ARGS__) \ (benchmark::State & state) { \ for (auto _ : state) { \ bench(state); \ } \ } #define UMF_BENCHMARK_REGISTER_F(BaseClass, Method) \ BENCHMARK_REGISTER_F(BaseClass, Method) \ ->Apply( \ &BENCHMARK_PRIVATE_CONCAT_NAME(BaseClass, Method)::defaultArgs) // Benchmarks scenarios: // The benchmark arguments specified in Args() are, in order: // benchmark arguments, allocator arguments, size generator arguments. // The exact meaning of each argument depends on the benchmark, allocator, and size components used. // Refer to the 'argsName()' function in each component to find detailed descriptions of these arguments. static void multithreaded(benchmark::internal::Benchmark *benchmark) { benchmark->Threads(12); benchmark->Threads(8); benchmark->Threads(4); benchmark->Threads(1); } static void singlethreaded(benchmark::internal::Benchmark *benchmark) { benchmark->Threads(1); } static void default_multiple_alloc_fix_size(benchmark::internal::Benchmark *benchmark) { benchmark->Args({10000, 1, 4096}); benchmark->Iterations(500000); } static void default_multiple_alloc_uniform_size(benchmark::internal::Benchmark *benchmark) { benchmark->Args({10000, 1, 8, 4096, 8}); benchmark->Args({10000, 1, 8, 128, 8}); benchmark->Iterations(500000); } UMF_BENCHMARK_TEMPLATE_DEFINE(multiple_malloc_free_benchmark, glibc_fix, fixed_alloc_size, glibc_malloc); UMF_BENCHMARK_REGISTER_F(multiple_malloc_free_benchmark, glibc_fix) ->Apply(&default_multiple_alloc_fix_size) ->Apply(&multithreaded); UMF_BENCHMARK_TEMPLATE_DEFINE(multiple_malloc_free_benchmark, glibc_uniform, uniform_alloc_size, glibc_malloc); UMF_BENCHMARK_REGISTER_F(multiple_malloc_free_benchmark, glibc_uniform) ->Apply(&default_multiple_alloc_uniform_size) ->Apply(&multithreaded); UMF_BENCHMARK_TEMPLATE_DEFINE(multiple_malloc_free_benchmark, proxy_pool, fixed_alloc_size, pool_allocator>); UMF_BENCHMARK_REGISTER_F(multiple_malloc_free_benchmark, proxy_pool) ->Apply(&default_multiple_alloc_fix_size) // reduce iterations, as this benchmark is slower than others ->Iterations(50000) ->Apply(&singlethreaded); UMF_BENCHMARK_TEMPLATE_DEFINE(multiple_malloc_free_benchmark, os_provider, fixed_alloc_size, provider_allocator); UMF_BENCHMARK_REGISTER_F(multiple_malloc_free_benchmark, os_provider) ->Apply(&default_multiple_alloc_fix_size) // reduce iterations, as this benchmark is slower than others ->Iterations(50000) ->Apply(&singlethreaded); UMF_BENCHMARK_TEMPLATE_DEFINE(multiple_malloc_free_benchmark, disjoint_pool_fix, fixed_alloc_size, pool_allocator>); UMF_BENCHMARK_REGISTER_F(multiple_malloc_free_benchmark, disjoint_pool_fix) ->Apply(&default_multiple_alloc_fix_size) ->Apply(&multithreaded); UMF_BENCHMARK_TEMPLATE_DEFINE(multiple_malloc_free_benchmark, disjoint_pool_uniform, uniform_alloc_size, pool_allocator>); UMF_BENCHMARK_REGISTER_F(multiple_malloc_free_benchmark, disjoint_pool_uniform) ->Apply(&default_multiple_alloc_uniform_size) ->Apply(&multithreaded); #ifdef UMF_POOL_JEMALLOC_ENABLED UMF_BENCHMARK_TEMPLATE_DEFINE(multiple_malloc_free_benchmark, jemalloc_pool_fix, fixed_alloc_size, pool_allocator>); UMF_BENCHMARK_REGISTER_F(multiple_malloc_free_benchmark, jemalloc_pool_fix) ->Apply(&default_multiple_alloc_fix_size) ->Apply(&multithreaded); UMF_BENCHMARK_TEMPLATE_DEFINE(multiple_malloc_free_benchmark, jemalloc_pool_uniform, uniform_alloc_size, pool_allocator>); UMF_BENCHMARK_REGISTER_F(multiple_malloc_free_benchmark, jemalloc_pool_uniform) ->Apply(&default_multiple_alloc_uniform_size) ->Apply(&multithreaded); #endif #ifdef UMF_POOL_SCALABLE_ENABLED UMF_BENCHMARK_TEMPLATE_DEFINE(multiple_malloc_free_benchmark, scalable_pool_fix, fixed_alloc_size, pool_allocator>); UMF_BENCHMARK_REGISTER_F(multiple_malloc_free_benchmark, scalable_pool_fix) ->Apply(&default_multiple_alloc_fix_size) ->Apply(&multithreaded); UMF_BENCHMARK_TEMPLATE_DEFINE(multiple_malloc_free_benchmark, scalable_pool_uniform, uniform_alloc_size, pool_allocator>); UMF_BENCHMARK_REGISTER_F(multiple_malloc_free_benchmark, scalable_pool_uniform) ->Apply(&default_multiple_alloc_uniform_size) ->Apply(&multithreaded); #endif UMF_BENCHMARK_TEMPLATE_DEFINE(multiple_malloc_free_benchmark, proxy_pool_fixedprovider, fixed_alloc_size, pool_allocator>); UMF_BENCHMARK_REGISTER_F(multiple_malloc_free_benchmark, proxy_pool_fixedprovider) ->Apply(&default_multiple_alloc_fix_size) ->Apply(&singlethreaded); UMF_BENCHMARK_TEMPLATE_DEFINE(multiple_malloc_free_benchmark, fixed_provider, fixed_alloc_size, provider_allocator); UMF_BENCHMARK_REGISTER_F(multiple_malloc_free_benchmark, fixed_provider) ->Apply(&default_multiple_alloc_fix_size) ->Apply(&singlethreaded); UMF_BENCHMARK_TEMPLATE_DEFINE(multiple_malloc_free_benchmark, disjoint_pool_fix_fixedprovider, fixed_alloc_size, pool_allocator>); UMF_BENCHMARK_REGISTER_F(multiple_malloc_free_benchmark, disjoint_pool_fix_fixedprovider) ->Apply(&default_multiple_alloc_fix_size) ->Apply(&multithreaded); UMF_BENCHMARK_TEMPLATE_DEFINE(multiple_malloc_free_benchmark, disjoint_pool_uniform_fixedprovider, uniform_alloc_size, pool_allocator>); UMF_BENCHMARK_REGISTER_F(multiple_malloc_free_benchmark, disjoint_pool_uniform_fixedprovider) ->Apply(&default_multiple_alloc_uniform_size) ->Apply(&multithreaded); #ifdef UMF_POOL_JEMALLOC_ENABLED UMF_BENCHMARK_TEMPLATE_DEFINE(multiple_malloc_free_benchmark, jemalloc_pool_fixedprovider, fixed_alloc_size, pool_allocator>); UMF_BENCHMARK_REGISTER_F(multiple_malloc_free_benchmark, jemalloc_pool_fixedprovider) ->Apply(&default_multiple_alloc_fix_size) ->Apply(&multithreaded); UMF_BENCHMARK_TEMPLATE_DEFINE(multiple_malloc_free_benchmark, jemalloc_pool_uniform_fixedprovider, uniform_alloc_size, pool_allocator>); UMF_BENCHMARK_REGISTER_F(multiple_malloc_free_benchmark, jemalloc_pool_uniform_fixedprovider) ->Apply(&default_multiple_alloc_uniform_size) ->Apply(&multithreaded); #endif #ifdef UMF_POOL_SCALABLE_ENABLED UMF_BENCHMARK_TEMPLATE_DEFINE(multiple_malloc_free_benchmark, scalable_pool_fix_fixedprovider, fixed_alloc_size, pool_allocator>); UMF_BENCHMARK_REGISTER_F(multiple_malloc_free_benchmark, scalable_pool_fix_fixedprovider) ->Apply(&default_multiple_alloc_fix_size) ->Apply(&multithreaded); UMF_BENCHMARK_TEMPLATE_DEFINE(multiple_malloc_free_benchmark, scalable_pool_uniform_fixedprovider, uniform_alloc_size, pool_allocator>); UMF_BENCHMARK_REGISTER_F(multiple_malloc_free_benchmark, scalable_pool_uniform_fixedprovider) ->Apply(&default_multiple_alloc_uniform_size) ->Apply(&multithreaded); #endif //BENCHMARK_MAIN(); int main(int argc, char **argv) { if (initAffinityMask()) { return -1; } benchmark::Initialize(&argc, argv); benchmark::RunSpecifiedBenchmarks(); benchmark::Shutdown(); } umf/benchmark/benchmark.hpp0000644000000000000000000003407614544400200013133 0ustar /* * Copyright (C) 2024-2025 Intel Corporation * * Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception * */ /* * This file defines a benchmarking framework for evaluating memory allocation * and deallocation performance using the Unified Memory Framework (UMF). The * design is modular and extensible, allowing for flexible benchmarking of different * allocation strategies, size distributions, and memory providers. * * **Key Design Features:** * - **Modular Components**: The framework is built using interfaces and templates, * which allows for easy extension and customization of allocation strategies, * size distributions, and memory providers. * - **Flexible Allocation Size Generators**: Includes classes like `fixed_alloc_size` * and `uniform_alloc_size` that generate allocation sizes based on different * strategies. These classes implement the `alloc_size_interface`. * - **Abstract Allocator Interface**: The `allocator_interface` defines the basic * methods for memory allocation and deallocation. Concrete allocators like * `provider_allocator` and `pool_allocator` implement this interface to work * with different memory providers and pools. * - **Benchmarking Classes**: Classes like `alloc_benchmark` and `multiple_malloc_free_benchmark` * templates the allocation size generator and allocator to perform benchmarks. * It manages the setup, execution, and teardown of the benchmark. * - **Threaded Execution Support**: The benchmarks support multi-threaded execution * by maintaining thread-specific allocation data and synchronization. * * **Component Interactions:** * - **Size Generators and Allocators**: The `alloc_benchmark` class uses a size * generator (e.g., `fixed_alloc_size` or `uniform_alloc_size`) to determine the * sizes of memory allocations, and an allocator (e.g., `provider_allocator` or * `pool_allocator`) to perform the actual memory operations. * - **Benchmark Execution**: During the benchmark, `alloc_benchmark` repeatedly * calls the `bench` method, which performs allocations and deallocations using * the allocator and size generator. * - **Allocator Adapters**: The `provider_allocator` and `pool_allocator` adapt * specific memory providers and pools to the `allocator_interface`, allowing * them to be used interchangeably in the benchmark classes. This abstraction * enables benchmarking different memory management strategies without changing * the core benchmarking logic. * - **Pre-allocations and Iterations**: The `alloc_benchmark` can perform a set * number of pre-allocations before the benchmark starts, and manages allocation * and deallocation cycles to simulate memory pressure and fragmentation. * - **Derived Benchmarks**: `multiple_malloc_free_benchmark` extends * `alloc_benchmark` to perform multiple random deallocations and reallocations * in each iteration, using a uniform distribution to select which allocations * to free and reallocate. This models workloads with frequent memory churn. * * **Execution Flow:** * 1. **Setup Phase**: * - The benchmark class initializes the size generator and allocator. * - Pre-allocations are performed if specified. * - Thread-specific data structures for allocations are prepared. * 2. **Benchmark Loop**: * - For each iteration, the `bench` method is called. * - The size generator provides the next allocation size. * - The allocator performs the allocation. * - Allocations are tracked per thread. * 3. **Teardown Phase**: * - All remaining allocations are freed. * - Allocator and size generator are cleaned up. * * **Customization and Extension:** * - New size generators can be created by implementing the `alloc_size_interface`. * - New allocators can be adapted by implementing the `allocator_interface`. * - Additional benchmarking scenarios can be created by extending `benchmark_interface`. */ #include #include #include #include #include #include "benchmark_size.hpp" #include "benchmark_umf.hpp" struct alloc_data { void *ptr; size_t size; }; struct next_alloc_data { size_t offset; size_t size; }; #ifndef WIN32 std::vector affinityMask; int initAffinityMask() { cpu_set_t mask; CPU_ZERO(&mask); if (sched_getaffinity(0, sizeof(mask), &mask) == -1) { perror("sched_getaffinity"); return 1; } for (int cpu = 0; cpu < CPU_SETSIZE; cpu++) { if (CPU_ISSET(cpu, &mask)) { cpu_set_t mask; CPU_ZERO(&mask); CPU_SET(cpu, &mask); affinityMask.push_back(mask); } } // we reverse affinityMask to avoid using cpu 0 if possible. // CPU 0 is usually the most used one by other applications on the system. std::reverse(affinityMask.begin(), affinityMask.end()); return 0; } void setAffinity(benchmark::State &state) { size_t tid = state.thread_index(); if (tid >= affinityMask.size()) { state.SkipWithError("Not enough CPUs available to set affinity"); } auto &mask = affinityMask[tid]; if (sched_setaffinity(0, sizeof(mask), &mask) != 0) { state.SkipWithError("Failed to set affinity"); } } #else int initAffinityMask() { printf( "Affinity set not supported on Windows, benchmark can be unstable\n"); return 0; } void setAffinity([[maybe_unused]] benchmark::State &state) { // Not implemented for Windows } #endif // function that ensures that all threads have reached the same point inline void waitForAllThreads(const benchmark::State &state) { static std::atomic count{0}; static std::atomic generation{0}; const int totalThreads = state.threads(); int gen = generation.load(std::memory_order_relaxed); int c = count.fetch_add(1, std::memory_order_acq_rel) + 1; if (c == totalThreads) { // Last thread - reset count and bump generation count.store(0, std::memory_order_relaxed); generation.fetch_add(1, std::memory_order_acq_rel); } else { // Not the last thread: spin until the generation changes while (generation.load(std::memory_order_acquire) == gen) { std::this_thread::yield(); } } } template ::value>> class provider_allocator : public allocator_interface { public: unsigned SetUp(::benchmark::State &state, unsigned argPos) override { provider.SetUp(state); return argPos; } void TearDown(::benchmark::State &state) override { provider.TearDown(state); } void *benchAlloc(size_t size) override { void *ptr; if (umfMemoryProviderAlloc(provider.provider, size, 0, &ptr) != UMF_RESULT_SUCCESS) { return NULL; } return ptr; } void benchFree(void *ptr, size_t size) override { umfMemoryProviderFree(provider.provider, ptr, size); } static std::string name() { return Provider::name(); } private: Provider provider; }; // TODO: assert Pool to be a pool_interface. template class pool_allocator : public allocator_interface { public: unsigned SetUp(::benchmark::State &state, unsigned argPos) override { pool.SetUp(state); return argPos; } void TearDown(::benchmark::State &state) override { pool.TearDown(state); } virtual void *benchAlloc(size_t size) override { return umfPoolMalloc(pool.pool, size); } virtual void benchFree(void *ptr, [[maybe_unused]] size_t size) override { umfPoolFree(pool.pool, ptr); } static std::string name() { return Pool::name(); } private: Pool pool; }; template struct benchmark_interface : public benchmark::Fixture { int parseArgs(::benchmark::State &state, int argPos) { Size generator; argPos = generator.SetUp(state, argPos); argPos = allocator.SetUp(state, argPos); alloc_sizes.resize(state.threads()); for (auto &i : alloc_sizes) { i = generator; } return argPos; } void SetUp(::benchmark::State &state) { parseArgs(state, 0); } void TearDown(::benchmark::State &state) { for (auto &i : alloc_sizes) { i.TearDown(state); } allocator.TearDown(state); } virtual void bench(::benchmark::State &state) = 0; virtual std::vector argsName() { auto s = Size::argsName(); auto a = Allocator::argsName(); std::vector res = {}; res.insert(res.end(), s.begin(), s.end()); res.insert(res.end(), a.begin(), a.end()); return res; } virtual std::string name() { return Allocator::name(); } static void defaultArgs(Benchmark *benchmark) { auto *bench = static_cast *>(benchmark); benchmark->ArgNames(bench->argsName())->Name(bench->name()); } std::vector alloc_sizes; Allocator allocator; }; // This class benchmarks performance of random deallocations and (re)allocations template < typename Size, typename Alloc, typename = std::enable_if_t::value>, typename = std::enable_if_t::value>> class multiple_malloc_free_benchmark : public benchmark_interface { using distribution = std::uniform_int_distribution; template using vector2d = std::vector>; using base = benchmark_interface; int allocsPerIterations = 10; bool thread_local_allocations = true; size_t max_allocs = 0; vector2d allocations; std::vector iters; vector2d next; std::vector::const_iterator> next_iter; int64_t iterations; public: void SetUp(::benchmark::State &state) override { auto tid = state.thread_index(); if (tid == 0) { // unpack arguments iterations = state.max_iterations; int argPos = 0; max_allocs = state.range(argPos++); thread_local_allocations = state.range(argPos++); base::parseArgs(state, argPos); allocations.resize(state.threads()); next.resize(state.threads()); next_iter.resize(state.threads()); #ifndef WIN32 // Ensure that system malloc does not have memory pooled on the heap malloc_trim(0); #endif } setAffinity(state); // sync thread to ensure that thread 0 parsed args and did all initialization waitForAllThreads(state); // Prepare workload for warp up prealloc(state); prepareWorkload(state); // Start warm up with all threads at once waitForAllThreads(state); // warm up for (int j = 0; j < iterations; j++) { bench(state); } waitForAllThreads(state); // prepare workload for actual benchmark. freeAllocs(state); prealloc(state); prepareWorkload(state); } void TearDown(::benchmark::State &state) override { auto tid = state.thread_index(); freeAllocs(state); waitForAllThreads(state); if (tid == 0) { // release memory used by benchmark next.clear(); next_iter.clear(); allocations.clear(); iters.clear(); } base::TearDown(state); } void bench(benchmark::State &state) override { auto tid = state.thread_index(); auto &allocation = allocations[tid]; for (int i = 0; i < allocsPerIterations; i++) { auto &n = *next_iter[tid]++; auto &alloc = allocation[n.offset]; base::allocator.benchFree(alloc.ptr, alloc.size); alloc.size = n.size; alloc.ptr = base::allocator.benchAlloc(alloc.size); if (alloc.ptr == NULL) { state.SkipWithError("allocation failed"); } } } virtual std::string name() { return base::name() + "/multiple_malloc_free"; } virtual std::vector argsName() { auto n = benchmark_interface::argsName(); std::vector res = {"max_allocs", "thread_local_allocations"}; res.insert(res.end(), n.begin(), n.end()); return res; } private: void prealloc(benchmark::State &state) { auto tid = state.thread_index(); auto &i = allocations[tid]; i.resize(max_allocs); auto sizeGenerator = base::alloc_sizes[tid]; for (size_t j = 0; j < max_allocs; j++) { auto size = sizeGenerator.nextSize(); i[j].ptr = base::allocator.benchAlloc(size); if (i[j].ptr == NULL) { state.SkipWithError("preallocation failed"); return; } i[j].size = size; } } void freeAllocs(benchmark::State &state) { auto tid = state.thread_index(); auto &i = allocations[tid]; for (auto &j : i) { if (j.ptr != NULL) { base::allocator.benchFree(j.ptr, j.size); j.ptr = NULL; j.size = 0; } } } void prepareWorkload(benchmark::State &state) { auto tid = state.thread_index(); auto &n = next[tid]; std::default_random_engine generator; distribution dist; generator.seed(0); dist.param(distribution::param_type(0, max_allocs - 1)); auto sizeGenerator = base::alloc_sizes[tid]; n.clear(); for (int64_t j = 0; j < state.max_iterations * allocsPerIterations; j++) { n.push_back({dist(generator), sizeGenerator.nextSize()}); } next_iter[tid] = n.cbegin(); } }; umf/benchmark/benchmark_size.hpp0000644000000000000000000000402214544400200014151 0ustar /* * Copyright (C) 2024-2025 Intel Corporation * * Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception * */ #include #include #include class alloc_size_interface { public: virtual unsigned SetUp([[maybe_unused]] ::benchmark::State &state, [[maybe_unused]] unsigned argPos) = 0; virtual void TearDown([[maybe_unused]] ::benchmark::State &state) = 0; virtual size_t nextSize() = 0; static std::vector argsName() { return {""}; }; }; class fixed_alloc_size : public alloc_size_interface { public: unsigned SetUp(::benchmark::State &state, unsigned argPos) override { size = state.range(argPos); return argPos + 1; } void TearDown([[maybe_unused]] ::benchmark::State &state) override {} size_t nextSize() override { return size; }; static std::vector argsName() { return {"size"}; } private: size_t size; }; class uniform_alloc_size : public alloc_size_interface { using distribution = std::uniform_int_distribution; public: unsigned SetUp(::benchmark::State &state, unsigned argPos) override { auto min = state.range(argPos++); auto max = state.range(argPos++); auto gran = state.range(argPos++); if (min % gran != 0 && max % gran != 0) { state.SkipWithError("min and max must be divisible by granularity"); return argPos; } generator.seed(0); dist.param(distribution::param_type(min / gran, max / gran)); multiplier = gran; return argPos; } void TearDown([[maybe_unused]] ::benchmark::State &state) override {} size_t nextSize() override { return dist(generator) * multiplier; } static std::vector argsName() { return {"min_size", "max_size", "granularity"}; } private: std::default_random_engine generator; distribution dist; size_t multiplier = 1; }; umf/benchmark/benchmark_umf.hpp0000644000000000000000000002306214544400200013773 0ustar /* * Copyright (C) 2024-2025 Intel Corporation * * Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception * */ #include #include #include #include #include #include #include #include #ifdef UMF_POOL_SCALABLE_ENABLED #include #endif #include #include #ifdef UMF_POOL_JEMALLOC_ENABLED #include #endif struct provider_interface { using params_ptr = std::unique_ptr; umf_memory_provider_handle_t provider = NULL; virtual void SetUp(::benchmark::State &state) { if (state.thread_index() != 0) { return; } auto params = getParams(state); auto umf_result = umfMemoryProviderCreate(getOps(state), params.get(), &provider); if (umf_result != UMF_RESULT_SUCCESS) { state.SkipWithError("umfMemoryProviderCreate() failed"); } } virtual void TearDown([[maybe_unused]] ::benchmark::State &state) { if (state.thread_index() != 0) { return; } if (provider) { umfMemoryProviderDestroy(provider); } } virtual umf_memory_provider_ops_t * getOps([[maybe_unused]] ::benchmark::State &state) { return nullptr; } virtual params_ptr getParams([[maybe_unused]] ::benchmark::State &state) { return {nullptr, [](void *) {}}; } }; template ::value>> struct pool_interface { using params_ptr = std::unique_ptr; virtual void SetUp(::benchmark::State &state) { provider.SetUp(state); if (state.thread_index() != 0) { return; } auto params = getParams(state); auto umf_result = umfPoolCreate(getOps(state), provider.provider, params.get(), 0, &pool); if (umf_result != UMF_RESULT_SUCCESS) { state.SkipWithError("umfPoolCreate() failed"); } } virtual void TearDown([[maybe_unused]] ::benchmark::State &state) { if (state.thread_index() != 0) { return; } // TODO: The scalable pool destruction process can race with other threads // performing TLS (Thread-Local Storage) destruction. // As a temporary workaround, we introduce a delay (sleep) // to ensure the pool is destroyed only after all threads have completed. // Issue: #933 std::this_thread::sleep_for(std::chrono::milliseconds(500)); if (pool) { umfPoolDestroy(pool); } }; virtual umf_memory_pool_ops_t * getOps([[maybe_unused]] ::benchmark::State &state) { return nullptr; } virtual params_ptr getParams([[maybe_unused]] ::benchmark::State &state) { return {nullptr, [](void *) {}}; } T provider; umf_memory_pool_handle_t pool; }; class allocator_interface { public: virtual unsigned SetUp([[maybe_unused]] ::benchmark::State &state, [[maybe_unused]] unsigned argPos) = 0; virtual void TearDown([[maybe_unused]] ::benchmark::State &state) = 0; virtual void *benchAlloc(size_t size) = 0; virtual void benchFree(void *ptr, [[maybe_unused]] size_t size) = 0; static std::vector argsName() { return {}; } }; struct glibc_malloc : public allocator_interface { unsigned SetUp([[maybe_unused]] ::benchmark::State &state, unsigned argPos) override { return argPos; } void TearDown([[maybe_unused]] ::benchmark::State &state) override{}; void *benchAlloc(size_t size) override { return malloc(size); } void benchFree(void *ptr, [[maybe_unused]] size_t size) override { free(ptr); } static std::string name() { return "glibc"; } }; struct os_provider : public provider_interface { provider_interface::params_ptr getParams(::benchmark::State &state) override { umf_os_memory_provider_params_handle_t raw_params = nullptr; umfOsMemoryProviderParamsCreate(&raw_params); if (!raw_params) { state.SkipWithError("Failed to create os provider params"); return {nullptr, [](void *) {}}; } // Use a lambda as the custom deleter auto deleter = [](void *p) { auto handle = static_cast(p); umfOsMemoryProviderParamsDestroy(handle); }; return {static_cast( raw_params), deleter}; } umf_memory_provider_ops_t * getOps([[maybe_unused]] ::benchmark::State &state) override { return umfOsMemoryProviderOps(); } static std::string name() { return "os_provider"; } }; struct fixed_provider : public provider_interface { private: char *mem = NULL; const size_t size = 1024 * 1024 * 1024; // 1GB public: virtual void SetUp(::benchmark::State &state) override { if (state.thread_index() != 0) { return; } if (!mem) { mem = new char[size]; } provider_interface::SetUp(state); } virtual void TearDown(::benchmark::State &state) override { if (state.thread_index() != 0) { return; } delete[] mem; mem = nullptr; provider_interface::TearDown(state); } provider_interface::params_ptr getParams(::benchmark::State &state) override { umf_fixed_memory_provider_params_handle_t raw_params = nullptr; umfFixedMemoryProviderParamsCreate(&raw_params, mem, size); if (!raw_params) { state.SkipWithError("Failed to create fixed provider params"); return {nullptr, [](void *) {}}; } // Use a lambda as the custom deleter auto deleter = [](void *p) { auto handle = static_cast(p); umfFixedMemoryProviderParamsDestroy(handle); }; return {static_cast( raw_params), deleter}; } umf_memory_provider_ops_t * getOps([[maybe_unused]] ::benchmark::State &state) override { return umfFixedMemoryProviderOps(); } static std::string name() { return "fixed_provider"; } }; template struct proxy_pool : public pool_interface { umf_memory_pool_ops_t * getOps([[maybe_unused]] ::benchmark::State &state) override { return umfProxyPoolOps(); } static std::string name() { return "proxy_pool<" + Provider::name() + ">"; } }; template struct disjoint_pool : public pool_interface { umf_memory_pool_ops_t * getOps([[maybe_unused]] ::benchmark::State &state) override { return umfDisjointPoolOps(); } typename pool_interface::params_ptr getParams(::benchmark::State &state) override { umf_disjoint_pool_params_handle_t raw_params = nullptr; auto ret = umfDisjointPoolParamsCreate(&raw_params); if (ret != UMF_RESULT_SUCCESS) { state.SkipWithError("Failed to create disjoint pool params"); return {nullptr, [](void *) {}}; } typename pool_interface::params_ptr params( raw_params, [](void *p) { umfDisjointPoolParamsDestroy( static_cast(p)); }); ret = umfDisjointPoolParamsSetSlabMinSize(raw_params, 4096); if (ret != UMF_RESULT_SUCCESS) { state.SkipWithError("Failed to set slab min size"); return {nullptr, [](void *) {}}; } ret = umfDisjointPoolParamsSetCapacity(raw_params, 4); if (ret != UMF_RESULT_SUCCESS) { state.SkipWithError("Failed to set capacity"); return {nullptr, [](void *) {}}; } ret = umfDisjointPoolParamsSetMinBucketSize(raw_params, 8); if (ret != UMF_RESULT_SUCCESS) { state.SkipWithError("Failed to set min bucket size"); return {nullptr, [](void *) {}}; } ret = umfDisjointPoolParamsSetMaxPoolableSize(raw_params, 4096 * 16); if (ret != UMF_RESULT_SUCCESS) { state.SkipWithError("Failed to set max poolable size"); return {nullptr, [](void *) {}}; } return params; } static std::string name() { return "disjoint_pool<" + Provider::name() + ">"; } }; #ifdef UMF_POOL_JEMALLOC_ENABLED template struct jemalloc_pool : public pool_interface { umf_memory_pool_ops_t * getOps([[maybe_unused]] ::benchmark::State &state) override { return umfJemallocPoolOps(); } static std::string name() { return "jemalloc_pool<" + Provider::name() + ">"; } }; #endif #ifdef UMF_POOL_SCALABLE_ENABLED template struct scalable_pool : public pool_interface { virtual umf_memory_pool_ops_t * getOps([[maybe_unused]] ::benchmark::State &state) override { return umfScalablePoolOps(); } static std::string name() { return "scalable_pool<" + Provider::name() + ">"; } }; #endif umf/benchmark/multithread.cpp0000644000000000000000000001150514544400200013506 0ustar /* * * Copyright (C) 2024-2025 Intel Corporation * * Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception * */ #include "multithread.hpp" #include #include #include #include #include #include #include #include struct bench_params { // bench_params() = default; size_t n_repeats = 5; size_t n_iterations = 50000; size_t n_threads = 20; size_t alloc_size = 64; }; using poolCreateExtParams = std::tuple; static auto poolCreateExtUnique(poolCreateExtParams params) { umf_memory_pool_handle_t hPool; auto [pool_ops, pool_params, provider_ops, provider_params] = params; umf_memory_provider_handle_t provider = nullptr; auto ret = umfMemoryProviderCreate(provider_ops, provider_params, &provider); if (ret != UMF_RESULT_SUCCESS) { std::cerr << "provider create failed" << std::endl; abort(); } ret = umfPoolCreate(pool_ops, provider, pool_params, UMF_POOL_CREATE_FLAG_OWN_PROVIDER, &hPool); if (ret != UMF_RESULT_SUCCESS) { std::cerr << "pool create failed" << std::endl; abort(); } return std::shared_ptr(hPool, &umfPoolDestroy); } static void mt_alloc_free(poolCreateExtParams params, const bench_params &bench = bench_params()) { auto pool = poolCreateExtUnique(params); std::vector> allocs(bench.n_threads); std::vector numFailures(bench.n_threads); for (auto &v : allocs) { v.reserve(bench.n_iterations); } auto values = umf_bench::measure( bench.n_repeats, bench.n_threads, [&, pool = pool.get()](auto thread_id) { for (size_t i = 0; i < bench.n_iterations; i++) { allocs[thread_id].push_back( umfPoolMalloc(pool, bench.alloc_size)); if (!allocs[thread_id].back()) { numFailures[thread_id]++; } } for (size_t i = 0; i < bench.n_iterations; i++) { umfPoolFree(pool, allocs[thread_id][i]); } // clear the vector as this function might be called multiple times allocs[thread_id].clear(); }); std::cout << "mean: " << umf_bench::mean(values) << " [ms] std_dev: " << umf_bench::std_dev(values) << " [ms]" << " (total alloc failures: " << std::accumulate(numFailures.begin(), numFailures.end(), 0ULL) << " out of " << bench.n_iterations * bench.n_repeats * bench.n_threads << ")" << std::endl; } int main() { umf_os_memory_provider_params_handle_t osParams = nullptr; umf_result_t res = umfOsMemoryProviderParamsCreate(&osParams); if (res != UMF_RESULT_SUCCESS) { std::cerr << "os memory provider params create failed" << std::endl; return -1; } #if defined(UMF_POOL_SCALABLE_ENABLED) // Increase iterations for scalable pool since it runs much faster than the remaining // ones. bench_params params; params.n_iterations *= 20; std::cout << "scalable_pool mt_alloc_free: "; mt_alloc_free(poolCreateExtParams{umfScalablePoolOps(), nullptr, umfOsMemoryProviderOps(), osParams}, params); #else std::cout << "skipping scalable_pool mt_alloc_free" << std::endl; #endif #if defined(UMF_POOL_JEMALLOC_ENABLED) std::cout << "jemalloc_pool mt_alloc_free: "; mt_alloc_free(poolCreateExtParams{umfJemallocPoolOps(), nullptr, umfOsMemoryProviderOps(), osParams}); #else std::cout << "skipping jemalloc_pool mt_alloc_free" << std::endl; #endif // NOTE: disjoint pool is always enabled umf_disjoint_pool_params_handle_t hDisjointParams = nullptr; umf_result_t ret = umfDisjointPoolParamsCreate(&hDisjointParams); if (ret != UMF_RESULT_SUCCESS) { std::cerr << "disjoint pool params create failed" << std::endl; return -1; } std::cout << "disjoint_pool mt_alloc_free: "; mt_alloc_free(poolCreateExtParams{umfDisjointPoolOps(), hDisjointParams, umfOsMemoryProviderOps(), osParams}); // ctest looks for "PASSED" in the output std::cout << "PASSED" << std::endl; ret = umfDisjointPoolParamsDestroy(hDisjointParams); if (ret != UMF_RESULT_SUCCESS) { std::cerr << "disjoint pool params destroy failed" << std::endl; return -1; } return 0; } umf/benchmark/multithread.hpp0000644000000000000000000000476114544400200013521 0ustar /* * * Copyright (C) 2024 Intel Corporation * * Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception * */ #ifndef UMF_BENCH_MULTITHREAD_HPP #define UMF_BENCH_MULTITHREAD_HPP #include #include #include #include #include #include #include #include #include #include #include "multithread_helpers.hpp" namespace umf_bench { template typename TimeUnit::rep measure(F &&func) { auto start = std::chrono::steady_clock::now(); func(); auto duration = std::chrono::duration_cast( std::chrono::steady_clock::now() - start); return duration.count(); } /* Measure time of execution of run_workload(thread_id) function. */ template auto measure(size_t iterations, size_t concurrency, F &&run_workload) { if (iterations == 1) { throw std::runtime_error("iterations must be > 1"); } using ResultsType = typename TimeUnit::rep; std::vector results; for (size_t i = 0; i < iterations; i++) { std::vector iteration_results(concurrency); umf_test::syncthreads_barrier syncthreads(concurrency); umf_test::parallel_exec(concurrency, [&](size_t id) { syncthreads(); iteration_results[id] = measure([&]() { run_workload(id); }); }); // skip the first 'warmup' iteration if (i != 0) { results.insert(results.end(), iteration_results.begin(), iteration_results.end()); } } return results; } template T min(const std::vector &values) { return *std::min_element(values.begin(), values.end()); } template T max(const std::vector &values) { return *std::max_element(values.begin(), values.end()); } template double mean(const std::vector &values) { return std::accumulate(values.begin(), values.end(), 0.0) / values.size(); } template double std_dev(const std::vector &values) { auto m = mean(values); std::vector diff_squares; diff_squares.reserve(values.size()); for (auto &v : values) { diff_squares.push_back((v - m) * (v - m)); } return std::sqrt(mean(diff_squares)); } } // namespace umf_bench #endif /* UMF_BENCH_MULTITHREAD_HPP */ umf/benchmark/ubench.c0000644000000000000000000005044714544400200012100 0ustar /* * * Copyright (C) 2023-2025 Intel Corporation * * Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception * */ #include #ifndef _WIN32 #include #endif #include #include #include #include #include #include #include #ifdef UMF_POOL_JEMALLOC_ENABLED #include #endif #include "utils_common.h" #if (defined UMF_PROVIDER_LEVEL_ZERO_ENABLED && defined UMF_BUILD_GPU_TESTS) #include "utils_level_zero.h" #endif // NOTE: with strict compilation flags, ubench compilation throws some // warnings. We disable them here because we do not want to change the ubench // code. // disable warning 6308:'realloc' might return null pointer: assigning null // pointer to 'failed_benchmarks', which is passed as an argument to 'realloc', // will cause the original memory block to be leaked. // disable warning 6001: Using uninitialized memory // '*ubench_state.benchmarks.name'. #if defined(_MSC_VER) #pragma warning(push) #pragma warning(disable : 6308) #pragma warning(disable : 6001) #endif // _MSC_VER #include "ubench.h" // BENCHMARK CONFIG #define N_ITERATIONS 1000 #define ALLOC_SIZE (utils_get_page_size()) // OS MEMORY PROVIDER CONFIG #define OS_MEMORY_PROVIDER_TRACE (0) // DISJOINT POOL CONFIG #define DISJOINT_POOL_SLAB_MIN_SIZE (ALLOC_SIZE) #define DISJOINT_POOL_MAX_POOLABLE_SIZE (2 * ALLOC_SIZE) #define DISJOINT_POOL_CAPACITY (N_ITERATIONS + 10) #define DISJOINT_POOL_MIN_BUCKET_SIZE (ALLOC_SIZE) typedef struct alloc_s { void *ptr; size_t size; } alloc_t; typedef void *(*malloc_t)(void *provider, size_t size, size_t alignment); typedef void (*free_t)(void *provider, void *ptr, size_t size); static int Alloc_size; static void do_benchmark(alloc_t *array, size_t iters, malloc_t malloc_f, free_t free_f, void *provider) { int i = 0; do { array[i].ptr = malloc_f(provider, Alloc_size, 0); } while (array[i++].ptr != NULL && i < (int)iters); while (--i >= 0) { free_f(provider, array[i].ptr, Alloc_size); } } static alloc_t *alloc_array(size_t iters) { Alloc_size = (int)ALLOC_SIZE; alloc_t *array = malloc(iters * sizeof(alloc_t)); if (array == NULL) { perror("malloc() failed"); exit(-1); } return array; } ////////////////// GLIBC static void *glibc_malloc(void *provider, size_t size, size_t alignment) { (void)provider; // unused (void)alignment; // unused return malloc(size); } static void glibc_free(void *provider, void *ptr, size_t size) { (void)provider; // unused (void)size; // unused free(ptr); } UBENCH_EX(simple, glibc_malloc) { alloc_t *array = alloc_array(N_ITERATIONS); do_benchmark(array, N_ITERATIONS, glibc_malloc, glibc_free, NULL); // WARMUP UBENCH_DO_BENCHMARK() { do_benchmark(array, N_ITERATIONS, glibc_malloc, glibc_free, NULL); } free(array); } ////////////////// OS MEMORY PROVIDER static void *w_umfMemoryProviderAlloc(void *provider, size_t size, size_t alignment) { void *ptr = NULL; umf_result_t umf_result; umf_memory_provider_handle_t hProvider = (umf_memory_provider_handle_t)provider; umf_result = umfMemoryProviderAlloc(hProvider, size, alignment, &ptr); if (umf_result != UMF_RESULT_SUCCESS || ptr == NULL) { fprintf(stderr, "error: umfMemoryProviderAlloc() failed\n"); exit(-1); } return ptr; } static void w_umfMemoryProviderFree(void *provider, void *ptr, size_t size) { umf_result_t umf_result; umf_memory_provider_handle_t hProvider = (umf_memory_provider_handle_t)provider; umf_result = umfMemoryProviderFree(hProvider, ptr, size); if (umf_result != UMF_RESULT_SUCCESS) { fprintf(stderr, "error: umfMemoryProviderFree() failed\n"); exit(-1); } } UBENCH_EX(simple, os_memory_provider) { alloc_t *array = alloc_array(N_ITERATIONS); umf_result_t umf_result; umf_memory_provider_handle_t os_memory_provider = NULL; umf_os_memory_provider_params_handle_t os_params = NULL; umf_result = umfOsMemoryProviderParamsCreate(&os_params); if (umf_result != UMF_RESULT_SUCCESS) { fprintf(stderr, "error: umfOsMemoryProviderParamsCreate() failed\n"); exit(-1); } umf_result = umfMemoryProviderCreate(umfOsMemoryProviderOps(), os_params, &os_memory_provider); umfOsMemoryProviderParamsDestroy(os_params); if (umf_result != UMF_RESULT_SUCCESS) { fprintf(stderr, "error: umfMemoryProviderCreate() failed\n"); exit(-1); } do_benchmark(array, N_ITERATIONS, w_umfMemoryProviderAlloc, w_umfMemoryProviderFree, os_memory_provider); // WARMUP UBENCH_DO_BENCHMARK() { do_benchmark(array, N_ITERATIONS, w_umfMemoryProviderAlloc, w_umfMemoryProviderFree, os_memory_provider); } umfMemoryProviderDestroy(os_memory_provider); free(array); } static void *w_umfPoolMalloc(void *provider, size_t size, size_t alignment) { (void)alignment; // unused umf_memory_pool_handle_t hPool = (umf_memory_pool_handle_t)provider; return umfPoolMalloc(hPool, size); } static void w_umfPoolFree(void *provider, void *ptr, size_t size) { (void)size; // unused umf_result_t umf_result; umf_memory_pool_handle_t hPool = (umf_memory_pool_handle_t)provider; umf_result = umfPoolFree(hPool, ptr); if (umf_result != UMF_RESULT_SUCCESS) { fprintf(stderr, "error: umfPoolFree() failed\n"); exit(-1); } } ////////////////// PROXY POOL WITH OS MEMORY PROVIDER UBENCH_EX(simple, proxy_pool_with_os_memory_provider) { alloc_t *array = alloc_array(N_ITERATIONS); umf_result_t umf_result; umf_memory_provider_handle_t os_memory_provider = NULL; umf_os_memory_provider_params_handle_t os_params = NULL; umf_result = umfOsMemoryProviderParamsCreate(&os_params); if (umf_result != UMF_RESULT_SUCCESS) { fprintf(stderr, "error: umfOsMemoryProviderParamsCreate() failed\n"); exit(-1); } umf_result = umfMemoryProviderCreate(umfOsMemoryProviderOps(), os_params, &os_memory_provider); umfOsMemoryProviderParamsDestroy(os_params); if (umf_result != UMF_RESULT_SUCCESS) { fprintf(stderr, "error: umfMemoryProviderCreate() failed\n"); exit(-1); } umf_memory_pool_handle_t proxy_pool; umf_result = umfPoolCreate(umfProxyPoolOps(), os_memory_provider, NULL, 0, &proxy_pool); if (umf_result != UMF_RESULT_SUCCESS) { fprintf(stderr, "error: umfPoolCreate() failed\n"); exit(-1); } do_benchmark(array, N_ITERATIONS, w_umfPoolMalloc, w_umfPoolFree, proxy_pool); // WARMUP UBENCH_DO_BENCHMARK() { do_benchmark(array, N_ITERATIONS, w_umfPoolMalloc, w_umfPoolFree, proxy_pool); } umfPoolDestroy(proxy_pool); umfMemoryProviderDestroy(os_memory_provider); free(array); } ////////////////// DISJOINT POOL WITH OS MEMORY PROVIDER UBENCH_EX(simple, disjoint_pool_with_os_memory_provider) { alloc_t *array = alloc_array(N_ITERATIONS); umf_result_t umf_result; umf_memory_provider_handle_t os_memory_provider = NULL; umf_os_memory_provider_params_handle_t os_params = NULL; umf_result = umfOsMemoryProviderParamsCreate(&os_params); if (umf_result != UMF_RESULT_SUCCESS) { fprintf(stderr, "error: umfOsMemoryProviderParamsCreate() failed\n"); exit(-1); } umf_result = umfMemoryProviderCreate(umfOsMemoryProviderOps(), os_params, &os_memory_provider); umfOsMemoryProviderParamsDestroy(os_params); if (umf_result != UMF_RESULT_SUCCESS) { fprintf(stderr, "error: umfMemoryProviderCreate() failed\n"); exit(-1); } umf_disjoint_pool_params_handle_t disjoint_memory_pool_params = NULL; umf_result = umfDisjointPoolParamsCreate(&disjoint_memory_pool_params); if (umf_result != UMF_RESULT_SUCCESS) { fprintf(stderr, "ERROR: umfDisjointPoolParamsCreate failed\n"); exit(-1); } umf_result = umfDisjointPoolParamsSetSlabMinSize( disjoint_memory_pool_params, DISJOINT_POOL_SLAB_MIN_SIZE); if (umf_result != UMF_RESULT_SUCCESS) { fprintf(stderr, "error: umfDisjointPoolParamsSetSlabMinSize() failed\n"); exit(-1); } umf_result = umfDisjointPoolParamsSetMaxPoolableSize( disjoint_memory_pool_params, DISJOINT_POOL_MAX_POOLABLE_SIZE); if (umf_result != UMF_RESULT_SUCCESS) { fprintf(stderr, "error: umfDisjointPoolParamsSetMaxPoolableSize() failed\n"); exit(-1); } umf_result = umfDisjointPoolParamsSetCapacity(disjoint_memory_pool_params, DISJOINT_POOL_CAPACITY); if (umf_result != UMF_RESULT_SUCCESS) { fprintf(stderr, "error: umfDisjointPoolParamsSetCapacity() failed\n"); exit(-1); } umf_result = umfDisjointPoolParamsSetMinBucketSize( disjoint_memory_pool_params, DISJOINT_POOL_MIN_BUCKET_SIZE); if (umf_result != UMF_RESULT_SUCCESS) { fprintf(stderr, "error: umfDisjointPoolParamsSetMinBucketSize() failed\n"); exit(-1); } umf_memory_pool_handle_t disjoint_pool; umf_result = umfPoolCreate(umfDisjointPoolOps(), os_memory_provider, disjoint_memory_pool_params, 0, &disjoint_pool); if (umf_result != UMF_RESULT_SUCCESS) { fprintf(stderr, "error: umfPoolCreate() failed\n"); exit(-1); } do_benchmark(array, N_ITERATIONS, w_umfPoolMalloc, w_umfPoolFree, disjoint_pool); // WARMUP UBENCH_DO_BENCHMARK() { do_benchmark(array, N_ITERATIONS, w_umfPoolMalloc, w_umfPoolFree, disjoint_pool); } umfPoolDestroy(disjoint_pool); umfDisjointPoolParamsDestroy(disjoint_memory_pool_params); umfMemoryProviderDestroy(os_memory_provider); free(array); } #if (defined UMF_POOL_JEMALLOC_ENABLED) ////////////////// JEMALLOC POOL WITH OS MEMORY PROVIDER UBENCH_EX(simple, jemalloc_pool_with_os_memory_provider) { alloc_t *array = alloc_array(N_ITERATIONS); umf_result_t umf_result; umf_memory_provider_handle_t os_memory_provider = NULL; umf_os_memory_provider_params_handle_t os_params = NULL; umf_result = umfOsMemoryProviderParamsCreate(&os_params); if (umf_result != UMF_RESULT_SUCCESS) { fprintf(stderr, "error: umfOsMemoryProviderParamsCreate() failed\n"); exit(-1); } umf_result = umfMemoryProviderCreate(umfOsMemoryProviderOps(), os_params, &os_memory_provider); umfOsMemoryProviderParamsDestroy(os_params); if (umf_result != UMF_RESULT_SUCCESS) { fprintf(stderr, "error: umfMemoryProviderCreate() failed\n"); exit(-1); } umf_memory_pool_handle_t jemalloc_pool; umf_result = umfPoolCreate(umfJemallocPoolOps(), os_memory_provider, NULL, 0, &jemalloc_pool); if (umf_result != UMF_RESULT_SUCCESS) { fprintf(stderr, "error: umfPoolCreate() failed\n"); exit(-1); } do_benchmark(array, N_ITERATIONS, w_umfPoolMalloc, w_umfPoolFree, jemalloc_pool); // WARMUP UBENCH_DO_BENCHMARK() { do_benchmark(array, N_ITERATIONS, w_umfPoolMalloc, w_umfPoolFree, jemalloc_pool); } umfPoolDestroy(jemalloc_pool); umfMemoryProviderDestroy(os_memory_provider); free(array); } #endif /* (defined UMF_POOL_JEMALLOC_ENABLED) */ #if (defined UMF_POOL_SCALABLE_ENABLED) ////////////////// SCALABLE (TBB) POOL WITH OS MEMORY PROVIDER UBENCH_EX(simple, scalable_pool_with_os_memory_provider) { alloc_t *array = alloc_array(N_ITERATIONS); umf_result_t umf_result; umf_memory_provider_handle_t os_memory_provider = NULL; umf_os_memory_provider_params_handle_t os_params = NULL; umf_result = umfOsMemoryProviderParamsCreate(&os_params); if (umf_result != UMF_RESULT_SUCCESS) { fprintf(stderr, "error: umfOsMemoryProviderParamsCreate() failed\n"); exit(-1); } umf_result = umfMemoryProviderCreate(umfOsMemoryProviderOps(), os_params, &os_memory_provider); umfOsMemoryProviderParamsDestroy(os_params); if (umf_result != UMF_RESULT_SUCCESS) { fprintf(stderr, "error: umfMemoryProviderCreate() failed\n"); exit(-1); } umf_memory_pool_handle_t scalable_pool; umf_result = umfPoolCreate(umfScalablePoolOps(), os_memory_provider, NULL, 0, &scalable_pool); if (umf_result != UMF_RESULT_SUCCESS) { fprintf(stderr, "error: umfPoolCreate() failed\n"); exit(-1); } do_benchmark(array, N_ITERATIONS, w_umfPoolMalloc, w_umfPoolFree, scalable_pool); // WARMUP UBENCH_DO_BENCHMARK() { do_benchmark(array, N_ITERATIONS, w_umfPoolMalloc, w_umfPoolFree, scalable_pool); } umfPoolDestroy(scalable_pool); umfMemoryProviderDestroy(os_memory_provider); free(array); } #endif /* (defined UMF_POOL_SCALABLE_ENABLED) */ #if (defined UMF_PROVIDER_LEVEL_ZERO_ENABLED && defined UMF_BUILD_GPU_TESTS) static void do_ipc_get_put_benchmark(alloc_t *allocs, size_t num_allocs, size_t repeats, umf_ipc_handle_t *ipc_handles) { for (size_t r = 0; r < repeats; ++r) { for (size_t i = 0; i < num_allocs; ++i) { size_t handle_size = 0; umf_result_t res = umfGetIPCHandle(allocs[i].ptr, &(ipc_handles[i]), &handle_size); if (res != UMF_RESULT_SUCCESS) { fprintf(stderr, "umfGetIPCHandle() failed\n"); } } for (size_t i = 0; i < num_allocs; ++i) { umf_result_t res = umfPutIPCHandle(ipc_handles[i]); if (res != UMF_RESULT_SUCCESS) { fprintf(stderr, "umfPutIPCHandle() failed\n"); } } } } static int create_level_zero_params(ze_context_handle_t *context, ze_device_handle_t *device) { uint32_t driver_idx = 0; ze_driver_handle_t driver = NULL; int ret = utils_ze_init_level_zero(); if (ret != 0) { fprintf(stderr, "Failed to init Level Zero!\n"); return ret; } ret = utils_ze_find_driver_with_gpu(&driver_idx, &driver); if (ret || driver == NULL) { fprintf(stderr, "Cannot find L0 driver with GPU device!\n"); return ret; } ret = utils_ze_create_context(driver, context); if (ret != 0) { fprintf(stderr, "Failed to create L0 context!\n"); return ret; } ret = utils_ze_find_gpu_device(driver, device); if (ret) { fprintf(stderr, "Cannot find GPU device!\n"); utils_ze_destroy_context(*context); return ret; } return ret; } UBENCH_EX(ipc, disjoint_pool_with_level_zero_provider) { const size_t BUFFER_SIZE = 100; const size_t N_BUFFERS = 1000; umf_result_t umf_result; ze_context_handle_t context = NULL; ze_device_handle_t device = NULL; umf_level_zero_memory_provider_params_handle_t level_zero_params = NULL; int ret = create_level_zero_params(&context, &device); if (ret != 0) { fprintf(stderr, "error: create_level_zero_params() failed\n"); exit(-1); } umf_result = umfLevelZeroMemoryProviderParamsCreate(&level_zero_params); if (umf_result != UMF_RESULT_SUCCESS) { fprintf(stderr, "error: umfLevelZeroMemoryProviderParamsCreate() failed\n"); goto err_destroy_context; } umf_result = umfLevelZeroMemoryProviderParamsSetContext(level_zero_params, context); if (umf_result != UMF_RESULT_SUCCESS) { fprintf(stderr, "error: umfLevelZeroMemoryProviderParamsSetContext() failed\n"); goto err_destroy_params; } umf_result = umfLevelZeroMemoryProviderParamsSetDevice(level_zero_params, device); if (umf_result != UMF_RESULT_SUCCESS) { fprintf(stderr, "error: umfLevelZeroMemoryProviderParamsSetDevice() failed\n"); goto err_destroy_params; } umf_result = umfLevelZeroMemoryProviderParamsSetMemoryType( level_zero_params, UMF_MEMORY_TYPE_DEVICE); if (umf_result != UMF_RESULT_SUCCESS) { fprintf( stderr, "error: umfLevelZeroMemoryProviderParamsSetMemoryType() failed\n"); goto err_destroy_params; } alloc_t *allocs = alloc_array(N_BUFFERS); if (allocs == NULL) { fprintf(stderr, "error: alloc_array() failed\n"); goto err_destroy_context; } umf_ipc_handle_t *ipc_handles = calloc(N_BUFFERS, sizeof(umf_ipc_handle_t)); if (ipc_handles == NULL) { fprintf(stderr, "error: calloc() failed\n"); goto err_free_allocs; } umf_memory_provider_handle_t provider = NULL; umf_result = umfMemoryProviderCreate(umfLevelZeroMemoryProviderOps(), level_zero_params, &provider); if (umf_result != UMF_RESULT_SUCCESS) { fprintf(stderr, "error: umfMemoryProviderCreate() failed\n"); goto err_free_ipc_handles; } umf_disjoint_pool_params_handle_t disjoint_params = NULL; umf_result = umfDisjointPoolParamsCreate(&disjoint_params); if (umf_result != UMF_RESULT_SUCCESS) { fprintf(stderr, "ERROR: umfDisjointPoolParamsCreate failed\n"); goto err_provider_destroy; } umf_result = umfDisjointPoolParamsSetSlabMinSize(disjoint_params, BUFFER_SIZE * 10); if (umf_result != UMF_RESULT_SUCCESS) { fprintf(stderr, "error: umfDisjointPoolParamsSetSlabMinSize() failed\n"); goto err_params_destroy; } umf_result = umfDisjointPoolParamsSetMaxPoolableSize( disjoint_params, 4ull * 1024ull * 1024ull); if (umf_result != UMF_RESULT_SUCCESS) { fprintf(stderr, "error: umfDisjointPoolParamsSetMaxPoolableSize() failed\n"); goto err_params_destroy; } umf_result = umfDisjointPoolParamsSetCapacity(disjoint_params, 64ull * 1024ull); if (umf_result != UMF_RESULT_SUCCESS) { fprintf(stderr, "error: umfDisjointPoolParamsSetCapacity() failed\n"); goto err_params_destroy; } umf_result = umfDisjointPoolParamsSetMinBucketSize(disjoint_params, 64); if (umf_result != UMF_RESULT_SUCCESS) { fprintf(stderr, "error: umfDisjointPoolParamsSetMinBucketSize() failed\n"); goto err_params_destroy; } umf_pool_create_flags_t flags = UMF_POOL_CREATE_FLAG_NONE; umf_memory_pool_handle_t pool; umf_result = umfPoolCreate(umfDisjointPoolOps(), provider, disjoint_params, flags, &pool); if (umf_result != UMF_RESULT_SUCCESS) { fprintf(stderr, "error: umfPoolCreate() failed\n"); goto err_params_destroy; } for (size_t i = 0; i < N_BUFFERS; ++i) { allocs[i].ptr = umfPoolMalloc(pool, BUFFER_SIZE); if (allocs[i].ptr == NULL) { goto err_buffer_destroy; } allocs[i].size = BUFFER_SIZE; } do_ipc_get_put_benchmark(allocs, N_BUFFERS, N_ITERATIONS, ipc_handles); // WARMUP UBENCH_DO_BENCHMARK() { do_ipc_get_put_benchmark(allocs, N_BUFFERS, N_ITERATIONS, ipc_handles); } err_buffer_destroy: for (size_t i = 0; i < N_BUFFERS; ++i) { umfPoolFree(pool, allocs[i].ptr); } umfPoolDestroy(pool); err_params_destroy: umfDisjointPoolParamsDestroy(disjoint_params); err_provider_destroy: umfMemoryProviderDestroy(provider); err_free_ipc_handles: free(ipc_handles); err_free_allocs: free(allocs); err_destroy_params: umfLevelZeroMemoryProviderParamsDestroy(level_zero_params); err_destroy_context: utils_ze_destroy_context(context); } #endif /* (defined UMF_BUILD_LEVEL_ZERO_PROVIDER && defined UMF_BUILD_GPU_TESTS) */ // TODO add IPC benchmark for CUDA UBENCH_MAIN() #if defined(_MSC_VER) #pragma warning(pop) #endif // _MSC_VER umf/benchmark/ubench.h0000644000000000000000000010021514544400200012072 0ustar /* The latest version of this library is available on GitHub; https://github.com/sheredom/ubench.h */ /* This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. For more information, please refer to */ #ifndef SHEREDOM_UBENCH_H_INCLUDED #define SHEREDOM_UBENCH_H_INCLUDED #ifdef _MSC_VER /* Disable warning about not inlining 'inline' functions. TODO: We'll fix this later by not using fprintf within our macros, and instead use snprintf to a realloc'ed buffer. */ #pragma warning(disable : 4710) /* Disable warning about inlining functions that are not marked 'inline'. TODO: add a UBENCH_NOINLINE onto the macro generated functions to fix this. */ #pragma warning(disable : 4711) /* Disable warning about replacing undefined preprocessor macro '__cplusplus' with 0 emitted from microsofts own headers. See: https://developercommunity.visualstudio.com/t/issue-in-corecrth-header-results-in-an-undefined-m/433021 */ #pragma warning(disable : 4668) #if _MSC_VER > 1930 /* Disable warning about 'const' variable is not used. */ #pragma warning(disable : 5264) #endif #pragma warning(push, 1) #endif #if defined(__cplusplus) #define UBENCH_C_FUNC extern "C" #else #define UBENCH_C_FUNC #endif #if defined(__cplusplus) #define UBENCH_NULL NULL #else #define UBENCH_NULL 0 #endif #if defined(_MSC_VER) && (_MSC_VER < 1920) typedef __int64 ubench_int64_t; typedef unsigned __int64 ubench_uint64_t; #else #include typedef int64_t ubench_int64_t; typedef uint64_t ubench_uint64_t; #endif #include #include #include #include #include #if defined(_MSC_VER) #pragma warning(pop) #endif #if defined(_MSC_VER) typedef union { struct { unsigned long LowPart; long HighPart; } DUMMYSTRUCTNAME; struct { unsigned long LowPart; long HighPart; } u; ubench_int64_t QuadPart; } ubench_large_integer; UBENCH_C_FUNC __declspec(dllimport) int __stdcall QueryPerformanceCounter( ubench_large_integer *); UBENCH_C_FUNC __declspec(dllimport) int __stdcall QueryPerformanceFrequency( ubench_large_integer *); #elif defined(__linux__) /* slightly obscure include here - we need to include glibc's features.h, but we don't want to just include a header that might not be defined for other c libraries like musl. Instead we include limits.h, which we know on all glibc distributions includes features.h */ #include #if defined(__GLIBC__) && defined(__GLIBC_MINOR__) #include #if ((2 < __GLIBC__) || ((2 == __GLIBC__) && (17 <= __GLIBC_MINOR__))) /* glibc is version 2.17 or above, so we can just use clock_gettime */ #define UBENCH_USE_CLOCKGETTIME #else #include #include #endif #endif #elif defined(__APPLE__) #include #endif #if defined(__cplusplus) #define UBENCH_C_FUNC extern "C" #else #define UBENCH_C_FUNC #endif #if defined(__cplusplus) && (__cplusplus >= 201103L) #define UBENCH_NOEXCEPT noexcept #else #define UBENCH_NOEXCEPT #endif #if defined(__cplusplus) && defined(_MSC_VER) #define UBENCH_NOTHROW __declspec(nothrow) #else #define UBENCH_NOTHROW #endif #if defined(_MSC_VER) && (_MSC_VER < 1920) #define UBENCH_PRId64 "I64d" #define UBENCH_PRIu64 "I64u" #else #include #define UBENCH_PRId64 PRId64 #define UBENCH_PRIu64 PRIu64 #endif #if defined(__cplusplus) #define UBENCH_INLINE inline #elif defined(_MSC_VER) #define UBENCH_INLINE __forceinline #else #define UBENCH_INLINE inline #endif #if defined(_MSC_VER) #define UBENCH_NOINLINE __declspec(noinline) #else #define UBENCH_NOINLINE __attribute__((noinline)) #endif #if defined(__cplusplus) #if defined(__clang__) #define UBENCH_INITIALIZER_BEGIN_DISABLE_WARNINGS \ _Pragma("clang diagnostic push") \ _Pragma("clang diagnostic ignored \"-Wglobal-constructors\"") #define UBENCH_INITIALIZER_END_DISABLE_WARNINGS _Pragma("clang diagnostic pop") #else #define UBENCH_INITIALIZER_BEGIN_DISABLE_WARNINGS #define UBENCH_INITIALIZER_END_DISABLE_WARNINGS #endif #define UBENCH_INITIALIZER(f) \ struct f##_cpp_struct { \ f##_cpp_struct(); \ }; \ UBENCH_INITIALIZER_BEGIN_DISABLE_WARNINGS static f##_cpp_struct \ f##_cpp_global UBENCH_INITIALIZER_END_DISABLE_WARNINGS; \ f##_cpp_struct::f##_cpp_struct() #elif defined(_MSC_VER) #if defined(_WIN64) #define UBENCH_SYMBOL_PREFIX #else #define UBENCH_SYMBOL_PREFIX "_" #endif #if defined(__clang__) #define UBENCH_INITIALIZER_BEGIN_DISABLE_WARNINGS \ _Pragma("clang diagnostic push") _Pragma( \ "clang diagnostic ignored \"-Wmissing-variable-declarations\"") #define UBENCH_INITIALIZER_END_DISABLE_WARNINGS _Pragma("clang diagnostic pop") #else #define UBENCH_INITIALIZER_BEGIN_DISABLE_WARNINGS #define UBENCH_INITIALIZER_END_DISABLE_WARNINGS #endif #pragma section(".CRT$XCU", read) #define UBENCH_INITIALIZER(f) \ static void __cdecl f(void); \ UBENCH_INITIALIZER_BEGIN_DISABLE_WARNINGS __pragma( \ comment(linker, "/include:" UBENCH_SYMBOL_PREFIX #f "_")) \ UBENCH_C_FUNC __declspec(allocate(".CRT$XCU")) void(__cdecl * \ f##_)(void) = f; \ UBENCH_INITIALIZER_END_DISABLE_WARNINGS static void __cdecl f(void) #else #if defined(__linux__) #if defined(__clang__) #if __has_warning("-Wreserved-id-macro") #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wreserved-id-macro" #endif #endif #define __STDC_FORMAT_MACROS 1 #if defined(__clang__) #if __has_warning("-Wreserved-id-macro") #pragma clang diagnostic pop #endif #endif #endif #define UBENCH_INITIALIZER(f) \ static void f(void) __attribute__((constructor)); \ static void f(void) #endif #if defined(__cplusplus) #define UBENCH_CAST(type, x) static_cast(x) #define UBENCH_PTR_CAST(type, x) reinterpret_cast(x) #define UBENCH_EXTERN extern "C" #else #define UBENCH_CAST(type, x) ((type)(x)) #define UBENCH_PTR_CAST(type, x) ((type)(x)) #define UBENCH_EXTERN extern #endif #ifdef _MSC_VER /* io.h contains definitions for some structures with natural padding. This is uninteresting, but for some reason MSVC's behaviour is to warn about including this system header. That *is* interesting */ #pragma warning(disable : 4820) #pragma warning(push, 1) #include #pragma warning(pop) #define UBENCH_COLOUR_OUTPUT() (_isatty(_fileno(stdout))) #else #include #define UBENCH_COLOUR_OUTPUT() (isatty(STDOUT_FILENO)) #endif static UBENCH_INLINE ubench_int64_t ubench_ns(void) { #ifdef _MSC_VER ubench_large_integer counter; ubench_large_integer frequency; QueryPerformanceCounter(&counter); QueryPerformanceFrequency(&frequency); return UBENCH_CAST(ubench_int64_t, (counter.QuadPart * 1000000000) / frequency.QuadPart); #elif defined(__linux) struct timespec ts; const clockid_t cid = CLOCK_REALTIME; #if defined(UBENCH_USE_CLOCKGETTIME) clock_gettime(cid, &ts); #else syscall(SYS_clock_gettime, cid, &ts); #endif return UBENCH_CAST(ubench_int64_t, ts.tv_sec) * 1000 * 1000 * 1000 + ts.tv_nsec; #elif __APPLE__ return UBENCH_CAST(ubench_int64_t, mach_absolute_time()); #endif } struct ubench_run_state_s { ubench_int64_t *ns; ubench_int64_t size; ubench_int64_t sample; }; typedef void (*ubench_benchmark_t)(struct ubench_run_state_s *ubs); struct ubench_benchmark_state_s { ubench_benchmark_t func; char *name; }; struct ubench_state_s { struct ubench_benchmark_state_s *benchmarks; size_t benchmarks_length; FILE *output; double confidence; }; /* extern to the global state ubench needs to execute */ UBENCH_EXTERN struct ubench_state_s ubench_state; #if defined(_MSC_VER) #define UBENCH_UNUSED #else #define UBENCH_UNUSED __attribute__((unused)) #endif #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wvariadic-macros" #pragma clang diagnostic ignored "-Wc++98-compat-pedantic" #endif #define UBENCH_PRINTF(...) \ if (ubench_state.output) { \ fprintf(ubench_state.output, __VA_ARGS__); \ } \ printf(__VA_ARGS__) #ifdef __clang__ #pragma clang diagnostic pop #endif #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wvariadic-macros" #pragma clang diagnostic ignored "-Wc++98-compat-pedantic" #endif #ifdef _MSC_VER #define UBENCH_SNPRINTF(BUFFER, N, ...) _snprintf_s(BUFFER, N, N, __VA_ARGS__) #else #define UBENCH_SNPRINTF(...) snprintf(__VA_ARGS__) #endif #ifdef __clang__ #pragma clang diagnostic pop #endif static UBENCH_INLINE int ubench_do_benchmark(struct ubench_run_state_s *ubs) { ubench_int64_t curr_sample = ubs->sample++; ubs->ns[curr_sample] = ubench_ns(); return curr_sample < ubs->size ? 1 : 0; } #define UBENCH_DO_BENCHMARK() while (ubench_do_benchmark(ubench_run_state) > 0) #define UBENCH_EX(SET, NAME) \ UBENCH_EXTERN struct ubench_state_s ubench_state; \ static void ubench_##SET##_##NAME(struct ubench_run_state_s *ubs); \ UBENCH_INITIALIZER(ubench_register_##SET##_##NAME) { \ const size_t index = ubench_state.benchmarks_length++; \ const char *name_part = #SET "." #NAME; \ const size_t name_size = strlen(name_part) + 1; \ char *name = UBENCH_PTR_CAST(char *, malloc(name_size)); \ if (name == NULL) { \ perror("malloc() failed"); \ exit(-1); \ } \ ubench_state.benchmarks = UBENCH_PTR_CAST( \ struct ubench_benchmark_state_s *, \ realloc(UBENCH_PTR_CAST(void *, ubench_state.benchmarks), \ sizeof(struct ubench_benchmark_state_s) * \ ubench_state.benchmarks_length)); \ ubench_state.benchmarks[index].func = &ubench_##SET##_##NAME; \ ubench_state.benchmarks[index].name = name; \ UBENCH_SNPRINTF(name, name_size, "%s", name_part); \ } \ void ubench_##SET##_##NAME(struct ubench_run_state_s *ubench_run_state) #define UBENCH(SET, NAME) \ static void ubench_run_##SET##_##NAME(void); \ UBENCH_EX(SET, NAME) { \ UBENCH_DO_BENCHMARK() { ubench_run_##SET##_##NAME(); } \ } \ void ubench_run_##SET##_##NAME(void) #define UBENCH_F_SETUP(FIXTURE) \ static void ubench_f_setup_##FIXTURE(struct FIXTURE *ubench_fixture) #define UBENCH_F_TEARDOWN(FIXTURE) \ static void ubench_f_teardown_##FIXTURE(struct FIXTURE *ubench_fixture) #define UBENCH_EX_F(FIXTURE, NAME) \ UBENCH_EXTERN struct ubench_state_s ubench_state; \ static void ubench_f_setup_##FIXTURE(struct FIXTURE *); \ static void ubench_f_teardown_##FIXTURE(struct FIXTURE *); \ static void ubench_run_ex_##FIXTURE##_##NAME(struct FIXTURE *, \ struct ubench_run_state_s *); \ static void ubench_f_##FIXTURE##_##NAME( \ struct ubench_run_state_s *ubench_run_state) { \ struct FIXTURE fixture; \ memset(&fixture, 0, sizeof(fixture)); \ ubench_f_setup_##FIXTURE(&fixture); \ ubench_run_ex_##FIXTURE##_##NAME(&fixture, ubench_run_state); \ ubench_f_teardown_##FIXTURE(&fixture); \ } \ UBENCH_INITIALIZER(ubench_register_##FIXTURE##_##NAME) { \ const size_t index = ubench_state.benchmarks_length++; \ const char *name_part = #FIXTURE "." #NAME; \ const size_t name_size = strlen(name_part) + 1; \ char *name = UBENCH_PTR_CAST(char *, malloc(name_size)); \ ubench_state.benchmarks = UBENCH_PTR_CAST( \ struct ubench_benchmark_state_s *, \ realloc(UBENCH_PTR_CAST(void *, ubench_state.benchmarks), \ sizeof(struct ubench_benchmark_state_s) * \ ubench_state.benchmarks_length)); \ ubench_state.benchmarks[index].func = &ubench_f_##FIXTURE##_##NAME; \ ubench_state.benchmarks[index].name = name; \ UBENCH_SNPRINTF(name, name_size, "%s", name_part); \ } \ void ubench_run_ex_##FIXTURE##_##NAME( \ struct FIXTURE *ubench_fixture, \ struct ubench_run_state_s *ubench_run_state) #define UBENCH_F(FIXTURE, NAME) \ static void ubench_run_##FIXTURE##_##NAME(struct FIXTURE *); \ UBENCH_EX_F(FIXTURE, NAME) { \ UBENCH_DO_BENCHMARK() { \ ubench_run_##FIXTURE##_##NAME(ubench_fixture); \ } \ } \ void ubench_run_##FIXTURE##_##NAME(struct FIXTURE *ubench_fixture) static UBENCH_INLINE int ubench_should_filter(const char *filter, const char *benchmark); int ubench_should_filter(const char *filter, const char *benchmark) { if (filter) { const char *filter_cur = filter; const char *benchmark_cur = benchmark; const char *filter_wildcard = UBENCH_NULL; while (('\0' != *filter_cur) && ('\0' != *benchmark_cur)) { if ('*' == *filter_cur) { /* store the position of the wildcard */ filter_wildcard = filter_cur; /* skip the wildcard character */ filter_cur++; while (('\0' != *filter_cur) && ('\0' != *benchmark_cur)) { if ('*' == *filter_cur) { /* we found another wildcard (filter is something like *foo*) so we exit the current loop, and return to the parent loop to handle the wildcard case */ break; } else if (*filter_cur != *benchmark_cur) { /* otherwise our filter didn't match, so reset it */ filter_cur = filter_wildcard; } /* move benchmark along */ benchmark_cur++; /* move filter along */ filter_cur++; } if (('\0' == *filter_cur) && ('\0' == *benchmark_cur)) { return 0; } /* if the benchmarks have been exhausted, we don't have a match! */ if ('\0' == *benchmark_cur) { return 1; } } else { if (*benchmark_cur != *filter_cur) { /* benchmark doesn't match filter */ return 1; } else { /* move our filter and benchmark forward */ benchmark_cur++; filter_cur++; } } } if (('\0' != *filter_cur) || (('\0' != *benchmark_cur) && ((filter == filter_cur) || ('*' != filter_cur[-1])))) { /* we have a mismatch! */ return 1; } } return 0; } static UBENCH_INLINE int ubench_strncmp(const char *a, const char *b, size_t n) { /* strncmp breaks on Wall / Werror on gcc/clang, so we avoid using it */ unsigned i; for (i = 0; i < n; i++) { if (a[i] < b[i]) { return -1; } else if (a[i] > b[i]) { return 1; } } return 0; } static UBENCH_INLINE FILE *ubench_fopen(const char *filename, const char *mode) { #ifdef _MSC_VER FILE *file; if (0 == fopen_s(&file, filename, mode)) { return file; } else { return UBENCH_NULL; } #else return fopen(filename, mode); #endif } static UBENCH_INLINE int ubench_main(int argc, const char *const argv[]); int ubench_main(int argc, const char *const argv[]) { ubench_uint64_t failed = 0; size_t index = 0; size_t *failed_benchmarks = UBENCH_NULL; size_t failed_benchmarks_length = 0; const char *filter = UBENCH_NULL; ubench_uint64_t ran_benchmarks = 0; enum colours { RESET, GREEN, RED }; const int use_colours = UBENCH_COLOUR_OUTPUT(); const char *colours[] = {"\033[0m", "\033[32m", "\033[31m"}; if (!use_colours) { for (index = 0; index < sizeof colours / sizeof colours[0]; index++) { colours[index] = ""; } } /* loop through all arguments looking for our options */ for (index = 1; index < UBENCH_CAST(size_t, argc); index++) { /* Informational switches */ const char help_str[] = "--help"; const char list_str[] = "--list-benchmarks"; /* Benchmark config switches */ const char filter_str[] = "--filter="; const char output_str[] = "--output="; const char confidence_str[] = "--confidence="; if (0 == ubench_strncmp(argv[index], help_str, strlen(help_str))) { printf( "ubench.h - the single file benchmarking solution for C/C++!\n" "Command line Options:\n"); printf( " --help Show this message and exit.\n" " --filter= Filter the benchmarks to run (EG. " "MyBench*.a would run MyBenchmark.a but not MyBenchmark.b).\n" " --list-benchmarks List benchmarks, one per line. " "Output names can be passed to --filter.\n" " --output= Output a CSV file of the " "results.\n" " --confidence= Change the confidence cut-off for " "a " "failed test. Defaults to 2.5%%\n"); goto cleanup; } else if (0 == ubench_strncmp(argv[index], filter_str, strlen(filter_str))) { /* user wants to filter what benchmarks run! */ filter = argv[index] + strlen(filter_str); } else if (0 == ubench_strncmp(argv[index], output_str, strlen(output_str))) { ubench_state.output = ubench_fopen(argv[index] + strlen(output_str), "w+"); } else if (0 == ubench_strncmp(argv[index], list_str, strlen(list_str))) { for (index = 0; index < ubench_state.benchmarks_length; index++) { UBENCH_PRINTF("%s\n", ubench_state.benchmarks[index].name); } /* when printing the benchmark list, don't actually run the benchmarks */ goto cleanup; } else if (0 == ubench_strncmp(argv[index], confidence_str, strlen(confidence_str))) { /* user wants to specify a different confidence */ ubench_state.confidence = atof(argv[index] + strlen(confidence_str)); /* must be between 0 and 100 */ if ((ubench_state.confidence < 0) || (ubench_state.confidence > 100)) { fprintf(stderr, "Confidence must be in the range [0..100] (you " "specified %f)\n", ubench_state.confidence); goto cleanup; } } } for (index = 0; index < ubench_state.benchmarks_length; index++) { if (ubench_should_filter(filter, ubench_state.benchmarks[index].name)) { continue; } ran_benchmarks++; } printf("%s[==========]%s Running %" UBENCH_PRIu64 " benchmarks.\n", colours[GREEN], colours[RESET], UBENCH_CAST(ubench_uint64_t, ran_benchmarks)); if (ubench_state.output) { fprintf(ubench_state.output, "name, mean (ns), stddev (%%), confidence (%%)\n"); } for (index = 0; index < ubench_state.benchmarks_length; index++) { int result = 1; size_t mndex = 0; ubench_int64_t best_avg_ns = 0; double best_deviation = 0; double best_confidence = 101.0; struct ubench_run_state_s ubs; #define UBENCH_MIN_ITERATIONS 10 #define UBENCH_MAX_ITERATIONS 500 ubench_int64_t iterations = 10; const ubench_int64_t max_iterations = UBENCH_MAX_ITERATIONS; const ubench_int64_t min_iterations = UBENCH_MIN_ITERATIONS; /* Add one extra timestamp slot, as we save times between runs and time * after exiting the last one */ ubench_int64_t ns[UBENCH_MAX_ITERATIONS + 1]; #undef UBENCH_MAX_ITERATIONS #undef UBENCH_MIN_ITERATIONS if (ubench_should_filter(filter, ubench_state.benchmarks[index].name)) { continue; } printf("%s[ RUN ]%s %s\n", colours[GREEN], colours[RESET], ubench_state.benchmarks[index].name); ubs.ns = ns; ubs.size = 1; ubs.sample = 0; /* Time once to work out the base number of iterations to use. */ ubench_state.benchmarks[index].func(&ubs); iterations = (100 * 1000 * 1000) / ((ns[1] <= ns[0]) ? 1 : ns[1] - ns[0]); iterations = iterations < min_iterations ? min_iterations : iterations; iterations = iterations > max_iterations ? max_iterations : iterations; for (mndex = 0; (mndex < 100) && (result != 0); mndex++) { ubench_int64_t kndex = 0; ubench_int64_t avg_ns = 0; double deviation = 0; double confidence = 0; iterations = iterations * (UBENCH_CAST(ubench_int64_t, mndex) + 1); iterations = iterations > max_iterations ? max_iterations : iterations; ubs.sample = 0; ubs.size = iterations; ubench_state.benchmarks[index].func(&ubs); /* Calculate benchmark run-times */ for (kndex = 0; kndex < iterations; kndex++) { ns[kndex] = ns[kndex + 1] - ns[kndex]; } for (kndex = 0; kndex < iterations; kndex++) { avg_ns += ns[kndex]; } avg_ns /= iterations; for (kndex = 0; kndex < iterations; kndex++) { const double v = UBENCH_CAST(double, ns[kndex] - avg_ns); deviation += v * v; } deviation = sqrt(deviation / UBENCH_CAST(double, iterations)); /* Confidence is the 99% confidence index - whose magic value is 2.576. */ confidence = 2.576 * deviation / sqrt(UBENCH_CAST(double, iterations)); confidence = (confidence / UBENCH_CAST(double, avg_ns)) * 100.0; deviation = (deviation / UBENCH_CAST(double, avg_ns)) * 100.0; /* If we've found a more confident solution, use that. */ result = confidence > ubench_state.confidence; /* If the deviation beats our previous best, record it. */ if (confidence < best_confidence) { best_avg_ns = avg_ns; best_deviation = deviation; best_confidence = confidence; } } if (result) { printf("confidence interval %f%% exceeds maximum permitted %f%%\n", best_confidence, ubench_state.confidence); } if (ubench_state.output) { fprintf(ubench_state.output, "%s, %" UBENCH_PRId64 ", %f, %f,\n", ubench_state.benchmarks[index].name, best_avg_ns, best_deviation, best_confidence); } { const char *const colour = (0 != result) ? colours[RED] : colours[GREEN]; const char *const status = (0 != result) ? "[ FAILED ]" : "[ OK ]"; const char *unit = "us"; if (0 != result) { const size_t failed_benchmark_index = failed_benchmarks_length++; failed_benchmarks = UBENCH_PTR_CAST( size_t *, realloc(UBENCH_PTR_CAST(void *, failed_benchmarks), sizeof(size_t) * failed_benchmarks_length)); if (failed_benchmarks == NULL) { perror("realloc() failed"); exit(-1); } failed_benchmarks[failed_benchmark_index] = index; failed++; } printf("%s%s%s %s (mean ", colour, status, colours[RESET], ubench_state.benchmarks[index].name); for (mndex = 0; mndex < 2; mndex++) { if (best_avg_ns <= 1000000) { break; } /* If the average is greater than a million, we reduce it and change the unit we report. */ best_avg_ns /= 1000; switch (mndex) { case 0: unit = "ms"; break; case 1: unit = "s"; break; } } printf("%" UBENCH_PRId64 ".%03" UBENCH_PRId64 "%s, confidence interval +- %f%%)\n", best_avg_ns / 1000, best_avg_ns % 1000, unit, best_confidence); } } printf("%s[==========]%s %" UBENCH_PRIu64 " benchmarks ran.\n", colours[GREEN], colours[RESET], ran_benchmarks); printf("%s[ PASSED ]%s %" UBENCH_PRIu64 " benchmarks.\n", colours[GREEN], colours[RESET], ran_benchmarks - failed); if (0 != failed) { printf("%s[ FAILED ]%s %" UBENCH_PRIu64 " benchmarks, listed below:\n", colours[RED], colours[RESET], failed); for (index = 0; index < failed_benchmarks_length; index++) { printf("%s[ FAILED ]%s %s\n", colours[RED], colours[RESET], ubench_state.benchmarks[failed_benchmarks[index]].name); } } cleanup: for (index = 0; index < ubench_state.benchmarks_length; index++) { free(UBENCH_PTR_CAST(void *, ubench_state.benchmarks[index].name)); } free(UBENCH_PTR_CAST(void *, failed_benchmarks)); free(UBENCH_PTR_CAST(void *, ubench_state.benchmarks)); if (ubench_state.output) { fclose(ubench_state.output); } return UBENCH_CAST(int, failed); } UBENCH_C_FUNC UBENCH_NOINLINE void ubench_do_nothing(void *const); #define UBENCH_DO_NOTHING(x) ubench_do_nothing(x) #if defined(_MSC_VER) UBENCH_C_FUNC void _ReadWriteBarrier(void); #define UBENCH_DECLARE_DO_NOTHING() \ void ubench_do_nothing(void *ptr) { \ (void)ptr; \ _ReadWriteBarrier(); \ } #elif defined(__clang__) #define UBENCH_DECLARE_DO_NOTHING() \ void ubench_do_nothing(void *ptr) { \ _Pragma("clang diagnostic push") _Pragma( \ "clang diagnostic ignored \"-Wlanguage-extension-token\""); \ asm volatile("" : : "r,m"(ptr) : "memory"); \ _Pragma("clang diagnostic pop"); \ } #else #define UBENCH_DECLARE_DO_NOTHING() \ void ubench_do_nothing(void *ptr) { \ asm volatile("" : : "r,m"(ptr) : "memory"); \ } #endif /* We need, in exactly one source file, define the global struct that will hold the data we need to run ubench. This macro allows the user to declare the data without having to use the UBENCH_MAIN macro, thus allowing them to write their own main() function. We also use this to define the 'do nothing' method that lets us keep data that the compiler would normally deem is dead for the purposes of timing. */ #define UBENCH_STATE() \ UBENCH_DECLARE_DO_NOTHING() \ struct ubench_state_s ubench_state = {0, 0, 0, 2.5} /* define a main() function to call into ubench.h and start executing benchmarks! A user can optionally not use this macro, and instead define their own main() function and manually call ubench_main. The user must, in exactly one source file, use the UBENCH_STATE macro to declare a global struct variable that ubench requires. */ #define UBENCH_MAIN() \ UBENCH_STATE(); \ int main(int argc, const char *const argv[]) { \ return ubench_main(argc, argv); \ } #endif /* SHEREDOM_UBENCH_H_INCLUDED */ umf/cmake/0000755000000000000000000000000014544400200007604 5ustar umf/cmake/FindCUDA.cmake0000644000000000000000000000227514544400200012131 0ustar # Copyright (C) 2024 Intel Corporation # Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception message(STATUS "Checking for module 'cuda' using find_library()") find_library(CUDA_LIBRARY NAMES libcuda cuda) set(CUDA_LIBRARIES ${CUDA_LIBRARY}) get_filename_component(CUDA_LIB_DIR ${CUDA_LIBRARIES} DIRECTORY) set(CUDA_LIBRARY_DIRS ${CUDA_LIB_DIR}) if(WINDOWS) find_file(CUDA_DLL NAMES "nvcuda.dll") get_filename_component(CUDA_DLL_DIR ${CUDA_DLL} DIRECTORY) set(CUDA_DLL_DIRS ${CUDA_DLL_DIR}) endif() if(CUDA_LIBRARY) message(STATUS " Found cuda using find_library()") message(STATUS " CUDA_LIBRARIES = ${CUDA_LIBRARIES}") message(STATUS " CUDA_INCLUDE_DIRS = ${CUDA_INCLUDE_DIRS}") message(STATUS " CUDA_LIBRARY_DIRS = ${CUDA_LIBRARY_DIRS}") if(WINDOWS) message(STATUS " CUDA_DLL_DIRS = ${CUDA_DLL_DIRS}") endif() else() set(MSG_NOT_FOUND "cuda NOT found (set CMAKE_PREFIX_PATH to point the " "location)") if(CUDA_FIND_REQUIRED) message(FATAL_ERROR ${MSG_NOT_FOUND}) else() message(WARNING ${MSG_NOT_FOUND}) endif() endif() umf/cmake/FindJEMALLOC.cmake0000644000000000000000000000317214544400200012600 0ustar # Copyright (C) 2024-2025 Intel Corporation # Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception message(STATUS "Checking for module 'jemalloc' using find_library()") find_library(JEMALLOC_LIBRARY NAMES libjemalloc jemalloc) set(JEMALLOC_LIBRARIES ${JEMALLOC_LIBRARY}) get_filename_component(JEMALLOC_LIB_DIR ${JEMALLOC_LIBRARIES} DIRECTORY) set(JEMALLOC_LIBRARY_DIRS ${JEMALLOC_LIB_DIR}) find_file(JEMALLOC_HEADER NAMES "jemalloc/jemalloc.h") if(JEMALLOC_HEADER) get_filename_component(JEMALLOC_INCLUDE_DIR_TBB ${JEMALLOC_HEADER} DIRECTORY) get_filename_component(JEMALLOC_INCLUDE_DIR ${JEMALLOC_INCLUDE_DIR_TBB} DIRECTORY) set(JEMALLOC_INCLUDE_DIRS ${JEMALLOC_INCLUDE_DIR}) else() set(MSG_NOT_FOUND " header NOT found " "(set CMAKE_PREFIX_PATH to point the location)") if(JEMALLOC_FIND_REQUIRED) message(FATAL_ERROR ${MSG_NOT_FOUND}) else() message(WARNING ${MSG_NOT_FOUND}) endif() endif() if(WINDOWS) find_file(JEMALLOC_DLL NAMES "bin/jemalloc.dll" "jemalloc.dll") get_filename_component(JEMALLOC_DLL_DIR ${JEMALLOC_DLL} DIRECTORY) set(JEMALLOC_DLL_DIRS ${JEMALLOC_DLL_DIR}) endif() if(JEMALLOC_LIBRARY) message(STATUS " Found jemalloc using find_library()") else() set(MSG_NOT_FOUND "jemalloc NOT found (set CMAKE_PREFIX_PATH to point the location)") if(JEMALLOC_FIND_REQUIRED) message(FATAL_ERROR ${MSG_NOT_FOUND}) else() message(WARNING ${MSG_NOT_FOUND}) endif() endif() umf/cmake/FindLIBHWLOC.cmake0000644000000000000000000000561214544400200012616 0ustar # Copyright (C) 2024-2025 Intel Corporation # Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception function(print_hwloc_dirs) message(STATUS " LIBHWLOC_LIBRARIES = ${LIBHWLOC_LIBRARIES}") message(STATUS " LIBHWLOC_INCLUDE_DIRS = ${LIBHWLOC_INCLUDE_DIRS}") message(STATUS " LIBHWLOC_LIBRARY_DIRS = ${LIBHWLOC_LIBRARY_DIRS}") message(STATUS " LIBHWLOC_API_VERSION = ${LIBHWLOC_API_VERSION}") if(WINDOWS) message(STATUS " LIBHWLOC_DLL_DIRS = ${LIBHWLOC_DLL_DIRS}") endif() endfunction() message(STATUS "Checking for module 'libhwloc' using find_library()") find_library(LIBHWLOC_LIBRARY NAMES ${UMF_HWLOC_NAME}) set(LIBHWLOC_LIBRARIES ${LIBHWLOC_LIBRARY}) get_filename_component(LIBHWLOC_LIB_DIR ${LIBHWLOC_LIBRARIES} DIRECTORY) set(LIBHWLOC_LIBRARY_DIRS ${LIBHWLOC_LIB_DIR}) find_file(LIBHWLOC_HEADER NAMES hwloc.h) get_filename_component(LIBHWLOC_INCLUDE_DIR ${LIBHWLOC_HEADER} DIRECTORY) set(LIBHWLOC_INCLUDE_DIRS ${LIBHWLOC_INCLUDE_DIR}) set(HWLOC_VERSION_CODE " #include #include #include \"hwloc.h\" void main(int argc, char** argv) { unsigned LIBHWLOC_API_PATCH = HWLOC_API_VERSION & 0xFF; unsigned LIBHWLOC_API_MINOR = (HWLOC_API_VERSION >> 8) & 0xFF; unsigned LIBHWLOC_API_MAJOR = (HWLOC_API_VERSION >> 16) & 0xFF; printf(\"%d.%d.%d\", LIBHWLOC_API_MAJOR, LIBHWLOC_API_MINOR, LIBHWLOC_API_PATCH); }") set(HWLOC_VERSION_CODE_FILENAME "hwloc_get_version.c") file(WRITE "${CMAKE_BINARY_DIR}/${HWLOC_VERSION_CODE_FILENAME}" "${HWLOC_VERSION_CODE}") try_run( HWLOC_RUN_RESULT HWLOC_COMPILE_RESULT ${CMAKE_BINARY_DIR} "${CMAKE_BINARY_DIR}/${HWLOC_VERSION_CODE_FILENAME}" CMAKE_FLAGS "-DINCLUDE_DIRECTORIES=${LIBHWLOC_INCLUDE_DIR}" RUN_OUTPUT_VARIABLE LIBHWLOC_API_VERSION) if(WINDOWS) find_file(LIBHWLOC_DLL NAMES "bin/${UMF_HWLOC_NAME}-15.dll" "${UMF_HWLOC_NAME}-15.dll") get_filename_component(LIBHWLOC_DLL_DIR ${LIBHWLOC_DLL} DIRECTORY) set(LIBHWLOC_DLL_DIRS ${LIBHWLOC_DLL_DIR}) endif() if(LIBHWLOC_LIBRARY) message(STATUS " Found libhwloc: ${LIBHWLOC_LIBRARY}") if(LIBHWLOC_FIND_VERSION) if(NOT LIBHWLOC_API_VERSION) print_hwloc_dirs() message(FATAL_ERROR "Failed to retrieve libhwloc version") elseif(NOT LIBHWLOC_API_VERSION VERSION_GREATER_EQUAL LIBHWLOC_FIND_VERSION) print_hwloc_dirs() message( FATAL_ERROR " Required version: ${LIBHWLOC_FIND_VERSION}, found ${LIBHWLOC_API_VERSION}" ) endif() endif() else() set(MSG_NOT_FOUND "libhwloc NOT found in the system (will fetch it from GitHub)") if(LIBHWLOC_FIND_REQUIRED) message(FATAL_ERROR ${MSG_NOT_FOUND}) else() message(WARNING ${MSG_NOT_FOUND}) endif() endif() umf/cmake/FindLIBNUMA.cmake0000644000000000000000000000122714544400200012500 0ustar # Copyright (C) 2024 Intel Corporation # Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception message(STATUS "Checking for module 'libnuma' using find_library()") find_library(LIBNUMA_LIBRARY NAMES libnuma numa) set(LIBNUMA_LIBRARIES ${LIBNUMA_LIBRARY}) if(LIBNUMA_LIBRARY) message(STATUS " Found libnuma using find_library()") else() set(MSG_NOT_FOUND "libnuma NOT found (set CMAKE_PREFIX_PATH to point the location)") if(LIBNUMA_FIND_REQUIRED) message(FATAL_ERROR ${MSG_NOT_FOUND}) else() message(WARNING ${MSG_NOT_FOUND}) endif() endif() umf/cmake/FindTBB.cmake0000644000000000000000000000336214544400200012022 0ustar # Copyright (C) 2024 Intel Corporation # Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception message(STATUS "Checking for module 'tbb' using find_library()") find_library(TBB_LIBRARY NAMES libtbbmalloc tbbmalloc) set(TBB_LIBRARIES ${TBB_LIBRARY}) get_filename_component(TBB_LIB_DIR ${TBB_LIBRARIES} DIRECTORY) set(TBB_LIBRARY_DIRS ${TBB_LIB_DIR}) find_file(TBB_HEADER NAMES "tbb/scalable_allocator.h") if(TBB_HEADER) get_filename_component(TBB_INCLUDE_DIR_TBB ${TBB_HEADER} DIRECTORY) get_filename_component(TBB_INCLUDE_DIR ${TBB_INCLUDE_DIR_TBB} DIRECTORY) set(TBB_INCLUDE_DIRS ${TBB_INCLUDE_DIR}) else() set(MSG_NOT_FOUND " header NOT found (set " "CMAKE_PREFIX_PATH to point the location)") if(TBB_FIND_REQUIRED) message(FATAL_ERROR ${MSG_NOT_FOUND}) else() message(WARNING ${MSG_NOT_FOUND}) endif() endif() if(WINDOWS) find_file(TBB_DLL NAMES "bin/tbbmalloc.dll" "tbbmalloc.dll") get_filename_component(TBB_DLL_DIR ${TBB_DLL} DIRECTORY) set(TBB_DLL_DIRS ${TBB_DLL_DIR}) endif() if(TBB_LIBRARY) message(STATUS " Found tbb using find_library()") message(STATUS " TBB_LIBRARIES = ${TBB_LIBRARIES}") message(STATUS " TBB_INCLUDE_DIRS = ${TBB_INCLUDE_DIRS}") message(STATUS " TBB_LIBRARY_DIRS = ${TBB_LIBRARY_DIRS}") if(WINDOWS) message(STATUS " TBB_DLL_DIRS = ${TBB_DLL_DIRS}") endif() else() set(MSG_NOT_FOUND "tbb NOT found (set CMAKE_PREFIX_PATH to point the " "location)") if(TBB_FIND_REQUIRED) message(FATAL_ERROR ${MSG_NOT_FOUND}) else() message(WARNING ${MSG_NOT_FOUND}) endif() endif() umf/cmake/cmake_uninstall.cmake.in0000644000000000000000000000144514544400200014370 0ustar # From: https://cmake.org/Wiki/CMake_FAQ if(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") message(FATAL_ERROR "Cannot find install manifest: @CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") endif(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) string(REGEX REPLACE "\n" ";" files "${files}") foreach(file ${files}) message(STATUS "Uninstalling $ENV{DESTDIR}${file}") if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") FILE(REMOVE $ENV{DESTDIR}${file}) else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") message(STATUS "File $ENV{DESTDIR}${file} does not exist.") endif(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") endforeach(file) umf/cmake/fix_coverity_issues.patch0000644000000000000000000000075414544400200014740 0ustar diff --git a/hwloc/topology-x86.c b/hwloc/topology-x86.c index 7aabd168f..b01e44557 100644 --- a/hwloc/topology-x86.c +++ b/hwloc/topology-x86.c @@ -1375,6 +1375,9 @@ look_procs(struct hwloc_backend *backend, struct procinfo *infos, unsigned long hwloc_bitmap_t set = NULL; unsigned i; + if(!get_cpubind||!set_cpubind) + return -1; + if (!data->src_cpuiddump_path) { orig_cpuset = hwloc_bitmap_alloc(); if (get_cpubind(topology, orig_cpuset, HWLOC_CPUBIND_STRICT)) { umf/cmake/helpers.cmake0000644000000000000000000004143414544400200012256 0ustar # Copyright (C) 2023-2025 Intel Corporation # Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # # helpers.cmake -- helper functions for top-level CMakeLists.txt # # CMake modules that check whether the C/C++ compiler supports a given flag include(CheckCCompilerFlag) include(CheckCXXCompilerFlag) # This function establishes version variables based on the git describe output. # If there's no git available in the system, the version will be set to "0.0.0". # If git reports only a hash, the version will be set to "0.0.0.git.". # Otherwise we'll use 3-component version: major.minor.patch, just for CMake's # sake. A few extra variables will be set for Win dll metadata. # # Important note: CMake does not support rc or git information. According to # semver rules, 1.5.1-rc1 should be less than 1.5.1, but it seems hard to # achieve such comparison in CMake. So, for CMake's sake we only set 3-component # version in variable "UMF_CMAKE_VERSION", ignoring the rc and git information. # It's only used to set SOVERSION and creating "umf-config.cmake" file. # # For Windows versioning in dll metadata, we use 4-component version plus a few # additional variables. REVISION has to be an integer and is calculated as: # REVISION = rc_no * 1000 + git_commit_no (commits count after the last release) # # For all other usages (beside CMake and Win dll), we use semver aligned version # "UMF_VERSION", which is in line with our tags (e.g. "1.5.0-rc2"). # # Example parsing of git output: # cmake-format: off # +-----------------------+-------+-------+-------+----------+--------+---------+------------+ # | \ CMake:| Major | Minor | Patch | | | | | # +-----------------------+-------+-------+-------+----------+--------+---------+------------+ # | git describe \ Win32:| MAJOR | MINOR | BUILD | REVISION | BUGFIX | PRIVATE | PRERELEASE | # +-----------------------+-------+-------+-------+----------+--------+---------+------------+ # | 1.5.0-rc2-0-gb8f7a32 | 1 | 5 | 0 | 2000 | | | true | # | 1.5.0-rc2 | 1 | 5 | 0 | 2000 | | | true | # | 1.5.0-rc3-6-gb8f7a32 | 1 | 5 | 0 | 3006 | | true | true | # | 1.5.0-0-gb8f7a32 | 1 | 5 | 0 | 0 | | | | # | 1.5.0 | 1 | 5 | 0 | 0 | | | | # | 1.5.0-6-123345678 | 1 | 5 | 0 | 6 | | true | | # | 1.5.2-rc1-0-gb8f7a32 | 1 | 5 | 2 | 1000 | true | | true | # | 1.5.2-rc4-6-gb8f7a32 | 1 | 5 | 2 | 4006 | true | true | true | # | 1.5.2-0-gb8f7a32 | 1 | 5 | 2 | 0 | true | | | # | 1.5.2-6-gb8f7a32 | 1 | 5 | 2 | 6 | true | true | | # | gb8f7a32 | 0 | 0 | 0 | 0 | | true | | # | ? (no git) | 0 | 0 | 0 | 0 | | true | | # +-----------------------+-------+-------+-------+----------+--------+---------+------------+ # cmake-format: on function(set_version_variables) # default values set(UMF_VERSION_PRERELEASE 0 PARENT_SCOPE) set(UMF_VERSION_PRIVATE 1 PARENT_SCOPE) set(UMF_VERSION_BUGFIX 0 PARENT_SCOPE) set(UMF_VERSION_REVISION 0 PARENT_SCOPE) set(UMF_CMAKE_VERSION "0.0.0" PARENT_SCOPE) set(UMF_VERSION "0.0.0" PARENT_SCOPE) execute_process( COMMAND git describe --always OUTPUT_VARIABLE GIT_VERSION WORKING_DIRECTORY ${UMF_CMAKE_SOURCE_DIR} OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET) if(NOT GIT_VERSION) # no git or it reported no version. Use default ver: "0.0.0" return() endif() # v1.5.0 - we're exactly on a tag -> UMF ver: "1.5.0" string(REGEX MATCHALL "\^v([0-9]+\.[0-9]+\.[0-9]+)\$" MATCHES ${GIT_VERSION}) if(MATCHES) set(UMF_VERSION "${CMAKE_MATCH_1}" PARENT_SCOPE) set(UMF_CMAKE_VERSION "${CMAKE_MATCH_1}" PARENT_SCOPE) set(UMF_VERSION_PRIVATE 0 PARENT_SCOPE) return() endif() # v1.5.0-rc1 - we're on a RC tag -> UMF ver: "1.5.0-rc1" string(REGEX MATCHALL "\^v([0-9]+\.[0-9]+\.[0-9]+)-rc([0-9]+)\$" MATCHES ${GIT_VERSION}) if(MATCHES) set(UMF_VERSION "${CMAKE_MATCH_1}-rc${CMAKE_MATCH_2}" PARENT_SCOPE) set(UMF_CMAKE_VERSION "${CMAKE_MATCH_1}" PARENT_SCOPE) math(EXPR revision "${CMAKE_MATCH_2} * 1000") set(UMF_VERSION_REVISION ${revision} PARENT_SCOPE) set(UMF_VERSION_PRERELEASE 1 PARENT_SCOPE) set(UMF_VERSION_PRIVATE 0 PARENT_SCOPE) return() endif() # v1.5.0-dev1 - we're on a development tag -> UMF ver: "1.5.0-dev1" string(REGEX MATCHALL "\^v([0-9]+\.[0-9]+\.[0-9]+)-(dev[0-9]?)\$" MATCHES ${GIT_VERSION}) if(MATCHES) set(UMF_VERSION "${CMAKE_MATCH_1}-${CMAKE_MATCH_2}" PARENT_SCOPE) set(UMF_CMAKE_VERSION "${CMAKE_MATCH_1}" PARENT_SCOPE) set(UMF_VERSION_PRIVATE 0 PARENT_SCOPE) return() endif() # v1.5.0-rc1-19-gb8f7a32 -> UMF ver: "1.5.0-rc1.git19.gb8f7a32" string(REGEX MATCHALL "v([0-9.]*)-rc([0-9]*)-([0-9]*)-([0-9a-g]*)" MATCHES ${GIT_VERSION}) if(MATCHES) set(UMF_VERSION "${CMAKE_MATCH_1}-rc${CMAKE_MATCH_2}.git${CMAKE_MATCH_3}.${CMAKE_MATCH_4}" PARENT_SCOPE) set(UMF_CMAKE_VERSION "${CMAKE_MATCH_1}" PARENT_SCOPE) math(EXPR revision "${CMAKE_MATCH_2} * 1000 + ${CMAKE_MATCH_3}") set(UMF_VERSION_REVISION ${revision} PARENT_SCOPE) set(UMF_VERSION_PRERELEASE 1 PARENT_SCOPE) return() endif() # v1.5.0-dev2-19-gb8f7a32 -> UMF ver: "1.5.0-dev2.git19.gb8f7a32" string(REGEX MATCHALL "v([0-9.]*)-(dev[0-9]?)-([0-9]*)-([0-9a-g]*)" MATCHES ${GIT_VERSION}) if(MATCHES) set(UMF_VERSION "${CMAKE_MATCH_1}-${CMAKE_MATCH_2}.git${CMAKE_MATCH_3}.${CMAKE_MATCH_4}" PARENT_SCOPE) set(UMF_CMAKE_VERSION "${CMAKE_MATCH_1}" PARENT_SCOPE) return() endif() # v1.5.0-19-gb8f7a32 -> UMF ver: "1.5.0-git19.gb8f7a32" string(REGEX MATCHALL "v([0-9.]*)-([0-9]*)-([0-9a-g]*)" MATCHES ${GIT_VERSION}) if(MATCHES) set(UMF_VERSION "${CMAKE_MATCH_1}-git${CMAKE_MATCH_2}.${CMAKE_MATCH_3}" PARENT_SCOPE) set(UMF_CMAKE_VERSION "${CMAKE_MATCH_1}" PARENT_SCOPE) set(UMF_VERSION_REVISION ${CMAKE_MATCH_2} PARENT_SCOPE) return() endif() # no full version is available (e.g. only a hash commit) or a pattern was # not recognized -> UMF ver: "0.0.0.git." set(UMF_VERSION "0.0.0.git.${GIT_VERSION}" PARENT_SCOPE) endfunction() # Sets ${ret} to version of program specified by ${name} in major.minor format function(get_program_version_major_minor name ret) execute_process( COMMAND ${name} --version OUTPUT_VARIABLE cmd_ret ERROR_QUIET) string(REGEX MATCH "([0-9]+)\.([0-9]+)" VERSION "${cmd_ret}") set(${ret} ${VERSION} PARENT_SCOPE) endfunction() # Checks compiler for given ${flag}, stores the output in C_HAS_${flag} and # CXX_HAS_${flag} (if compiler supports C++) function(check_compilers_flag flag) check_c_compiler_flag("${flag}" "C_HAS_${flag}") if(CMAKE_CXX_COMPILE_FEATURES) check_cxx_compiler_flag("${flag}" "CXX_HAS_${flag}") endif() endfunction() function(check_add_target_compile_options target) foreach(option ${ARGN}) check_compilers_flag(${option}) if(C_HAS_${option} AND CXX_HAS_${option}) target_compile_options(${target} PRIVATE ${option}) endif() endforeach() endfunction() function(add_umf_target_compile_options name) check_add_target_compile_options(${name} "-Wno-covered-switch-default") if(NOT MSVC) target_compile_options( ${name} PRIVATE -fPIC -Wall -Wextra -Wformat-security -Wno-cast-qual # TODO: remove this when const qualifier drop # will be solved in CTL $<$:-fdiagnostics-color=auto>) if(CMAKE_BUILD_TYPE STREQUAL "Release") target_compile_definitions(${name} PRIVATE -D_FORTIFY_SOURCE=2) endif() if(UMF_DEVELOPER_MODE) target_compile_options( ${name} PRIVATE -fno-omit-frame-pointer -fstack-protector-strong -Werror) endif() if(UMF_USE_COVERAGE) if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") message( FATAL_ERROR "To use the --coverage flag, the build type must be Debug" ) endif() target_compile_options(${name} PRIVATE --coverage) if(${CMAKE_C_COMPILER} MATCHES "gcc") # Fix for the following error: geninfo: ERROR: Unexpected # negative count '-1' for provider_os_memory.c:1037. Perhaps you # need to compile with '-fprofile-update=atomic target_compile_options(${name} PRIVATE -fprofile-update=atomic -g -O0) endif() endif() elseif(MSVC) target_compile_options( ${name} PRIVATE /MD$<$:d> $<$:/sdl> $<$:/analyze> /DYNAMICBASE /W4 /Gy /GS # disable warning 6326: Potential comparison of a constant # with another constant /wd6326 # disable 4200 warning: nonstandard extension used: # zero-sized array in struct/union /wd4200) if(UMF_DEVELOPER_MODE) target_compile_options(${name} PRIVATE /WX) endif() if(${CMAKE_C_COMPILER_ID} MATCHES "MSVC") target_compile_options( ${name} PRIVATE # below flags are not recognized by Clang /MP $<$:/LTCG> $<$:/NXCOMPAT>) endif() endif() endfunction() function(add_umf_target_link_options name) if(NOT MSVC) if(NOT APPLE) target_link_options(${name} PRIVATE "LINKER:-z,relro,-z,now") if(UMF_USE_COVERAGE) if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") message( FATAL_ERROR "To use the --coverage flag, the build type must be Debug" ) endif() target_link_options(${name} PRIVATE --coverage) endif() endif() elseif(MSVC) target_link_options( ${name} PRIVATE LINKER:/DYNAMICBASE LINKER:/HIGHENTROPYVA $<$:/DEPENDENTLOADFLAG:0x2000> $<$:/DEPENDENTLOADFLAG:0x2000> LINKER:/NXCOMPAT) endif() endfunction() function(add_umf_target_exec_options name) if(MSVC) target_link_options(${name} PRIVATE LINKER:/ALLOWISOLATION) endif() endfunction() function(add_umf_executable) # Parameters: # # * NAME - a name of the executable # * SRCS - source files # * LIBS - libraries to be linked with set(oneValueArgs NAME) set(multiValueArgs SRCS LIBS) cmake_parse_arguments( ARG "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) add_executable(${ARG_NAME} ${ARG_SRCS}) target_link_libraries(${ARG_NAME} PRIVATE ${ARG_LIBS}) add_umf_target_compile_options(${ARG_NAME}) add_umf_target_exec_options(${ARG_NAME}) add_umf_target_link_options(${ARG_NAME}) endfunction() function(add_umf_library) # Parameters: # # * NAME - a name of the library # * TYPE - type of the library (shared or static) if shared library, # LINUX_MAP_FILE and WINDOWS_DEF_FILE must also be specified # * SRCS - source files # * LIBS - libraries to be linked with # * LINUX_MAP_FILE - path to linux linker map (.map) file # * WINDOWS_DEF_FILE - path to windows module-definition (DEF) file set(oneValueArgs NAME TYPE LINUX_MAP_FILE WINDOWS_DEF_FILE) set(multiValueArgs SRCS LIBS) cmake_parse_arguments( ARG "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) add_library(${ARG_NAME} ${ARG_TYPE} ${ARG_SRCS}) string(TOUPPER "${ARG_TYPE}" ARG_TYPE) if(ARG_TYPE STREQUAL "SHARED") if(NOT ARG_LINUX_MAP_FILE OR NOT ARG_WINDOWS_DEF_FILE) message(FATAL_ERROR "LINUX_MAP_FILE or WINDOWS_DEF_FILE " "not specified") endif() if(WINDOWS) target_link_options(${ARG_NAME} PRIVATE LINKER:/DEF:${ARG_WINDOWS_DEF_FILE}) elseif(LINUX) target_link_options(${ARG_NAME} PRIVATE "-Wl,--version-script=${ARG_LINUX_MAP_FILE}") endif() endif() target_link_libraries(${ARG_NAME} PRIVATE ${ARG_LIBS}) target_include_directories( ${ARG_NAME} PRIVATE ${UMF_CMAKE_SOURCE_DIR}/include ${UMF_CMAKE_SOURCE_DIR}/src/utils ${UMF_CMAKE_SOURCE_DIR}/src/base_alloc ${UMF_CMAKE_SOURCE_DIR}/src/coarse) add_umf_target_compile_options(${ARG_NAME}) add_umf_target_link_options(${ARG_NAME}) endfunction() # Add sanitizer ${flag}, if it is supported, for both C and C++ compiler macro(add_sanitizer_flag flag) set(SANITIZER_FLAG "-fsanitize=${flag}") if(NOT MSVC) # Not available on MSVC. set(SANITIZER_ARGS "-fno-sanitize-recover=all") endif() # Save current 'SAVED_CMAKE_REQUIRED_FLAGS' state and temporarily extend it # with '-fsanitize=${flag}'. It is required by CMake to check the compiler # for availability of provided sanitizer ${flag}. set(SAVED_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${SANITIZER_FLAG}") if(${flag} STREQUAL "address") set(check_name "HAS_ASAN") elseif(${flag} STREQUAL "undefined") set(check_name "HAS_UBSAN") elseif(${flag} STREQUAL "thread") set(check_name "HAS_TSAN") elseif(${flag} STREQUAL "memory") set(check_name "HAS_MSAN") endif() # Check C and CXX compilers for a given sanitizer flag. check_c_compiler_flag("${SANITIZER_FLAG}" "C_${check_name}") if(NOT C_${check_name}) message(FATAL_ERROR "sanitizer '${flag}' is not supported " "by the C compiler)") endif() if(CMAKE_CXX_COMPILE_FEATURES) check_cxx_compiler_flag("${SANITIZER_FLAG}" "CXX_${check_name}") if(NOT CXX_${check_name}) message(FATAL_ERROR "sanitizer '${flag}' is not supported by the " "CXX compiler)") endif() endif() add_compile_options("${SANITIZER_FLAG}") # Check C and CXX compilers for sanitizer arguments. if(SANITIZER_ARGS) check_c_compiler_flag("${SANITIZER_ARGS}" "C_HAS_SAN_ARGS") if(NOT C_HAS_SAN_ARGS) message(FATAL_ERROR "sanitizer argument '${SANITIZER_ARGS}' is " "not supported by the C compiler)") endif() if(CMAKE_CXX_COMPILE_FEATURES) check_cxx_compiler_flag("${SANITIZER_ARGS}" "CXX_HAS_SAN_ARGS") if(NOT CXX_HAS_SAN_ARGS) message(FATAL_ERROR "sanitizer argument '${SANITIZER_ARGS}' " "is not supported by the CXX compiler)") endif() endif() add_compile_options("${SANITIZER_ARGS}") endif() # Clang/gcc needs the flag added to the linker. The Microsoft LINK linker # doesn't recognize sanitizer flags and will give a LNK4044 warning. if(NOT MSVC) add_link_options("${SANITIZER_FLAG}") endif() set(CMAKE_REQUIRED_FLAGS ${SAVED_CMAKE_REQUIRED_FLAGS}) endmacro() umf/cmake/umf-config.cmake.in0000644000000000000000000000017614544400200013251 0ustar @PACKAGE_INIT@ include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@-targets.cmake") check_required_components("@PROJECT_NAME@") umf/docs/0000755000000000000000000000000014544400200007454 5ustar umf/docs/README.md0000644000000000000000000000214114544400200010731 0ustar # Documentation To generate HTML documentation, run the `generate_docs.py` script from any sub-directory of the repository (most likely `build`). To display the proper version of UMF in the documentation title, set the `UMF_VERSION` variable before running the script. ```bash cd build $ UMF_VERSION= python ../docs/generate_docs.py ``` Documentation can also be built using the build target 'docs' (see details below). This script will create `./docs_build` sub-directory, where the intermediate and final files will be created. HTML docs will be in the `./docs_build/generated/html` directory. ## make docs To run documentation generation via build target use CMake commands below. To enable this target, python executable (in required version) has to be found in the system. ```bash cmake -B build cmake --build build --target docs ``` ## Requirements Script to generate HTML docs requires: * [Doxygen](http://www.doxygen.nl/) at least v1.9.1 * [Python](https://www.python.org/downloads/) at least v3.8 * and python pip requirements, as defined in `third_party/requirements.txt` umf/docs/assets/0000755000000000000000000000000014544400200010756 5ustar umf/docs/assets/images/0000755000000000000000000000000014544400200012223 5ustar umf/docs/assets/images/intro_architecture.png0000644000000000000000000011700114544400200016626 0ustar PNG  IHDR%v}sRGBgAMA a pHYskIDATx^UsŸ:c1@Qׄ&kE]A1#FPQD#aD1;²ct1TVAŀ:5ܗ߹uu~?ϭTTPĥL:?UI:CJc {gƒ#͎h,LͷjujieLɢ T4N:j/{VRJڲ:HA A <=(LA LA \|4\rIiGy~' > K^syg9jt0?&Ms͓l7sLsg掛^zo6fe4 J>sM7!bj3[\3_|Eiǖ>wTe֬YM~[oV3ynX,f 7{g6G6cƌQ9xzC&MC1bs_~kn~? ̃]\sǚ||?`x s)$H%qF}t.C=dqZ 0]tQK/5'N4Ç7/yG=́Jӓzo>xzwߙ:˼fw4cǎ!]{5 0,k5 %:*l 2dH(ol38:X]g̘aӼJAGaZv6tS3l0;EONHstjI'm[omN8jiөS'Wfm#4(liGn̄ L޽,ygrK; .*Hz[9wb-{myl`膴P+lMw}Gxrk9# ֖ ~*nkܹ_:#,(Q{)S*;u8 mGͼU['7(R>5T uо+%,I[q&lbkR#V[m|uQN\ξRr^\<&loMmU2o*%y7|cVXa?m5/"QT=rHE=2%4vyg3x`Qbֈ Ĭ*ce]-[}a͙\b[N\=n\a@(&ӉuJ'r'n4l=h|5#^7a2e L lYp+rW^-]Uz9tlyt!ՔRP7O,&Jnw~__\}Æ|;^9A;fh|; E7t]o*ٿet8SXRJz:Fծ>F?} i<[^ j{4'q9ޢLW9u)WrNƝCkjU^~}t5M2r*v 1iVԔ|g믿MW huA@ۍ6:@+ 8כ_f6?1 ?xFUJ0T yk{غtb3#ʤܿAQ+]7p {):O=T|[[-:L&xBs=s\\bNnf[k_Dtpvٳmc9Mˬ'hoLS3k߰>T]Bwԩ%.8F)v؆oZG_Շ4-ԸJ (x;y~w?= m=h} 8t o:_|}NT@^*U}%6U/rSl?U4L;S۲.J9昼̭NEʄ%5[*MIHEn}~ R7Ȥ*5W^y%c\(KBB5O:ayve\')}]eUM'BŔ7q]:,w \SJv]Ktt׺jcߣs(-OMHT{i9JIG#׆*@\NE%;. GR|qi/n3T[B_R pս]-W;J$ُI:&*Ӡ5JCTS(Q&\% ጻ=ոB7RI5q1keU=.Q%IUW]5򸋎*бWMyPSɼz)>$7f돚GDnjRU9QZw&}1k$Y^̢^T9 J:oXi]w{`V:r/fB5,apnI&)8CjE\uUퟳ ~aRuG ??A4Gm@<ҸŔPrN*izL*qOhbDKw詳n=rZJ wڜv*NI8Hk3R89< 8\tYR7A _Z3 A A *AIͷqb&h(hnb܁r&и4rTG#(AMp1qi$-=/QP-% .fCP S%2EP S%2EP S%@Y^|4\rIi<=_-:sas̱OG:F@P2N|4O>Ħ5k/t1z̆nZwufϞmFmƌc"\B# (I'>>hK;ߚzN_^tEyK/L8 >,GϤ*E:F@P2N|Ի̘1=s6VbM75Æ ӓ'ONQH@:8(JRĉF0}t[[lf޼y6Vj 6S3Ə>((HAI8Q~}Y8vSNf뭷{ꩧ_ kS Kh$%)G#x7?o^{m:϶~{ӹsg /sʥ`;… ` Js=` YR9s$$7k,҉"\B"('>>h<%$f=P)P}S KhT-ftY.`ͩ}S[nEN:DyC=#vd-}!C >m~ݿ{0?.FLcǶ!F;jfFioj5f:5tj7RVGd![7n0e#ժVG֣G`ʘٳgS@}itZm3fL0e) }JE+AI4_4ӼS1Jd@Jd@Jd@Jd@Jd@Jd@Jd@Jd@Jd@Jd@Jd@Jd@Jd@Jd |5i'=zS̞=;QF &߿93w@vֶf6>l0e[oLvmͿ=f֬Y6cƌ 1oO-26;w>CPR+whw^{5sk.C砄[(8Cw{$ȂJwU|Mq#nBX@i iQwRQS$Ԓ KV=1im $pOԒ4>jJ4$Ԓ KKrjIrtGP 1ckCsRnk&_KTA HJ[%A=t7^ZD)tPKZ (+ZJwՖP8qi4Z (+ZJwEԒsZ A VjIP(ݍ-ap!͠V@Q)<jȐ!G ޵jFQ{dʂ71 6m Zb~+0~ur)*Qu '? $[;%2b0VH3%EmJ_-;mÔM~kK$V>麛]j@"ՎPKa0$1$S2 w^{2Yf,ea.-Wǫ E^>%GJBX&Le8,e&^f-zͪ;:@  cԔ̓PS S%2EP S%2EP S%2EP S%2EPxǴhJ>|'sϵ^O>dq-\?˹ϙ3'7!bΝ@"(nT2 nV3y͍7h^}U:]wt%/h.%GaZvn /$jZ{7xhj:u2z@=Wӹsg?/4ޝwikI\rI/@h1h sZ G1g,3|XL4vX/mfm}[5\pw{G6/]n=zӧYfega2Tn:L6|]; Q8I[`?~|4hfm]we^(\`{1<9餓rqi-~)SL -o{՜E毯cm4iRn=;r5W{bˠbv{^_Ҝ}6m=@s͌۫%T'Ã{W3fL0e)4y{lfM6 >}e(;< ז[nioLTgRf2}mR8-gzw[[[SO=e>#zٛڻロ?6/.e]vY;->۹ꪫ̮֩i57qek}Lm9ۯmk-/|6jii1f66]۴a]7pC5mq~ssLRYVs<`*SԔ*tTɗn, +r5%ʄL ]L/R[\w 3ϴbe:,J\i?o1 СC>;Ov*#u9C mK+p-[GJY^jK=v+ o[58Ds%MᴐWO~bFKR1tۭJA]^o+"o9P\Sҥ;qs}um8_5CLlFy>۷o_nWe_;lF=.\[j냣2qJvDW)H9,+Ԕ>%*e^^J655хkatM͑Gi3Z*-v4dvb"dMۣ0]2E8m)qNٳg}촂Jyr__8Du kSs2U[mnQI18$=ڶoٞ_ pD[mZJW+-@6`{e!]]Մ,f)+_~?JUҮ礨F_Mt7 wtw=~Gph\Zh3qܶyU Z0X6MU0֫׀lmy6_~yROnѶD ]Wܼk%Ǥ}@PR.^TkmVXa[~)s.@$*+s\TɊe\ePuqV.L/QUn{~H\&47G ? g&cgy&xW[5o9cM*F]vŖ\jqIZ{G۴zx\fܵ?DXo]5֣I~HݸB']GeW"檝Q?`E }GW![~9,e+/U;>.s)]uR( jkO%RID>跻{$n?FC5{N0RjbCtR -\sL\ x;qN"|둣s.)JuHѴ{*r][𮽨Z^GוOwn3q5l:nj(7*(arYA .`j.PG]aUǽ7|>NRS_JƶcJU7v^wum^ {a.# 4#pU()}?+tpUj;^ݕV? 2]!jʩuR!~Wjeu}F5i~5uO$}@PT.q6~UIQluAE~JY Ф/EpǡP+Œr]R* Ss9\tE#zTVO5TF)h֮aLedH e]\2M,[w(uQsT.K7eBi}^Հ__Ak.UC"cR>Cc (A]RSUjFҒ(i8DqLuXjv.QU2b5(uI[9vPɚjLR[1Lm*ֵ-@|Qɺ$4_mumXsE畆^W5Q42gqF^rMN{#*cR}l.֍Dյr7NhV ;GFSK+ÝlRq_|qM9Ǯ+WӍ1-oƌvi6NJu-cXlTZtF-ֿ7:~vMDVzR!tt_馛Fr#I Q\i*[*hӨ]}K߯Jr!%[*UBW]jcReP,]iÍC-}W/M 2[~難R8ׅNغiDUa+ ZRqPJUk_ixG+6#^feUᚁ6qٚ2ņU6oR@ H4#rn jZ}t->qAK!~uVu.䘤Pߨ)AEMIi\{`~mH |(Ք8|͹R(j¨R5%  v7i^ۥ'ZF>}75%Ng9%arYV))5%Qɔ뿠$(# ]v Ms_We|U˫j}ٳ?~|O}W-H̵Zƀ򞗢g&L(Bi2~UH$}GM H5%(OjJCM LA LA @e]ĉM@Jd@Jd@Jd@ZZֱ}ѣG0eٳdZM *I)b驒{b!Qix#.4n`*S%hd"&T,=2(P{%| @JЪo)A @| Կ C9̝;72<=N=TۅwIqL7qW79s^O :g%)O?l_׫jn-b3T\rI έ*f]w.]hV$eݻw7'N.|ʔ 8о_ʥQszFP S-ftN>T#2fT2-S͟}:u6m/gk96lcZZZ̐!C'|bN:\mgviv /]I ͋/|ҶܝvlfV >mSly??[oϖ\rI}t~[FQLnȑ#rvAۧO2D}IL˧;֬'I; R1:c?/I&W{mz|X4U2K/dμ߳;:(r_$.w^jJOSz~#]wy=Jػwo>NZi2ɾ(܋S䞈E@ڢOf^-T)0sLw}{k1gqywO;rh~4h3f'QGen U~OSN9e:nQA{YgfTɱ-v s=) >sۭW7ѣG 8)-Wyԟ{5W]us=i{t1ZqgU%{m2e^yv:)eJ>l{3q_d޼yy=#wg~뭷mV-Ij^;Z9QPꫯKz4[tTbD2n_/OAP8uɓ_|a IcJ7=| lS7nmjJdm+lzT@S3Is?(5*V!DΝUk*XqХepFSsP%ӧ݌97.쒻Y%6|.ԾYv-xWV)d[Kd3 vav{U&=RF^1AMAP|o]KO"e*ë'pGQ裏5&ZC='O<*SdZAMv+P0(CU?>(M5dJ{tUilfEEu֌ ͕-"6pp\&nZve`_ϟovDR1lذ(JHCe o!VlJqV[~rMA0r5ְu㞑+[=Q4d׿Z{trkRzgKr7FSO<_Q6IFmuI(q Sfc[1LG֫?|3Jk'*Q>mS׭jl <]v]>=QiHJi{t4_KJߛ7<$F#{kbUmB'iƲٕsl+=jzF9z믿nW,7|M=?)6z-FVsO@J~iZfE馛nEŶ4JJzoF;ʑht$u/:Se&Q,g oOKL~eAIԶo-,nH/6SOٿAe A:RONÙxjt4oj7S)&e= qYuU կd?5zHq*>trRk%mmGG$P3 Cֳ۬Y6Ӹ~|駉g)sZTӢb#_s!.sOIҺ$AQP4GR=@xQZƍ{ӨKy-}qcqCk*g`-W7)Sۯi`rLo=COWM댬gxHVG/8xW\T'k7|$j? Jj[=)QiJ{!(Gi5d:&k$=B ؎Iź%J$cFR7ƭnjbPlHUq*\emV*6W+RLMqFWɱ[1Լj#:z2cz2dz? 2ӦMRe]w?L=ꫯMu4(sJEs0Iۡ2ZOrf|D=jB*{ |5i'=zSme`j4YkfE/;lnxnXʤnhn |efQFmȐ!y79IgB__\GY&n6cǎM&4P=2C Dc֧6 4˔J駅N8fL-'IvqǂËSOZ!ɹ]lݢ+-]&㶧SιD9ǿ{"i>q{S$&QnƗ]vYə={2Cjt8@*nص~J!/R4Jm޽(]E)t }Z ~ &Df( Ikn94YɶItۀi= &WιjJ\ˎJ)=q7r?%B@ڢOԔ"%uLD88AIR *mMs9uTGi7%9JFH5'N4sOGP[@Jd@Jd@Jd@Jd.\ho93wS|)ͅ@Jd@Jd@JG1x9Sͷ~k~G3sLsi҈1cƌ1}Q0W{T:?i i"=AImֶmtN:>ږFiC=ǘӧO>Ė{fe o]v1쳏q{StA?~0`}k~ &37|ږQq|7{t~DzjJ/9r$7*%ۤHOi*S"( -DM S1c|@3!(Șj,Á/?j8lJ2.6(xẄ́hP$~h$%Uҋ'.U44Z4+:QZ4+:QZ43:WB- A @-͎΄kE%Ak1h sZ G1gzD~-#4@HOHS9ǿ{b-D&T_,oVXhDr<Le:/v 43:Λ/~ 4i iб|8jJd lW45cMIϗ5%TʽJ@VJd!o i"=!M| h4*5%2EP S%2EP S%[oe}ټ/^{-4/0a5jT7ooZ!b:w+le1sL@veF y)i`CTB%Ij@o8 קy3dȐ]<{:H)Ѿw֭9r-it .4voͅ^h_?SRjJēO>iƌ7ujDYr%͉'[ҩS'f5%EMIy)i*QKz/ ՖPK>G!Zim/5* ^ <8*,BG] W6$Qr՗禛n'py̱k?3w\Yqv4B%B\! ZSyjI2GuS~<SN2q11bl^ƣ>j>P&>N*:_~e1B6|m6k=l jOLJP\i#( P*OqZ4gsqLFRpUW]e6xc=W^yS@ꫯ~zn 0/͜+U6Dt_A_s5M~r>ί.[[z6lc'O݌3ΑDPҀJ)DB- *k;S2|͠+w݌?lzAOk*]jX.b;/Rӷonin~({ T /{7m@`d=>Ei2c ʑ^vwe^ ;ˌW2uP`c$e[Ol{i);dH))=]ܱcǚW\1O4}"{-bLl7lfСv:2V nےK7tI6e=zy#O#SRNfÇ9Cu6ѩVY :k~jzo`h/yl)aÂi2U̟bO/SO|osIYK#h yjP|lF|6.l!{ok1w?T;>!_Ywu:묓>w~ +7x|Wfw4---o~wnCMXn_WEkCm%X^3csw֢^m;GSVs\:('4eSn[+l[n9;nk2{4Wr+?l֋+ؠ*Cj[m%N_Z>az[jna,3 YeOYmwڂf9FQoͩ&GqGC5rS#G޾omjUoSߓRWaE}?j?LzA4uPuԠLqAoU QAIM˝?QAF:zBPRoDԩڵ~ꩧB>3BGb6}OV]uUOs|.twgyl& AJ"(D}=u=Ojm< P-.ß6*]̚5+#NQ4Lhy} ͋ 믿>jv冼o"K5ZUowՐHs|.ƈdB6`;!/qSO,X`3"}p\}v*eլi̙3ΰU30bĈ"mp*]\yvZOߟ_CGVBhK5'yWb~Sf)5$ɐ*ju5%塦uCbw#Iw\x' >Oim[oL<9tNx6 Q]wնvWbQnT䡇 E4+ *Y}ׂOKS[⪉YP2]K\w5m4s'D ,Θ1N!(A]PSCUp\m%EM#c8#̵^[͉e;n6sǷ{p CbͱOn#ni): ԅ5X+XDDOXtճFN"uįg}Nc;2:|o߾v?]itAO0],D/(n*o ѣ8*V;O#w{w}v=zӧOdղ֯-Qzk{,7:+@s&nycuLץ}߷GTl%N#87Mn?[5ʾى'oy饗-cQF.s^x~^=X֏?n>_,.NmO?|ƭߟǷ[wkm]iOl_Y &w ~}bQ#Jx;<|\Q>ӶSιSN9%/M裏K΁m:.+M+ϗm "QSڒE[r1W%j6Uj;\';]u+g%<$>i5NFyꩧ_o۩ffz;Za촶Sfqqq>U{|j!OUJҷrK(hΝM &w o([/;Q*ps5Ɣֵϋ7hh?isbAWۧe~G@s!(AQ've͛g?6*V35KҸ+B! RCE=A˒]ڿUVY.q/w}/p/nYf5r+6{G2Lr&$蕲^͓fHJ3˸kHb Q9=qSdhmg%|JPw9VY\%*I-OD I7;aZjkO܋q_uU#3ht&eZ].-~W6@(hg㶯&uvRT3}T"oPmx83!I?2qU{͇u{SJ^]ur.:#eoo˸bu{K/uxW&e~]sBASHi5 :&A|MwihsP:-!QN(!K7xc(&OiswQSRҨ6lk ( )5mP{8d-߬w9ܓ rJYuHC;&azһ:蠃r#I =%[+͐$霫ծ)j(~ Րd.6v8V5< FJ.nQT[BJM꿢Tz}s|nN# .@kNplx51MAMd\?=?A\j墋.s_}',eLRk8\m SL)eyyvm6#PGs߼zٌF`Wy.0qe>qopt|5zҡ/IzsG- ꛂ~].liUoYUfF7qƙs=f(H'q%j^z͈yʈ 4(Q;L85eFs*FL8޼9>f2վ wD%=,SS{)z|uzT~&W}8uHʯAQ's)oaOG/M) ($A(ESJsӞCHA ꪫono<?yWM=lXYi|~> {wJ:˧N I۳g=I[ő#G\Sov޵R;kn=Yi̲.|sG @>hBu=Wߠгk|v=ziZOx!`41AQHxw}stK.AsFsi5@Z:O0U=8(LMfTBڅR3jjRA5;vlMFr-S"P  SͫjPհz?CiG9teRKԶq{Sh *ܐtpS|jbzEP47T%LSN-D$4dh_&K^{s#(4-Z4*Ո#rͶQ)R-bN=\( Q.뮻>5_xgͫ`fʔ)vz ꧡ}PY߱к)!/Ÿk]tpo~-ftN>T#j֨ZM 'bzp̧km|Zjq.EԽ޶q…'k(:d Eբe*ciz衶H,X#Qmsn4ljܼ@iСvZ϶Q?޼}x0e]"\^(h{5״54}bm;~^#GO7obu$ TKdu^-TJ*DPon4 6i]bFPR:?XHed(QA&CO5/.-{Ȑ!|W5XCuYpp$wܱ Jm½ǨOFj5%塦ue\rIs_߾((Lnȑ#m>s:\WK0)jJzܸqO HP[riKO@3"(Aڿ/B&X%{wԗUS42t(j:wl~s}ƻ;m-l_ ?^@##(1k,4^|vg5O>(>>l`.:3f馛HKs-]BGwt?餓6lcFm՟aʔ)9 vtԩS͋/hz-ۚ߬:3_3и5ϡjze}יּ6pݻ}'j^}ڵoo~3m4?ݾxww '|j[i(vZSI(B(PO%ח_~P'l{zT kh^eb_}UO?~Gs=w >mqgO%X:\+a wV8@ɓOAՌ3*9#g~뭷m mhnߩV_~Z&)\NW+v_i~^y裏6/==Pcx ۴Keex]F~w6W\qmZ6qlZǍ̷Җ_}ͦnj*-r嫉[~8(OAҰyi^7OAM\&Wkܺr&RQ|\F|=>mO_j' mO<H7?  $WD4T@䷿mݽJK?8dwFҥK g4/r19 6qR JA9_yjN|GO?c-e]b)6~[SO=e6,+(mm9(ibzPAJf6(ʂ柟읶QX}=P1iQF[E 5G?P?ӧZ5Xv)+[fPbv v>]ShI~k.bۖ: ]ET@R4@=V~X'n}̧km|F GiTIe ϟoՄ3,[w(XvewVYe,>T l$>g֋-Wl|׎I(WmZWci̽k町 HF|Z)IfAڜgaי6 i]b?h4َwi֯N+#|)S*^oo_-ĕܫY:dK~:iS-y;6zOSTCfA:5 s?lKZ F$X :qD/f8&lbku_Ky(Tj:QDAIkfȍ =@HKzQ]ei!swJ>WJ̻@gߗECjo6u|]?CIhY"F:P:\2naS͟$>`CWIiee֬Yve=ܓO&W~KW=I]OOw8-K4.0Q᯳@P`IѼ#3 L2i^h.)q).RPPIAyJid/8x .zJs’&HAI 5!A^G ?HIԴirϧu3S}`v#f&lQv54-tp׾m ,( S<d@EFLku XF?5S0ntSN91 L=M6 z!Cl鶆U=NCw7p-q [-ʔ+Jߵ}}w3/+:~o92mkFSޯ=:o`eTaj&CӅ(Pp5XQI)DPw檫&wP/7'pBfǞ={ǛDnW\Qr5Rns0\z饱BRZ~ d543&L(\b(({ --ftN>i45Z,~H0J#>J#2f-zcLeUF(@3f, A M>ݎ `Op@4>k+} JJC6L8@Jd@Jd@Jd@ZZֱ})2hj0@:#=zS̞=;E[Le"(4USO%@1yr/@}"(j@4:5BgD {hfo@Jd@Jd@Jd@Jd4&L`p|5* K%@1q!Ν; [fe1d2F@6J9J9OEPT# ;I) UBF/%@1Sܢ0A PEdl:( CPT# ;QPJ*#cd#<0A PedPJ cd?( EP# ;:\sM 9cS(e` (ߛ9OOQ ZOˮ܇T=)cfϞL"j 2EM P#K,el  F H#"(j@{%2EP SttPT! ͈tJ; ()"()"()"()I .4v/I'dn?̳>k?z+퉱{キիW wߙ!CO>$Yo ,0=yki6tSuY'4I&E.;kv|45Q RDw4w37pC^Jt=z;p 7ξΝ|FLk>.{6XQOGsM7c=6vO<9S8"p>@CPG1\suYr%͡j:,sUW38l{<_|aߣͧ~j~aR a̙O6} RZs=/oBewq62R L&*@'xJ`̙c:;ݭ[73asA-¬Zf29w[sؿ@R s駟n\Kic1Ǐ7ofy睶ET2:#ycǎVatpjf62˘s9ҳgO[,3=1ݻw7'Nf lT~iK/`N:>} F5 L;v5Z>j8ӻwo;GAE^tlOtp*U(XqttN?S/hԴՐ1"qve fUS>>5;rUH xԼБtpDX# K>NϚ5hU9ͩj3qQ;~zr!f̘1棏> /㫯2wq߿nި&/ZϴiڭGMiϟ|k1TO}Vh~lo^'pK)[F,]kfڌR 6lv:9&Χ8tpke]N2*q(Ri5SG#RSePg_oԨQTeh:vi=n#ew^xh.S9HBGv¤ԡ82~:Ks=%Rj3|/leUV5g̈́M>ӫjl_0? K@ <؎r1թk䖧 +- SɥWS7W^Wҭhj2ʘiӼjf;2qI3=EJ_~m>|&6&5/C/޼ߥq$o.#z뭗kƨK3W\qm.̹{v> AI kzQ) ~i.u*^}KFIFkTN;ʨELԩX 7kNoyI&5)wfruS]쳏Jn㚐M)x;gK\TEAY2rK  M~ 5͆5[6Q8m&FzX]2.z]\Fr鹡X }o]ve8&O˰qyW$%n-F6)-C OG/WҲ:ë]#p DSS!iш̈́5:ҥK[r)QM>ZmՂwf I28Wp}|e$JXg̘av[6l#5- BڵJ<.(7{fBJZ%$͆3h.=N~_ߋS327$଼f喳~AWpo+aM!m)82Ԍ3,[w2AGh&r{ ZTR W" AIk]JJc7|M8 _AWɤr&ʴ_:.R({Tj_eE+12i|js%t]fRK!J(~UPأ@,I\Icehp3F*ּdM-f"Pf:} )pOm8~tpʬ2B⨄vNRilRGFR5IyWb)Ru3Y I;6MZN Ź+FV)S~wxǂwhR||fCPMG alSvJ'I.㐔2YS,F(Sµ=4@߬pT(~s$K)IRG}FR<3ы/N+{inϫrJt͆M:y|DO@v?ntRq( Ӻ|6cKti?#%ݎAWۭLԦ:Dƍ&? s(T*3hxRK;X}dQFU#kIjT,CBAN!OOL:-L ;Yu:@U2?mg[U3$/=]+Qe?vkkylG C =99ITB\e ն]`#kfe□z0Xl3wf+gKV5 @)shYl)[oe?Ӄ~_0w,0?+Oj_MN6]vefԩXJ2~iT}3_r_vXZ|I|[wus^]yR*5Eұf52:61+h%lͧ&^:ӦMKs}饗l-3p>5}ϯD_~f„ R3w(#L~Je&T:D!7J]G}Ȑ!TۖL]_~9R$u$e8s3 }C=Ԧ$Ҡҳv)riḮ QUX=#{XSH8T#ԔhTÏ>Nr!o9T#DFD2 j^Ojo݀^W9ԔePsKkի r\(?4C܈Nx]弚A Qm+)CJoݎҗjV4O)?x3qĒ_͈dfE5,▁JPU%G}pշ^{ uQ `VZi܃A ѳqu۷}*JCPx4$W5%1Na}4JCPAK-VӬ!o`ʘ>87JCP暹ZψOC!fH"JCPDPm:?DJՎT}{)FGPDSL1}VJ9w}s-gs7 p^jΥD5)q !(@;*_fGuT0]Ͷ!]Rp/լ 9cS(e` @9Jp>zL3{`(/;LolKu3g &_N=ܓ8N;d}\r%/nZe:IU-q{Suk5Y`A0U:dvjIR-  4UStCZkLoSQ/_^)V[m#2pJд:StCꁞRKBM`aÆ 6 2 A} (@Srt,N/ ׫5:̡jܳDzijQJTqW Qm&QAyWr}tIϦHO p#zjKϟoTv>dKLQ0#2*&csJQ}JJдP)JAGwAP S%2EP S%2EP S%2EP S%2EP S%2EP S%2b>LMS` @RC@HbW5g.6*Sl7f.5%!ʹz1[^+Rmޣsiq.]sB.7 |iBy։ DO4.ɜs7ʥͧ`Gbn ؀DF߮_~I[IT[4$M 2.3 ۯe {^\^ H!ޣ&dk72?h BUTcwWmI ]%B}j14%%Q^Ht+e t8ož׫`L-S d  37h7 ;c&L2Ip5%ka Պ=鏳?o|oJU@Dt23GPAM:m+ qkͧ :'m¥jӶibGͧ*]~ߌ=W ob_%3zFHp-֧w.Ԅ˯%::wW=VͫJ/&\T1m`* 5r~~qR W^QV@fX}eN[~MQ֋DP7(FהD5ra=O9t_g-l僩;ؿߪ^4p\ֱ}*z%ς~~s#ouS5Q⯳,-Rz^_$\KTn4($uYN;h[tGR.?3. v\]R .)"6ڒj "]FZԿDrQSiI|ۙ[%cGi*9<%@r%%LA LA LÔi1)P!qCH!Ay4274-Jyp͂"()"()"()"()"()"()"()"()"()"(/zGkk믃y_}EFmtbKK$3gI4PO8O3T)aGLRZsqJ;ԕr/o5Ԣlfzy!԰#@& (]AI5%aGL\r7@JRGGw"()"():礁@rtt*rK=6^l2K.a~m%5_|C~l>ooWN|.:?Ү/@:vdjI:qnfj|ҋ MZt]wɻ>7<+t^_ٿh0%@Y|\ #k4kaIzoEgx`nPJRǎLC3I.trv /[f,ifD7Ͽ\2-3jڛ'S)t3|_ )"(JmlrTx4WJ>n{ |tM$u44YPZ?5tS~ڞrT"8lUiJm6k~+4e"(S[>0批kejkƵ٢u Fo(FqՌk~/s=},gkm[q/EP:vd((эhn2naK% J& 꺒]&- uK5Y +xgNW( ȸZ)5 qtA ,U#hRLaZ֖8*;+0H{uKrC. @P/!HAF)T*2Y5R)ߑS}\7_+>UZχ/Gq/S%hI3v_όK۴Cۨ!; t-ηZͯ8#Zn+SP%4̯[My0GWyjth~\5,2nilzs Iiev?ɧN\QGQõ T\(f!v]IPk?v``_՜5FPK;V nfYtcG |} )^7r)= r`7p??QUQL@uhs'\{Q)89Q c3^pP %&=oZCT/ڇ_SMԏfic s44q~ivLG9 YUC0}089kqT X?ȉN&^FjJ&i! CP˿޸Hˤz(:klRMA2j6\_QPXn=G>iN㜼xlvfMt1fW=JD]ՄKCF뚥jꩦ RT:s6XIr(%I%AO7eLE XMJE?0(c_;j{B߇Gn2 ~[0'[밞o>@ÿ.A4,F]O0D\ ZV[2j\DHí3 9r5m~Q. X<E<.ӛ/ڰKM`f $*P3Q5UmjUykSD.S vfW+&i [5lO)\wӍ77ܲ:ӹW3R)v)}<`*?o.?X|tkϿ!J ơ5TO) VKcH3ƿٲTiozlR~`ͷPR*5bM|n\n %?#N;k.~3t,Ŀ.>=`r*RZ՘σwPA ,?(AS9~ !,n}Zׁ]'ܱ\;uxW;$/(CWJei? zҪmв뢿I_0@1%6JkJy!>j mwD&O764tb4x.jпizfT47X~_/pgUKuީj  m5~KRO9fs=M$~7_AJAPݤCQ#pkd*QiT{F0LA7~rd]3?v?H:c'O9 ea%7*Εr+C^]y9`B&?(s;/>8r}4i]G5%z, ]5T%e@sл~ᎆ^2q]b4hƌ%D9GPpm2Һ1KC݈jQKD)IlI_L,hn9%)\ 4yP bOtP~yUk ;cyvyuYˋU.]SGh]69of} xZF}ZރjSi?UT+/.A2O<西9⢛y%i#)TZI*֏D7Q%ji'j^k}'$ B6[!|]rsU<>kS˻|;TEׅoq5Sؗ(VܺP@ͯ{ڡy\_̵ۡ2YfB%bi7@e HҠՓG$\kTqGVsgmg$ ? JC4&]t (mFw뢮; vJ Hd_v*>/5 H.5t+&* *B