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}sRGB®ÎégAMA±� üa pHYsÁÁ¸‘kí�–IDATx^íݸUž÷ñsŸ:c”1è@ÅQׄº&ŒèkE]AÄ1#FPQD#‚aD1;‹Â²¢Œct1�ˆTVAÅ€:†�5ëÜ—ß¹ušÓu«º«»«»ºû~?ÏÓÏ­îÛºêTÕùŸT²ÔüPÄ¥÷íÖL¢ƒ:í€?UýšI:C–ÒJã¤c {µ¸g¥©â�僬Õê¤#­ƒ ÍŽ h�”,ü€L”ÈÍ·Ðð²j¾ujßiÁšÕe÷ïLµÉ¢ùé ÕT­4N:j/‹{VšRJ¸ð Ú²:éHëA šA Ð<=(¡ù€L”ÈA €L”ÈA ��üÑ\|ñÅæÀ4—\rIði¼GyÄ~÷É'Ÿ >‰þ ›K…^çœsŽyæ™g‚9j�t‹0?Ý&MsæÌÉÍ“äŠléž7sæLsæ™g掛^zÿý÷›o¿ý6øfe¸¾4¶º J>ùäsÓM7™þýûçë!‡b®¾új3þüà[ù\Æî°Ã3_|ñEðiÇ–æ>á䮥wÝTeÖ¬YM�~õ[o½õV3yòäà“Ån¹åÒX,¹ä’fà 7Ì{­»îºö³gÏ6£G�6cÆŒ±ïQ9Òxz®»îº¢×CÝç&Mš¼C½Óù1bÄsá…š×_Ý~¦k’èýí·ßnó~�?þ¸ý¬º ¹Ìƒ]\î¸ãsì±Çš|Ð|ùå—ö†ªè?ü`žxâ sÊ)§Øà$­Hí%½qF}�“±t.­‹ÒõC=d§›‘Îq¸¯½öZð ªí€0]tQÞëÒK/5'N4Ç7Ë/¿¼yôÑGÍ=÷ÜÌ�J�ÆÓ“äzøÆo˜çŸ>x‡zöÝwß™³Î:˼úê«fÇw4cÇŽµ×!]“ô÷Þ{ï5 0Ë,³Œ¹æšköšÄ5 ™%:ˆ*±»ï¾ûl 2dÈ›H•(ýèÆolƒ“3Î8ƒ‘:ÄÉX]¤g̘aÓ¼J‡ä¹çžë�A÷Ga®½öZ³ÝvÛŸ Ú6ÝtS3lØ0;­‚®©ÕEONûHs¡t©û�jIüë'ê×ôéÓmÁåÖ[omN8á³Új«ÿiÓ©S'³ûî»›ßÿþ÷æW¿ú•Ùf›m‚ÿ #Ê4(¹í¶ÛliG·nÝÌ„ LïÞ½ƒÿ,¦èyç�g¶ÜrKóÎ;ï˜ .¸€“*Hzãä[9w‘Þb‹-ÌÞ{ïmÓö¼yól`ÒÑ膴úê«ïP+l°�MwÊà}ôÑGÁ§¨Òxrk¬±†9òÈ# Ö–¨‰�ò º~*£¾½ûî»öï¶ÛnkÿÆéܹ³í_²Î:럠#Ê,(Q{ú)S¦Ø*;u¼Ôß8º¨« —mGͼU[Ò'7ØÊ(øì³ÏÚR>5±ÑþT ’<õÔSöoô¨�–šÚ¹—Î3öq¢æQ³É¨yô;¦M›fN;í´ÜwU n¬æ~Çwœmª¦ßí¾ëšÿê·”ô7h[ü~T/¿ü²9÷ÜssóDm⥕vŠÍSL’ôèû7ß|3ïûá4¥å©PËýßõYŒÛFÒx6¶ß~{{¯W йs矶Q-³šû¸ëg!I�·¿o?ýôSÛœuоòÊ+ƒ%,–õúËI[qçÐ&›lbç�kRçš#«ÅËV[m|º˜îu¢ßQNà\ξŒRÎr’^‡’\Ò<&ÍlÉàoMùmêU›”2o*%yÿý÷Í7ß|cVXa…à?m5/ê‡"îÀ¹QT=räH›Eß=ûì³í´2†ú¾%4½vÞyg3xð`ûQbÖˆ ¢Ä¬*ceÀõ]-[}aÂÍ™\býÎõÖ[ÏN»\µ=ûï¿n›œ¨\a@ê(&þïÓ‰®ÏýíuJÙ'år'£nÜâŸ4þÅl�=öh|»˜5#ŒûÇ^7aµ£�2eŠ L Ýl‹Y¸p¡¹âŠ+ré¯W¯^ÁŒ-]UzÐ9¤tìÒl¡yt!Õÿ”þÕ”RÇP7OÝ,•îý&—J§êÐø—¿üÅì¶Ûnöw~øáæ_ÿõ_í\}Æ|;^9¿Aôý;ï¼Ófh|;ú õE7t]o*Ù¿ÍÂeîtî¹à8í´SèXRJzôï:ŸF�Õ®ù‡®ƒú¾>×Íßÿ¿ÛF?} i<[î^ï jô{´Ï4 ŠŽ³î'qÊ9Þ¢L¾Ž¹Ž‹›ÇíW„úéÁ¥å”9uÇ)ëõWrNÆ�CÊéþ¤ùÃó¸k‰¾•ŸÐöjU§üŠ^âç~þóŸ›}÷Ý×tíÚ5ø¤M¹û2¬œå”r*v ¨Æ1iV™Ô”|öÙgæë¯¿¶MWÂ�ž Ñ÷•huÃðÛAë@ë ê¤ÐÛ�6£ƒìÚë»ÒÍë:Éé@+¡ 8ÐÎýõ×›_ÿú×f³Í6³ß?1ë» Ž?þxÛF¥UJ0úT ‚þ§yk{”غtéb·3®š‚#�àʤ»Ü¿AºQ+ôÒöª„]7Èp {)û¤:O=õT|¨’[�ÿ[µ-:ñÂÜÅÌÿ­ÍL&•xé·êBæÛsÏ=s\\Ïb”Nn¾ùf[k¥´â_øDëtpvµ�ÅæéÙ³§mã­céÎ9MÿáÈˬ‰Òâ‰'žho®ÊL”£œßàSô3k¢ß°Ï>ûT´]ÍBûwêÔ©öš%.8®FÚ)v¬¢”“ÕØ†oÜZž»ÞGý_ÛÕ‡�4ž-Ô¸ý¬ûœö�J‹µÏ µ®(÷x;áyÜ~íw?=¸´ ®ém=¬¿’´µÍâ–ãÖãh} 8t ‰ •oÓ:•_Ú|óÍí}NT@ê^*àU}å%Ü6Uº/�r–Sl?º…Uë˜4«L;º«SÛ²Ë.¼«ŒJüŽ9昼̭NE¥Ê„»%5[ˆ*MÒI®H×Eª•žåœàn}~ R¹7Ȥû¤š*¹˜5›W^y%ö¦ªc\(€KBž‚èB5O®ƒ³:ªéa’yvÙe\')}]e•UìM'îÙBÅ”ó�7qíÌ]:,w»� \SJÿ¥v]K´¯tƒt׺j¥�¸c§Üô¨ß£s(Ì-OMHT«òÁ´{i9J³IGÀ#�׆Û*@ûÏÿüÏ\�N¡ûE%Ç;. ¹æGáR|qiÁ©—õ—›¶¢Ö/nžð3µÜúT�[¨¦B”_Rÿ èªpÕ½ÔÄ]-Wô;üÂÒJö¥¯œå$Ù�Iï‹Õ:&Í*Ó Ä5ÃJCTS(Q&\% ጻ¦‹=Õ¸’“¢’¼”ê÷B7ÈR÷I5¤q1k®ƒ¦ÄeU=.Q¥%I¨“¤‚žUW]5ò¸‹Ž½*бWÍM’yÂPªSøÉ¼z)×ò>þøãšý†$Â7ïf§ë�šGD½úõë×n´Ãj¥�R�U9éQ´ž¨Zw·©&ÿ·¦}1k$®Y^¡Ì¢^’´T9 J‹:oÔX¿i×]wÍ{©ó`V:â<ŽJÓ]SÊð«oß¾ßh�J)Õþ¾ÐKÃ�k¿�ÆëG÷îÝsÍ— •4‡•r¼«!ëõWƒ�ÅÀº‚¶RŽK!Z¶Žw8_˜Ö¾lÆcÒŒ2 J\Ç^¥”Ô»Œ]¥Í¾Tí¦vªêX¥6ŽÊ º‘.¢F³¨Fbv�À]©¸NDלÉ&êù™Tµ/fõNÇÐ5ËS§Å¨Œ¢{)ÈÔy¡«¥r%¥…Jpµ-ªás5yIæñ¹ÑÃhª¯”úcù¯ƒ:¨¢ÒÚr~*W­´Sê±*5=ã–'ꀪZ踗ëKH¯/jîª{¬ ‹ÝcË9Þiª—õW#m¹|›+4sù1Ýà }'ɹ¬íò �ÓÚ—å,'É~LªšÇ¤eÖ|˵MÚ±WY,„›%逪qQ31ñÛd*“¬þ jã¨Ì ªïºë.›h]_‹´NŠ(JxêCàNpw# ÷7(÷YÎ>©–r/fÍB¿Õ5,ÖaÍpáænI¸’&Õì)Œ’8É<ªN¾ñÆí±sé&®†ËuF-W9¿•«VÚ)õX•š‹I²<]+u¾9¤ñú¢–ºÇ¦•~ÂÇ;M�°þJÒ–òm®ÐLÍßuÿ.ÖÇGågœQô|Õ(¢ú¾+tNk_–³œ$ó$½Uû˜4›Ì‚$UË*rÔ¨ …2`J0—_~¹M°Êœ‡‡L+T²¬yU¡T¶B”yöÇšN뤈ãNp׿%j˽A¦µOÒRêŬ™¸c×ÇÇç8�…F‰¢&×[Ã.†ûL©© ÆÁ×q×xíÚ–$óhð=AÛäšýià‚p©�†Îvýb¢$)u*ç7 rÕJ;¥«RÓc1Å–§{Šj¢U0åž9@o\åï4Õûú+M[®�¨ò:T[¨��òPºŸ)ã®ó6êiÊ�è¡�jEPÊõ%é¾,g9IöcÜu(| ¨ö1i6™<§ÄQ“'UY)B0`€4hP»¦tÀþøÇ?Ú(Z¤j¢è$Ñзêgáªûm·Ýf—¯`F�ÇUã¢QgvÜqǼž›¡D¤êC—˜4¿“žÉájxDßW¤¬‡ñø#Ù$¥g—è¤ÕÍïóÏ?·B¸ƒ»ƒ ×,»A&Ý'iq'cÔIå_ÌDëî(Ü•¦´O õññix`�4£@Xé/jÆQ­&`òUZuŽTé‹jÍô,]øtžùÇ=É�Cj¦üEó\uÕU±ó¸ôퟳ ¤~øaûRu»†G ??A4ÏÑGm›@ê<Ò¸ôÅ”óP¹rÓNÚÇ*izL*¼<¥YÝW”1 §[Òxã+åxWC=¬¿ZiË51V¦:iS#m�†þUá³k�¢sDùeÐ݈€QçuZû²œå„ç)t*v ¨æ1i&-Áß²]zßn­Á¤ujßiÁTi”qSg`=Ð�œ•Q�zÒ§¨”L�‚jSÆŽk«]­Š(±ëÆ¢êC¿´Yó½ôÒKö»~ÂQâü÷ÿ÷ÈMýøñãm­ˆõýð6ˈª]¢ %â¸ïk™Q#p¹}ãÖ­'¡*ZÿÉO~bŸÐ®}VÎ>qO­×hbîDKú™èw詳n=áÿ‹«¥rÛZJ €ï²ûw¦ÚœvÀŸ*NÇI¤•ÖÑ8²Hk¤3ÔRµÒ8é¸9¹<€ 8«\¡tYå�ÒR7A š_Z3‚Ô A šA ’*µÐµ×èAI¦Í·Ðqèb¦&hªé(Ühn”¨bÜ�r” &¸˜и4rTGêŠÚ#(AMp1 qi$-=/�Q¢P-%¨ .fˆCP S%2EP S%2EP S%@¤‡Y^|ñÅæÀ4—\rIði<=�_ß-ô:çœsìÐÏasæÌ±ÿO²ÀG:ÒÁ¹„F@P’2N|4‚O>ùĦ™5k–ùâ‹/ìt1zÖ̆n˜÷ZwÝuíÿfÏžmF�mÆŒcß•"�éà\B# (I'>Áƒ>h¾üòK;ýí·ßš‡zÈN£‡_^tÑEy¯K/½ÔLœ8Ñ >Ü,¿üòæÑGµÏ¤*E:ÒÁ¹„F@P’2N|Ô»ï¾ûÎ̘1ÃÂýû÷·Ÿ=÷Üs6½VbÓM75Æ ³Ó“'ON�QH§@:8—Ð(JRĉ�F0}út[£·Å[˜½÷ÞÛl¹å–fÞ¼y6­Vjƒ 6°ËS3Æ�>ú(ø(éHçAIŠ8ñQï”~ž}öY8«v®S§Nfë­·¶ÿ{ê©§ì_ k¤S œKh$%)áÄG#xã�7ÌóÏ?oÖ^{m³Î:ëØÏ¶ß~{Ó¹sgó /˜¹sçÚÏÊ¥ó`þüùÁ; <¤S œKh$%)áÄG#p£¸í¿ÿþf™e–±ÓK/½´Ùf›mÌ?ü`Ój%\m¡¥"�éà\B#!(I '>êÝÂ… í` JŸê§äÛsÏ=í` åöYRÐ�‚æ9sæ$$Á�7kÖ,Ò‰š"�éà\B£"(©'>Áƒ>hû<%$Á�§f†=ôPð)P}¤S œKhT-fà­ÁtY.Ýíª`ªÍ©}§SÍï–[n±ýEN:é¤DÐÝ÷Õyý�C±Ÿ=òÈ#vd-ÿ³¤�}öÙö!�C‡ >m~—Ý¿{0Õæ´?ý.˜ª®FLëêó¤þþ÷¿›±cǶˆ!ŠF“;í´ÓÌj«­fFŽioj5�f‘Ö:â5•tš�j¥ñŽœ7ÈçRÇVöù¤Gd!³æ[7Ÿõn0eÌÑ#צ€ÒÕªùVGÖ£G�`ʘٳgS@}iätZÍm3fL0eÌàÁƒƒ) ×üæÕÔ}JÊE‚ç+AI4Î_”Š4Ó¼èS1JdŠ @¦JdŠ @¦JdŠ @¦JdŠ @¦JdŠ @¦JdŠ @¦JdŠ @¦JdŠ @¦JdŠ @¦JdŠ @¦JdŠ @¦JdŠ @¦JdŠ @¦JdªÅ | 5˜ÎiÛ'˜ªž=zSÆÌž=;˜�µQ£F™ &ïŠëß¿¿9ãŒ3‚w@vù¾’Ö¶ßÿýfòäÉÁ»6Ï>ûl0eÌÖ[oLµÙvÛmÍ¿ÿû¿ïÐ=ùä“fÖ¬YÁ»6cÆŒ ¦Œ1»îº«ùöÛoƒOâ-³Ì2æá‡6�;w>²CPRÚù+÷ÝwŸÙh£�‚wèˆ^{í5sÀæškÌ.»ì¼C£©ç „æ[ò(À8ä�C‚w…é{$È‚JwUšë¿|áÿM�q#nB¥œ¿ÊX�@i i�QÊw�RQS �$¥­Ô’ KÍVº›æ=1im µ$p’žOÔ’4>jJ4”$¥­Ô’ K”îÆKrþj�ÀIrŽt´óµGP Ò1ÇckC¢èsýÈR¸nœ¤ßk&…Î_éˆû…K¤TA €H…J[©%A= t7^¡ó—ZD)t®PK‚Z (+ª´•ZÔJwãÅÕ–Pâ�8qiƒ4ƒZ (+ª´•ZÔJwãE�¿Ô’ �¨s†ZÔ A €‚üÒVjIP�(Ý�®-aŸ ˜p!Í V@Qî)ï<½õjÈ�!æ‘G Þµ•îjÈÛFQ­{¢†ÿìòæd³Ê‚éæï�71 6þmð ÞZ¯Þb~òÉ+æ›Ö0ó~urð)*Q‹¼u  ¡©´õ'?ù µ$¨[”îÆûÛÚ;›Ö%–2Ÿüb·à 0—VH3¨%‚E©mú­·ÞJ_Ô-¿Ý;màóý°ôOÌ‚M~kK½�$”V>麛­]j… @"ÊèõÌÕŽPKÒÞÿ­¼a0$ó1µ$¨±†îS2ü¿æšÌ ÞwÞ^ë›á{¯¼‹Æ2Yf,“eÆa™Ù.óÿ-ýWóǫΠÞE«Çß^Í>%²GŸ’âJBX&ËL‚e²Ì8,“e&^f-ƒ’zÉͪžÏ;:º@ † cԔ̓š’òPS S%2EP S%2EP S%2EP S%2EP‚ªxä‘GÌ�hî¹çžà“ÂJý>Ð|òÉ'æÜsϵç†^O>ùdð q-\¸Ðôïß?—®Ë¹îÏ™3'7ÿ!‡bæÎ�ü@£"(ÐÔn¹å–T2ô n½õV3yòäà“êúñÇÍ�7Þh^}õU³Î:ë˜]wÝÕtéÒ%ø/ÐÝh.%šÚGa®½öZ³ÝvÛŸ¨n¸¡ýû /$j‚¥Z’{ï½7x ™”hj�:u2«¯¾zð@=ù×ýWÓ¹sgóÃ?˜ûî»/ø4Þ�wÞikI–\rIÛ/@óh1h ¦sZÇö ¦ª§G�Á”1³gϦÐ,ÔÌåºë®3|°í„XL¡ïë4vìXóâ‹/Ú÷ºm»í¶fÿý÷·míÃÔìåÏþ³}½õÖ[ö³5×\ÓpÀ¦wïÞö½è{£G�6/¿ü²]þ÷ßßn=‡z¨éÓ§�Yf™eìgaþ2Tÿàƒæn¬þ:õ½»ï¾ÛL›6Í|ùå——]îö¿óÎ;æ�ü£íƒ Qó8I×á[°`�?~|Þþ4h�Ùf›mÌ]wÝeû…^˜×Ä(é±óË\`{ì1»<9餓rµ¥¦qiË-§œý¦~)S¦L Þ-æo›¤õ{ÕœEÛ毯Ôc¨m›4iRn=²ãŽ;Æî»rö5ÊW­{bË ©ÁÔbµ¸Ç¢Žî§�vš½†Ó{øœòùóé^ñË_þÒœ}öÙ6m†¯=@–êñ¼s¢¶ÍŒÛ«%˜ÊT'³åáÃéœá{WÿÄ3fL0eÌàÁƒƒ)4‹yóæ™çž{Îl¼ñÆf“M6 >�÷}e(Ï;ï<óá‡Ú ×–[nioLªæ×ÿTîg�”‘Rf2Ÿ}ö™m R8-ÿÙgŸµÿßzë­íw[[[ÍSO=e>úè#³Þzëٛڻロ›çã�?6/½ô’Ý.e¼—]vY;ŸÏ-ãƒ>°Ìÿþïÿ¶Û¹êª«Ú̮֩i57Ðöêûºqºek}áLm9Û¯m»æškì�›ú-Ë/¿|ðÍ6áíjii1³fÍÊÛ6ý6]Û´Þðüaú]7ÜpCÞúµ5Ÿûmq¢~s±ýê”sLœR÷YVêñ¼s¢¶Í<çˆ`*SÔ” *tñTÉ—n,Êð£ �+ýr5%Ê„ªL™•žù™ ]L/½ôR[å¯Ò\wÓ íÌ3Ï´ýüÒbeÒÎ:ë,óùçŸçJÔ\iõóÏ?o¿£›Ú1Ç“«¹ðÿ¯ ÞСCíç>ÿ;šO’Ýv*#¨u9C† Ém“ÛýõK+Ùþp-“[¿Ž�›GJY‡¸ã^ޏãàjÜÿK=v…ö£¸ÿ+ oƒ¶[58ÊÜD•°º´èös%ûMÎá´�öïµ�WšùÉO~bFŽ™K—RÊ1tÛ­ JA]¯^½‚o·ýÖ+®¸"oÛÊ9ïP¹Ž\S¢óÒ¥;qŸùüÿ»sÖ}um—¾µž8áó_Ü5CƒLl´ÑFyµ‹>ÍÛ·o_»nWÓ¦e„Ï_¹ãŽ;lF¼�ð=Éß.è\Œ[¯ö‰j²ý냣ë„2õqóJÜv‹üâDíW)÷˜H9û,+Ô””‡>%¨*e^^ýõ¢¯ðJ™655Ñ…õ¸ãŽk—aÛtÓMÍ‘Gi3Z*-v4dêþð‡vb•"«dMÛ£’ý0]€Ã2õE8ýôÓm)q’N˜áíìÙ³§ÙgŸ}ì´‚›ÜöˆJyœr·_Ë_ÄÝúÃó”²ÿ8DÝôu† ¼kSî±s¢æÑ2U³Å[´Ûm·n�QI1¥ì·8Õø½Å$=†Ú¶›o¾Ùž_ pü€Dô[µmZžJW+ý-@¹6Ø`{­e¼ý!‚]º]«Õ„°,ºf)í+_~ùå¶?ŠJÖUÒ®ÂÑû¸ç¤¨öF‰›_Mt‡ž7¯ ”¹×wt¿Ðw´ü=÷ÜÓ~GËpÛîhÛ\æZ™hÍ3qâÄܶ¹yU ¤Z‘0X¨6MëU0ëÖ«×€lm�®ª…®yÝ6«´_¿~¹yõRóO‰ÚnѶ»€D× ]WܼÅök%ǤÒ}†Æ@P‚ªR‰‡.Å^ÊùT’£‹ÐÚk¯mVXa[­~)sª‹¯.@Ê$³Ê*«Øï+s¦›\TÉŠe\‹ePuqVÆ.LÛ/QUøn{’ŠÛ~­ÛÝHÂ\&4ê7G ¯Ã?á ªãg&¤’c·ƒgžy&xW™´ö[5o9ücè¶M¹­¶Ú*øF¾]vÙÅ–\j«qÞIèZ{ôÑGÛ´zßxã�\ÍfܵÚç?ÇDX¥o]ƒ5ŸÖ£¦IçŸ~®ÐH™Ý¸B']ÿG�eçWÓ"æª�Q€¨?—–«`E× }GËW!—[~ø9,î¦e+¸Ð<â¶MóªYÓ±ÇYØòÞ{ïÙŒ½î¯jVëÖ«×î»ïnkŽã¼¥—^Ú\h¿¨¦E5=n^½?üðÜý8¼Ýn¿jݺ¦L˜0ÁŒ¸y í×J�I¥û �� uéÓO?µ>•Öüñ‘/Uñë;>•¨©�𶏧ýº—.´ú¾úsø™)]u£‰£þR(ƒª ãj«­¼kO%RI”³ý…D>¥¬Ãõ‹‰Ëè·»„”{ì$n?êFª°‚C5{ò·ù„N0÷ߪäRÆjüÞb’CµÑ×t¡ãç«ä·•R€® «¸ ±Ÿ™-\ûÜsL\ xœÃ;Ìq…N®à"|î¸ë‘£s.ª)£JïuÑùªšHÑ´‚{‰*¬r´]»í¶[𮽨Z^Gו¸Oºwïn3öq×õñÑú5¯šl:n¿j¿(ó7¿š•*(Úa‡r…Y•“´öêA êš.`ªj.ôÒPG]¸”aUǽ7ß|Ó>Ûí´ÓNÁRëS¹Û_Jƶ–û¨”c—„JôU¶ï¾ûšÍ7ßÜvî^wÝum©áí·ß^òžË â¤ý{ãÔâÖê·a.#¯ ±¨˜4#ìèüpU(€)ô}?¸ˆªù+t�p…U…j;Õ^�Ý•ÁV? Ñ2]!ŽjÊ©ùuÁR!~€Wj­¦¶Ñeþu}¿F5£õi~5ëu�O¥Ç$�}†Æ@P‚ºä—T«�©.‚q¯Í6ÛÌ~ÏUñ«IíQõlÿuÐAE–~ë‚éJ±¢¨´Y •Ф¡Üí/E©ëpÇ¡PíŒöŸ+Å’rŽ]R* SŸ†sÎ9Ç\tÑE¶ù�†#ÖzTªV¬ßO5Tó÷F)åºÑh’Ö®Õú·aÊLëedÕüH”æ e„]�\­¶2°ªM,ôºõÖ[íw£(ðˆuQüs¥T.ðŠªùÕK×7e¼ãÎÙBÍi}ú^Õ€¨_†ö�ž÷â¯_A�k.çøûUCƒ—"�cRé>Cc (A]RæSUÌjF¢Ò’(ºÐi8DÇqLuXŒjv¢‹œ.”Q´ÝU2¥›b5•»ý¥(uIŽƒßÞ[Ê9v•PÉšjL²Rëß[Ê1L²m*Á½ñÆíÖµþ-@×|ÈQɺî$4Üì×_m§um�XŹsE畆•­^ÆW5¾Q4úš2ÞgœqF^ŸŽrù¿MµNçž{®í£ç#ùû ¿–*�cRë}†l” .©ºÖ�°¢DÂÕµº°ê¤Òr7ºˆ«N×hVáÒ ¥õð;G�‡FñçSæKã¨+Ã�´”®•lR¥®£ØqÐðŽ_|qð®M9Ç®µ+WÓ�ð1-oÆŒv½i6ÇŠ®u¨Æï-¤”cXlÛTZª‘tôF-«Ö¿ˆ¢‚7:¡ì·ß~‰‡ŸvM¦DýŸ¢V‰zéÚRË!®t©t_ë¾é¦›òF°r#I©  �Qî\��îi*€ÐÈ[*hÓ¨]úÝá}¡­üÁKÄ߯¥Jë˜ÔrŸ!%¨[*±UBW]«Íjc¬ReP,¨]­iÃ�¥C-}W/M« €2[~難R8µÃ×…NÕØºèiDUa+ƒ§öõþ ²ZÊÝþR”³ŽðqPçJU±k_ixGêËà+õØ£’6•”é©#¤Ž‘¿Ù^»’Žf•eÎUƒáš�ª6ÁýqÁ‚¶Ùš2×ņþU¿¯«¯¾Ú6µÒoÓïR@ ÎÞ H4ÿˆ#rn® ªjZÌù}Æt-×>qAKÔà!áýªí×~uVuÛî.«ä˜¤¹ÏPߨ)A‡EMIi´Ÿ\{`ÝÔÞ~ûmÛH •|(Žš’ÒÅÕ”8ÊôÞ|ó͹Rÿ(j¨¾Rþµ«Ú5% Ô ²Ðvé7i^ßøÛ¥'¯ZFÜ>ñ÷}7Ÿ®õÊäû5%N’ýªg¤è9%aå“r÷YV¨))5%Qɔ뿠¡$�(#¥þ —]v €º¢¦Mꥒs¿ä_™We|UË«�j}íêÙ³§?~|®ŸžO}•¡W-H¡Ìµ®½ZÆ€òž—¢ùûõëg&L˜(©ÆBóiŸ”2Ÿ¯Ø~Uð�H¹Ç$�}†úGM ëH5%(O’ÔjJÊCM €L”ÈA €L”ÈA @�Ûe—]ÌĉíM‹ @¦JdŠ @¦JdŠ @¦JdŠ @¦ZÌÀZƒéœÖ±}‚©êéÑ£G0eÌìÙ³ƒ©dZM ¦€Å*I·¤)„‘ž�¦bé©’{b!Qi±÷x #«çó.òþ4n¯–`*S%hd"‘&ÒÒT,=Õ2(P{%ÅÑ| @¦JêЪoÿ)÷šA @ê<ÿO¹Ð욦OI½´ÕCm¤�HSé i*çøÓ§Qº?qj0eÌœ/ ¦ÐˆêåyM £;A Ê—v Mul¤'¤©œã_­ ��t�´E]Ÿèè‹”ÈA €L”ÈA €L”ÈA €L”ÈA €L””é»ï¾3Ç{¬9ðÀÍ“O>| Ô¿… šþýû›C9ÄÌ�;7ø´2�<òˆ=N=õTóí·ßŸ–¦Û…ÆwìIÐq”¤L7ÑqãÆÙW7Ñ9sæØÌ^ÚÁOÚÛ Õ:ç¨g%)ûôÓOÍÃ?l_Êø×«jnç-·Üb3T—\rIð � ¤Î­²Ê*f×]wµ¯.]ºŸhVœó€Žˆ $eÝ»w7'N´¯í¶Û.ø´|Ê” 8оÖ_ýàÓÊ¥½�ÒQ­s€zFP S-fà­ÁtNëØ>ÁTõôèÑ#˜2föìÙÁT2-ƒ¦S‹¥½ÍŸ}ö™™:uª™6mšùòË/ígk®¹¦9à€Ì6ÛlcZZZÌ�!CÌ'Ÿ|bN:é¤\mƒúgœvÚiv¢ /¼°]Iç‚ ÌøñãÍ‹/¾|Ò¶Ü�vÚÉl»í¶fµÕV >mSlyòã�?š?ÿùÏöõÖ[oÙÏ–\rI»¼}÷Ý×tíÚÕ~æ‹[®FQºîºëL·nÝÌÈ‘#írî¾ûîvûAÛÛ§O³Ì2ËØÏD}I¦L™¼Ë§ï�;Ö¬¸âŠÁ'•I; ¤±¼R�­¯Ð1ÜÿýÍ:ë¬c?‹òòË/›I“&å­Wç×Þ{ïmzõê|²X’4Uê2£ÒÍK/½dî¼óμ߳Ã;˜ƒ:(r_$Ù.w^j»JÝO…ÔSzªÖ~#]wýã£y=ôÐÜñ�J“îØ»woû>NZi2ɾ(çúç«äÜ‹SÎñ¯äžˆæEº@Ú¢®OfÜ^-ÁT¦¨)‰0sæLó»ßýÎÜwß}¹Œ¸¼÷Þ{æšk®1gœq†y÷ÝwƒO“»ãŽ;ìrýµh¹·ß~»4h�3fŒ½'¥›öQGen¸á†Ü U~øáóÄO˜SN9¥äe:nÙQûAÛ{ÖYg•µÜfTɱ-v ôÞsÏ=Á§‹) >÷ÜsÍðáÃÛ­W7¯Ñ£G—ýôÓí 8)-W™Ùyç�íòÔŸãÞ{ï5W]u•ÙsÏ=íÿ”i{î¹çìt1ZæqÇg·U%{Êì¹mÕ2µ­¢e^yå•v:)eJÎ>ûl{3ßqÇÍå—_ž·dÞ¼yyË=âˆ#ìwöÙgû~ë­·Îm�¾—V-I½©äØj^•;†Z–Ÿ9ÒñQPøê«¯¶K§z©4[tŒTb�DËÔ2Î<óL» •ðén_/½ôÒæÄO´AP8�êuøá‡çŽýäÉ“Í_|a§ Ic™J7ú=|°¹à‚ lS7ÿî»ïn›·m¹å–ö»jJ”d»ü´®m»âŠ+lzT“@·�ãöS3Is?(­Ž5*ï¼V!DçÎ�íÿUk å*XqÇÐ¥e·üp¨FšŒSéõ¯’sP%žéÓ§ÛÚÝŒÏ9çû7Ê.»ì’»Y%¡6Ë|ð��.Ô¾Y‚ÝvÛ-xW˜ÛV•ð)³›d[Kͨd3ª ·vØav{Uê­À¤£ªäØ&=†ª�RF^ý1üöíAMAPÜ|Ûo¿½]¯‚KµñO"�e*ë'pGQæùè£�¶5&ZÆC=ü'žÛOÊ<*S§ÙÓdZûAéMçvø+¨P0é(CU»©Ú?¥ý�>ú(ø´M5Òd”J¯•ž{€ô”tƒUišl¶ÙfE›EuÖŒã× ¨ÄÍ•¬—ËßÖ-¶Ø"6pp\&¢”Œ€nÖZve`Ôá_æÏŸoÿvDåÛRŽ¡Ö1lذ‚™½(šÏJªÁHC±eº o!òV¥é¢ælJ—qüý´ÕV[üý~¦ºØrMšûAÇ0®ær�5Ö°uãž‘²òÊ+›å–[Î=ê“QŠ4Òd¥×¿Zœ{€Ò”t£r™k�ºRÌzë­gKü’r7FÝÈÕÙSO<÷_ª™Q†6IFªÔmu™Iš(”q SÇæfÊ–ªœc[ê1Œ£L–ÚÀ«GÿþýóÖ«ŒéóÏ?|3¹J–¹öÚk'*QÖ÷’ð÷“‚>mS¡×­·Þj¿ÛlÒÜ <–]vÙà]>=¸Qi¹ÕH“¾J¯i�{€t”4Äæ×_¼KŸJÿûß›Í7ß<ø$Ÿ™F#{kbUím•B'iƲٕsl+=†jzâF9zì±ÇÌ믿nÛÇW¢Ë,æý÷ß7ß|óMð®=?)èÓ6z¹í-¶ÜFÓû¡Vé§Òs§×O@éJ~iZµ¨fE¥æ™é¦›nÊéE¥í¢‘½Šµ³¯Å¶¢4¥ÛJŽ¡Jzo¼ñF;Ê‘šÙht$�À¤uû/�ä:•S�e&Q,øõ÷“†g oOÜKÛÞL£½Õû~¨eú©ôúÇõêAI Ô¶Îo¾ù¦-,—nŒþH/þ¨6ÅÚÃûÛúÔSOÙ¿…¸’Ae õA:’ÛR�¡OéNÙêxjt4õoŠjïî7S)&­eêù= ¾ŠqÏùYuÕU ¶Õ¯d?5“zßÕH“q*½þ‘¦ >”t£r–‹�R¥kãì‡%m¥mÕGâÉGÏ$P3 Í×ÌCóÖ³ð±õÓÛ¬Y³Š6ÙÓÓ¸ï¿ÿ~Ð|ú駉Žg)�sZËTÓ¢b#_©s´ž!.sÇßOIÒºÎÍ$AQ£©÷ýP�4Gë¨äúçïËRÏ=@õ”xüQZÆ�{ÒÓ¨Kí¬yË-·}’qÒÒcqCk*¨g`Ä-W7Ô)S¦ØéýöÛ¯i`˜¥r�­ŸÞôL�¸ùõo=ÓCO†WM‹ëŒ¬ŒgxHVGÿ‹/¾8xW\šËT�¸'¶kù7ß|³Í$j?ì±ÇÁâù JëÊìj[=¯Ÿ)QÏû¡i²�J¯åž{€ê!(ñ¨Ãò‘Gi§5d¤:&kÔ$=‡B¯ ØŽœºIé‰Åº©%áJ†õ$c�F£R7ÿƭ媂nžjbPlHUq*·\eýmÕÍVÛ*…ž‘6W+ RL·Mq•FWɱõÓ[Ü1Ô¼j#:†zø¢2cz„2dzà�? ±2…Ó¦M³Re´Ö]wÝà?…¥½L=±ýꫯ¶Mu4�šÐ(ƒ§sJ½öEÜs0ÂÂûIÛ¡ýä2’Z¶¶ïä“O¶r¥¿f|¦D=ï‡j¤ÉB*½þ•{Š| 5˜ÎiÛ'˜ªž=zSm£•¢eÐÔ`j±4·Y¥kîfE/;ï¼ólé¸nþêxênXʤº›¯nhn |eÈôf•ÇQFmÈ�!y7¿¸å9I–«gB¨_ƒ_û·\÷ÛõGŽY°Æ&î»nÙþ6écÇŽM­¦&í4PÉòÊ=¶Ž2C®ö ŽŽ¡ž•àDícŸÖ§ãª6ó ˆ4«Ë”ÅûJ–é§…N8ÁfL‹-'œ–ã¶ËI²ŸvÜqÇ‚ËSOé©Zû!ɹ]lÝ¢§¡+-Ç]ûª‘&ã¶§ÜëŸSι—D9Ç¿’{"šéi‹º>™q{µS™¢¦$‚ž¬&®ŠßQíˆnÆ—]vYÉ™žž={šñãÇÛá2ÃCÇj¹ºñ©t¹ÔÒ8·Üä•@* Œ‰nص~ð—J!/½ôR³ñÆŸ4·J�mïÞ½í(]ªE)t }ÚÇZ§æ §Ñ~ýú™ &Df( Ik™n9á4YɶI±ýtùå—Û€¨–i= õºª‘&‹©ôúWι¨jJê\’’ËŽ¨žJ¶Ñøê)=qÎ7¾rŽ?%âˆBº@Ú¢®OÔ”À"%uLDÎè88çAIRó �£Ž ê*›m¶ÃùMŠsÐÑ”Ô9u¸TGi7ü%€æÆ9èˆJê�F±ÑH5'N4÷ÜsOÍGÏP[œó€ŽŽ @¦JdŠ @¦JdŠ @¦JdŠ @¦JdŠ ‘.\hú÷ïo9ä3wîÜàS |¤)¤‰ôÍ… @¦JdŠ @¦JdŠ @¦Jªà‘G1x 9õÔSÍ·ß~k~üñG3sæLsÚi§ÙÏõÒˆ1cÆŒ1}ôQ0W<Í?mÚ´vó_}õÕfþüùÁ·âU:?²GšBšHO€zCPReß}÷�1b„¹ð Í[o½|jÌ?ü`}ôQ3dÈóøã�Ÿ¶§a/�:ê(sà 7´›ÿ‰'ž0§œrŠÍ8(c¥ÒùQHSHé PJªH7ûÁƒ›W_}ÕôèÑÜ~úéæÚk¯µ¯˜å—_ÞÞx¯¹æóä“Os-6gÎsÜqÇ™/¿üÒ,¹ä’6sàæ¿êª«ÌÆol¿§ŒÃ•W^i§}•Î�úCšBšHO€zAPREï½÷ž½Ù|ðÁæ‚ .0½zõ2«¯¾º}í¾ûîfüøñfË-·´ß½îºëÌ_|a§E™wîÖ­›™0a‚éÝ»wnþµÖZËœþùö†.Ï>û¬¹çž{ì´T:?êi i"=êAI•m½õÖ¶mt”N�:™£�>Ú–FªiÂC=üǘéÓ§›O>ùÄ–{ì±f™e– þ“o—]v1ûì³��ž™q{µS™¢¦¤ÊtA?~¼0`€}ø—³æškš~ýú™ &¼™öìÙ37ÿºë®|Ú–QØqÇíÍ|ðàÁ‘7{©t~ÔÒÒDzÔjJªÀ/…9r$7Ó*èÈ%Û¤©ô‘žHOi*çøS"Ž(¤ ¤-êúDM ¬©S§š1cÆä½|áÿéû@3!(Șj,Ã�‡/ü?j8ÑlJ2¶Ë.»˜�6Ú(xW˜¾§ïÍ„  hP‡$’~h$%U Ò‹‰'šË.»ŒêU¤‚4…4‘ž€ú”¤¶„Z4+‚€:Q¬„Z4+‚€:Q¨¶„Z43‚€:WB- šA @‰ª-¡–ÍŽ  Î„kE¨%A³k1h ¦sZÇö ¦ª§G�Á”1³gϦ’‰zD~-¶õ#í4@šêØHOHS9Ç¿’{b-Dý&T_·¿þÞ,ûï›oVXÃÌûÕÉÁ§hDõrˆ<—ÇíÕLeŠš€:ôÉ/vËû 43‚€:ô÷Λ˜/~¾•ý 4»¦i¾¤ÝÜé i¢ùбÑ|«8jJdŠš’¶êÛ ¦Œù˜ö¦”lWˆô”�ô„45cMI½”üõ¦žÏ—Èû5%¨Tçùʽ€J‘ž@VJdª!›o¡ ûi"=!M¤§ÒÐ| h4ß*5%2EP S%2EP S%ÔÐ[o½ež}öÙ¼—/ü¿×^{-ø4/‚ š0a‚íÇùÂÿ5jTð•š7ožùío›÷ò…ÿZ ”Ð!‡b:wî¼+l™e–1ÇsLð@¥vÙe³ÑFï ÓyªóšÏ)i`ìCTBµ%Ij@ú÷ïoÎ8ãŒà� ×§Âyä3dÈ�à]<�{:³ÖHÏ)Ѿ½îºë‚wåëÖ­›9r¤-˜it .4§�všùöÛoÍ…^hÖ_ýà?¨žSRjJÄ“O>iÆŒ“÷ò…ÿ7ujD¢ø YrÉ%͉'ž[Ò©S'³Ùf›ï5%µEMIy¨)i*QKz/å»èØ Õ–PK‚¤¸>•Gû!®¶„Z’òißm±Å±/5Ë* �úŽ^Í��‚ ¤� <8˜*,é÷€B�G’æ]€Ãõ© æŠWÎ6$Qér՗禛n²ý Üü'œp‚yüñÇíïþî»ï̱Çk?×àâö�Ö3wî\ûYõ‹qËvó£¹”4˜B%�”B¢\º!¸ „Z”‹ëSyüÚjIê—2ÅGu”¹á†ìSù�~øÁ<ñÄæ”SN±�…2àq”11b„íl^Æ£�>j>P&>NÛ¥Òå*È:ùä“í_~ùeð©1ï½÷žØBƒ6|ñÅÁ§‹m¿ýö6Ík=€ ·lõ Új«­‚OÑLJP\i#¥�(—ˆø P*®Oåqû‡Z’ú4gÎsÜqÇÙL±FíRðpíµ×Ú×UW]e6Þxcû=W^y¥�S@¢ãüꫯÚö§Ÿ~zn 0Ë/¿¼Íœ+U�Æ6D©t¹š_A–æ_sÍ5M¿~ýró>ÜίÑÐ.¸à[›â[zé¥Í6Ûlc§'Ož¸ˆöÝŒ3ì´ΑæDPÒ€¢J)…D¥Œè†B- *Áõ©<Ú?Ûm·µ$uHb—W)½†RïÝ»·Y}õÕík­µÖ2çŸ¾ÍØË³Ï>kî¹ç;íS­�2î|°Í ÷êÕ+·ŒÝwßÝŒ?Þl¹å–ö»zø£ŸAOkÂ*]®jX.¾øb;­ù/½ôRÓ·oßÜü›nºin~ýþ({î¹§ ÈT óòË/Ÿæ{óÍ7m@£`d�=ö>E³iêç”Ü2c�¹õéè“ Ê‘¿^Óüv›µ‚wÑêe™¿^ùÿòž ÷¿™;ËŒWé2uÓP`âc²Ì$üe†Ÿ[â®OìÏÂËlýñ{ÓÒi);�d™µÔHÏ))Æ=ñ]ܱcÇšW\1øO4÷}Õ"{ÆÇ-·Üb¦L™’·l7¿l½õÖfèСv:Š2ùêÓႤVº n¹Êøûó§õÛ’ìK7¿œtÒI6õe=z´yþùç#÷O±ÿ×#žSRžNfËÇÓ9Ã÷®þCuÔ6Ñ©Vµþ¤Y ݘùû:Ñkóµ~jzoð³`îhõ²Ìÿ÷/Úÿ¼yól)ä°aÂ¥µiæßÎ2ãUºÌŸþô§ÁÔbìO–™äå/SÍÿ¢®OìÏÂË|û³osÓI–YK#hß ¹y…jPº|î¹çlF|ï½÷6Ë.»lðŸö”!¾÷Þ{Íûï¿ok1öÝwßà?ÑT; >!_ýµYwÝuÍ:묓·>åw~ö³øãºÂ +Ø’7ÞxÃ|õÕWfÇw4---oƒ‚œ‡~Øþž�wÞÙnC¥¿MXnþ_ýêWEkCÝüßÿ½m²¥í’%–XÂ^3Ôþøcsç�wšÖÖÖ¢û¯^Ôóùµmæù;GS™¢ùVsÁ\µ‚:(×'4eÜÝÈSÛn»­ý[ÈÊ+¯l–[n9;n®´öÚkç2â…è{¾4·ÁWér+™?l½õÖ‹íðþÊ+¯Ø *éþCãjêæ[m%N_ïŠëú³ZôŠ>aœz[¦jœ¢núá·Ça™,3 –Yýe†¯OYmçw�¦Ú‚¤f9FµÔQ›oùÍ©ü&GqÔGCÅÕÿÁ5¿rëSŸ‹‘#GÚõÞ¾o¾ù¦âmˆj¾UéoSß“RæWãšaE}?j?ùëL²ŽzAó­ò4uP÷›ÊuÔ ¤ÔLqšA‰û¾šoUº QAI¥¿MÚË�?êûQA’Fö:û쳫zBPRšoDèÔ©“éÚµ«�~ê©§ìßB>ûì3ÛçBýG¶Øb‹àÓ6ï¾û®·}OV]uU›Os|•.·ÜùãtéÒÅöw·¼gžyÆþÝl³Í& AùJ"(ã­ŸD}Š=uÜ=àOó­¶ÚjÁ§m’< Pµêð-.ßæ6ø*]®þªóºÌš5+ö#NøÁŠQ4L²hyêì¯}¡ ÈÍÍ‹  †ÿÔñ믿>ö©æjv冼Ýo¿ý"Kö5ZUÜÛÕçâæ›o¶wÕ�øÏãHs|•.×ƈž½’dþB6Ø`;˜–£!„µ/èàÞq”ÄÐSÇO<ñD;­á}û÷ïo‹>øÀ¾,X`3äê"ª}pω¢çö\}õÕvä*e¾Õ¬iæÌ™æŒ3ΰ�ÀU30bĈ¼€"ímp*]®š\yä‘vZó«OÈý÷ߟ›_C<÷Üssóã×Þ¼þúëöïþûï_´šÝM�ûMå:jGwŸ‡+®¸¢`$õ‰8æ˜cò2Ñn}ê¸~øá‡\†’B1,w¢:ºûÊ]®ã~cÕtœyæ™¶Óz±Žñ~‡÷R�Q½ £{y¨)(¢gÏžfüøñfÀ€öá�Ž =äP™z 9•iw »e¨Ù“£öë×ÏL˜0¡àSÕÓØ†(•.WNTó/לËÑïÒˆ[£F�2ÿøÇ? vtwÔœ¬{÷îv:I44jJ�)¿t%É�‚â†Uó:ÔN>µGVÇÊB¥Žh£’K5'yõÕWíû¤çb£à~S¹fª)©5¿¦$É�ÀÍ*éð¾Åju5%塦uCíb£w¨#à¤I“‚wè\xà�æÉ'Ÿ >©Oim§„[o½ÕLž<9ø¤ºtNÝxã�6 Q‹]wÝÕ¶€´èºvå•WÚébÃûºQºèàÞñ” n¨T䡇 ÞEÓ�ê4+ *Y}íµ×‚OªKëS‡[•⪉ÅÀ�©YP2]K\wÿ5mÚ4sòÉ'ÛÀDÍÁ ï«,Θ1ÃNÓÁ½ã!(A]PSµCUép\m‰«%ÑEM#„ c8âˆ#̵×^[÷͉e; n»í6süñÇ·{Ýpà ¶æC÷îbͱ¦OŸnƒ#›nºið): ‚Ô…5ÖXÃ+X¨¶Dê–DO’ÕXæèütÕ³FÙN¨"êÓuúé§íį‚ÇgŸ}ÖNÓÁ½c¢£;2å:|ðÁ¦oß¾v”ŽÏ?ÿ¼]iŠªtýÿéAOê0ÕÑ]¥,êD÷â‹/Ú÷º(n»í¶¶*ØoŸª àèÑ£í8ê*áV;Öûî»ÏþO#†¨Š¹wïÞö{wß}·­‚v¥=‡z¨éÓ§OdÕ²Ö¯-ÏQzÿ÷ÿ÷ØõkøÇ{,7�:+@sû&n¼yõcЩŠuLö×¥}óý÷ß·ÛGáßTlûüõ%ýÍþñNú›ÜüðÃÖ#Ž8¢õ7¿ùMëçŸn?[”áµó�5ʾ÷Ù‰'žØúöÛoŸ¶y饗Ú-cQF¼õ¢‹.²óèsž^xÁ~î^�=öXðŸÖÖ�?þØn£>_,Ÿ.ÞNmûÓO?|ÚÆ­ßŸ§Ðú�ñãÇ·[�Þw…øëÒëÚk¯m]”iþ›ÿ·O“l_©¿Y ý&w ýßä~§ÿ}÷½bëõÓQšé#Jx;Ãû<|œ\ó·QÜ>õÓ¶S�ßàιSN9%/Mˆ¶åè£�ÎKÿâÎ�¸m�:.ú½áí+õ÷”‹ûMå²Ê+�¨žÏ—¨m "‚ÌQS‚º¢Ú’E[r¯1©„W%®j6U¨�©j;\'øãŽ;®]“u˜+Ôg%<�$µÏ>ûØi5ó›©èÁNÚFyê©§ì_­ÿæ›o¶Û©ff½zõ²Ÿ;Zÿ°aÃì´¶SÛá‹ÚfqËqëq´>µ½U‰ò{ì|šŒj!ÂOåU‰¹JÒ·ÜrK³(óhæÎ�ü§MÔö•û› ý&w Õo(Žûž[¯ã/;�ôQ*¥¡ps5—ƔֵϋÉâ7hhà?üáíši¹sÀßöbéA¿WÛ§e~ôÑGÿ@s!(AÝQ'veŽçÍ›gþó?ÿÓ6¡*–ùV35KÒ¸æ+¬°B»! õR³¯¨¦CÊE=—AË’®]»Ú¿¾UVYÅ.Ëqë/ÔìÈý®wß}×ö/pâÖ/nžY³få5«rë+6Þ{µÓ÷G�‰2ÏáLrÜö•û›‹ý&$…šè•²^ÇÍ“fúHJó3¦Ë¸kHÞb²ü QÜ9à¶=ÉqÑSŸõdhmg%¿Ð|JPw”9VÆYÔÁ\%¯*I-”ùþôÓOí÷ÈD I¨—ú7è;aZßj«­¼kOíÜ‹që_uÕU#3ü¢õht&eúõÄZ§Ðú] .-~å•WìúÔ¸Ú6•@ÇÑ(hâg’㶯Üßìÿ&uÀv’þ¦RÖëT3}T"Ü’ÅoPm†x8óÌ3íƒ!ý—ÎI­ëã�?¶Ç2ÉqñUò{͇ u©{÷î¹æS…J^Ãô]ur.ô:âˆ#ešêÅöÛoo›Ë¸Òbu¿÷Þ{KÚ/õÆuxWóý&e~]s´BAS¥²Hi5µú :&äA�Üß|óMû¤wÿ¥éihÆsP:‚Ô-õ!Q†ä„N(š!ñKœ7ÞxcÛ(î¥&Oisëw¥ÆQ”ÉS“”R›Ò¨ŸÍ6Ûl“k¥ ¢š¾( )5£¦mP{þ8ï¿ÿ¾ýÕd-¬’߬é�wÞ9÷›Ü“ú õrJY¯“uúHC­ƒ;&ªÕÒazÒ»ÿ:è ƒrÛ#IŽ‹¯Ž =%¨[+¯¼²Í�$霫šÕ®¨)ˆj¢(£ª~ Õ�dý.“§6ô¥v8Vß5�ÒÐÄÏ<óŒ FJíà.nQ´T[¡Bû½˜J³ûMê¿¢ßTêzÃ}s|n½NÖé# µþ .@�kNç p’lŸšðÝxã�ö¸5Ã1¤‡ MAMd\?=?A™\ŸjÔ墋.ªÊsŠ­_}'ô,e¼õL†Rk8\Çm S¦L)«ƒ»£eÜyç�y¥ÙÊüÝvÛm6#¯À PGs§Òß¼ÞzëÙŒ©FáÒ`¥®WÍyç�×.0qëõe�>Ê®q¨õopý‹t|Â5z¶ˆÒ¡/IzÐsGôÌ-¯� ꛂÞþýû·ëÿ~©ö].„Ó€l” i¨ÔU�o•YUfF7žqãÆ™sÏ=×fŠ•áÖ(Há'q§%jý—^z©ÍˆéÁyʈ 4(QÆ;L85£eòãFs*F��úLœ8ÑÞ¼Ï9çû¤ïƒ>Øf2Õ¾ß wœD%¿Ù=,SâS‰{)�öÝzµ|u†ÖzõT~ý&­W}8ÔïÁ—uúHʯAÐïQ's—)¯åoðaO­G/Më)ð (´­¾$éAûôÐ(ÇÍE×�SJsªûñÇþÓž—öÃC¥HA šŠ†½êª«Ìæ›ono<?ü°yõÕWM�=lÉXøYiÓúõ|·þ§Ÿ~Ú> {Çw´™³ðóJá:¼—Óü˧NÃÊ êIÛ³gÏÎ=I[™Å‘#G–\‹SÉovÞµÎR;¸k½ún½�=ö˜Yi¥•ìï¸ì²Ë̲Ë.|s±¬ÓG @�>úhÛÏB™u=ÞW«ß íгk|þùçv=zi¿ZOxûÂé!`4Â1AãQ�HxÀ÷Úwß}sý™tŽ©¦î’K.±As˜F‰siß5@úZ¢žäØ:¶O0U=ºá8Ê(LMfTB­Ú…R3jjž¥RA5¡;vlÙM¿€FÄý¦r-ƒ¦S‹Õ"¯P  ª•SÍ«jP‹Õ°ézøÇ?þÑÁ¢Úè¡C‡ÚiGÏÒ9ûì³ít’eRÏçKÔ¶™q{µS™¢¦h *Ü�¹åtpäSí¬ú¥¹­ê«‘æ|jb¨æ®z�ÕEP47šT%ÜùÔLñ”SNÉõ�š-öDú$4d°h_©&KÏ^¡ƒ{s#(4-¿¶„Zø4*Õˆ#rͶ”Q÷Ÿ)RÈ-·ÜbN=õÔ\‡(®ãüª«®š „ýQ±.¸à‚Øùõäôë®»Î>Þ5_òŸx®g ÄÍ«`fÊ”)vz¿ýö‹ ²üïê§¡}P¨Y”ß±¿ÐºÜ)°ÓÃ!‹/Ÿ§ìk]Úßtpo~-fà­ÁtNëØ>ÁTõôèÑ#˜jëÖ¨ZM ¦õè'Ÿ¼bÖzõ³p½ý̧km|Zßjq.EÔ½®Þ¶qáÂ…¹'¥k¬¸�Œ(ãî:d‹š EÕ¢¹e*c¬§™«i“¿•Üzè¡¶†ÁÕH,X°Àö#Qm…þïæs´n¢ÿ4Èl¸á†ö½jܼ¢@ièСvZÔïãì³Ï¶ÓQóŽ?Þ¼øâ‹ö}xÞ0eô]ð"ê\^(h÷·{Í5×´5ê4¯}¦æbªm¹ãŽ;ì~Ñ^#GŽÌíO7oøóbüuŠú´$ ³TÏçKd¾uÜ^-ÁT¦J*DPõoí—n4 6ù­i]b©à“úFPR:?XHÊeìõdô(QA‰¨&CO5/´.-{È�!‘™èÇÜÜ|óÍçW 5XC’uÇY¾ppž¢$ÙîwܱÝË JümÔ½µ¦çǨOŽFôj”î5%塦ueúôé<Á½ƒ!(@ÝÐðÆî�Žtpï8JP7ôùaÆ™‰'òÀÓ„ @¦JdŠ @¦JdŠ @¦JdŠ @¦JÐô.\hú÷ïo<ð@ûºçž{‚ÿ$7gΜÜü3}îܹÁ€x�<òˆM3§žzªùöÛoƒO@X‹ø@k0�Ó:¶O0U==zô¦Œ™={v0ÕxZM ¦«ÅþCr JN;í4óå—_Ú÷Ë,³Œ;vlâ'Ä~÷ÝwfÈ�!æ“O>±ï—\rIsá…šõ×_ß¾â((¹îºëL·nÝÌÈ‘#mÚÂá>µ�’«—s:ò\·WK0•)jJÐá¨ÄzܸqÁ»â¦OŸž HP[·Ür‹­i¸ä’K‚O@3"(A‡²á†Ú¿/¼ðB¢&Xª%¹÷Þ{ƒwÔ—UßþSî42‚t(ÿú¯ÿj:wîl~øásß}÷ŸÆ»óÎ;m-‰šl©_ õ¤óü?å^@##(‰1kÖ,šì4¡¥—^Ú|ðÁvúÙgŸ5O>ù¤�Ž¢¾(�>ú¨�>à€Ìl`§�.:ºÇ3fŒ¹é¦›ìHKÇsŒ-]�BG÷úçwt?餓Ì6ÛlcF�mžþù‚�ÕŸaÊ”)¹Žñï¿ÿ¾9ûì³ vtÿì³ÏÌÔ©SÍ‹/¾hÞzë-û™¾¿í¶Ûšý÷ß߬³Î:ö3_¸3´ÎÕиù5Ï¡‡jzõêeßÿøã�æÏþ³}¹ï¬¹æš6pêÝ»·}'j^·}ûéÚµ«ýÌçoßùçŸoî¿ÿ~3mÚ4»?ݾxç�wìw í'|<¶Ûn»à?‹¹}Ũ ɶ…·åå—_6“&M²ÇÈÑõhï½÷Îíã8…ö_Ôñ-ÖÑ]g�uV®6.j{+MSI÷ ²Ã}•h–�ƒPttoPê=a³뮻šQ£FQsÒ$:uêd3s2oÞ<óÜsÏÙiŸ†v™âýöÛ/ÑH]3gÎ4¿ûÝïl³0—y5{â‰'l¼ØpÄ·Ýv›Í0úó+ï Jóª�ˈ#Ì 7Ü�÷�÷Þ{Ï\sÍ5‡žU0pÔQGµ›×mß)§œbƒñ¸ù•!×y ß§Ì­oûí·Ï5‹S�B|ðA;¿2Ì[mµUðie m›£ó÷ÜsÏ5ÇÏ HD7ríãJö_’ãëó+—^zi»@¡Ò4•d¿PJ 8i>jеå–[Úi•(ñÅvZ”‘SIº(ãì˜BÄ(˜PÆO%×—_~¹­íP'ù«®ºÊl¼ñÆö{z—‰T€¤ »›ÿÚk¯µh^eb_}õUû�ÓO?Ý~GËßsÏ=íw´ ·í>mßqÇg·O%åâXóºùÝ:Ô\íÊ+¯´Óa ŽÂëÖw•‘V³8Õ@ÉäÉ“óö§OAÕŒ3ì´’¨*9âˆ#ìò÷Ùgû~ë­·Îm¯Ö m›h½nß©V©_¿~¹åé¥Z&)´ÿ\íN¡ýWèøú‰®+Úv_iªØ~ ^””€à¤y¨¶äè£�6Ë/¿¼=®=ôPðcÞxã Û´K�Äeœex]F~ç�w6W\q…m¥ù´žµÖZË6¡ql•ZÇ�ü¥Ì·Ò–æ_}õÕͦ›nj›Œ¹æƒ*-×r¬¨©‘¾£å«‰¡[~8(ð·OA–Ò°šyi^7¿¶OA‹¨¯M\&Wókûܺõr¹ý©&RQÞ|óÍ\F|�=ö>mOûÍ_¶¸õ­¶ÚjÁ'ù m›‚¦O<ÑÕHôíÛ7÷½?üðÜï�Û  í¿$ÇWü€DÁ‚𢅒4ÓT¡ý@½¨‹ ä·¿ýmݽ¢JK?8éòæd³äwþƒFÒ¥K›á— gà“4/rÏ1Ñ÷Ä9ì°Ã 6qR ¼JìÃA�ß9_”yŽjNæ‚Õô|ôÑGÁ§‹·OË?öØcÛ-ßÙe—]b)6¿~[÷îÝíôSO=eÿúü¨Í6Û,ñÃ+“(¶m¢m9�(¸ŒÚi¦©bû€zPA‰Jfëí¥6úÅ(ó²Ê‚éæŸŸ¹Èüì�¶QšÐXüÒ}=P1iÔQF[éE 5G?¸P?­Ó§ð¸Z€5ÖXÃþÕv)˜Š²òÊ+›å–[ÎfP•ùû¶Øb‹¢Ív ÕvÚ>Ñÿ]S·¨çÀh¿ªI’~ƒk.•–bÛ–„–á:ú»ó¿”ý§ù‡ øøü]íET@R«4@=¡ùV…~Xú'æ£n}̧km|‚F¢ þ‘Gi§•TÿI’�e çÏŸo§Õ„æÌ3Ï,øºõÖ[íw£(ðXvÙeƒwùVYe,”Êß>�ÖTŒ l$˜Ú>g½õÖ‹-¹å•Wl†|íµ×Ž­I(W’mZêW¡c¡çÎèiñî¥Ì½k¶ç”ºÿâ¼ûî»¶ž HâF|“Z¥)êIfA‰†ü¬ç—Úœ¢Œ×gœaþ§×™6 i]b©à?h4®ÙŽ£ÌâÀ�ƒw…i¸Ö¯¿þÚN+#þúë¯|)S*^ø›o¾±ÓÕäo_-ĕܫYœ:dK’~:iS-�yë±ÇË;…¤µÿ”6ÜúŠýþzOSTCfA‰:ôÖóË5— sÁÈÃ?lKZ FŸëí$Xüš Ï:qâÄD/�¢”fŸŠ8þöÕÊ&›lbkuüá–ýîê¼_Kªy¸ñÆí(Tj:¦Q¨´ÿÃÇDA“‘ÍIkÿ©fÈ�–¥¡› =´³ÞÓÕ@ó­„ÂÁH­KzQ]ê­‡eªi�˜™”ß!ªswÖJÝ>WJ¯Ì»š°•Ã@À­ó™gž±Óîàž„ß—ECìjª¨ó×o6å¤u|µ]?ô¨öC�IÜhYõž¦¨‚’"F:•P—:\ª2�nž¨ÎÝaÊøêÙµ¢íSÍŸ$Ù>÷`CÍWIieüeÖ¬Yvˆe=ÿ¤Ü“øôÓOý&W›ãó�¯~KÜóW=I]OOw8-Kµ4.0Q“¨ã᯳ÓÕ@Pƒ`I¹«”Ѽþúë#3¤¢ ¯†óÕLÅ2šiòŸ¶^hû”¡.õ)öqÜÃ)µ.õÑRPP�îI¸A”y÷‡Jöid¬‹/¾8x—Ï‘ì‚ .ˆÝzúºJ¸ýöÛsÍÖÂ’&õž¦HAI 5á!Aþ^êG¡ âã�?žËHªIÔ´iÓrϧ¨uæÜï3£íSºÖö}ðÁöµ`Á›Ùv#�©f¥”&lQ”ùv54êŒ-åtp×¾Õ¸mŽ ,â( Só<¸¦d¢@EÇFÇLÇkÝu× þ³XøøFí?5ýSÓ0ÑïÞn»íìtí›SN9Åö1‰ Lê=M�6‚ zðà�!Cl鶆ÒUŸ=ØNCÍêáw7Üpƒ­-ØqÇ [-Ê”+óëJßµ}Ǽ}ýîw¿3/¾ø¢ýžú‚ø�þ+á:¼‹~o9ÜÝ2ümÖökFåSàžÞ¯åŒ=:o`eöT¸Îèaþñ�Újö&ÚC‡µÓ…(PÔñp5XQ�I½§)ÒDP¤¤wïÞæª«®²ý&üwõ¥PÆñòË/7'œpBf™Çž={šñãÇ›DnßW\Qð‰ç¥r5Rns0Õ\z饱ÁBRZŽ~»Ž� ”dÍ5×4ýúõ3&L(ú\šbÇ×í¿¤˜(Ð(˜Ô{š --fà­ÁtNëØ>ÁŠi45˜ZŒý,~‚¹«…Hò0J #â>‚JôèÑ#˜2föìÙÁ-êzcÆíÕLeŠšUáFò¢¿(† @êô÷3fØé,žà A €ÔMŸ>ÝŽ ¥`¤ÖOp�‡ @ª4Ìî³Ï>k§+}Þ èJ¤JCð6ÌLœ8±âç�€Ž� @¦JdŠ @¦JdŠ @¦JdŠ @¦ZÌÀZƒéœÖ±}‚)Ó2hj0@:¸#©=zSÆÌž=;˜¢Eæ[ÇíÕLeŠš™"(4µUßþSî¨O%@��1²ÑyþŸr/@}"(j„Œ@4:º5BgD œ{hf¤o”‚Žîƒ @¦JdŠ @¦JdŠ @¦JdŠ @¦JdŠ Ð4&L˜`Ÿpí¿|áÿ�5*ø K%@�1²qÈ!‡˜Î�;ï [f™eÌ1Ǽd‰ ¨2F@6J9ŸJ9OÕEPT# ;IÎ)  ¾”UBÆÈF’óŠÂ¨/%@•�1²SèÜ¢0êA PEdŒ€l:¿( €úCPT# ;Qç…PŸJ€*#cd#ê<£0êA PedŒ€ìøç…P¿J€ cdÃ?ß( €úEPÔ# ;:çÖ\sM  Žµ˜�´Ó9­cûS(¦eÐÔ` (¬åß›õž»ÔÌï9Äü°ôO‚OQÊõ†ó åZê›OÍ÷Ë®¼«܇‘T�=‚)cfÏžLÑ"ï£ãöj ¦2EM P#­K,eÞÞl  ��F H #"(jˆŒ@{%2EP Stt¯PT‡!ö­Òó…ó ͈t�JÐÑ¥ £;Ä (�)‚™"(�)‚™"(�)‚™"(�)‚™â‰îÊòI¼ .4§�všùòË/ÍI'�d¶Ûn»à?åûì³Ï̳Ï>kþüç?›·Þz+ø´í‰±{ï½·éÕ«Wð ÒöÝwß™!C†˜O>ù$µãYoêù‰î ,0=ô�yýõ×óÒþšk®i6ÝtS³ûî»›uÖY'ø4ËI“&E.óŸÿùŸÍ¾ûîkºví|Š45ÒùÄÝQ žèŽRðDw4„ÇÜüîw¿37ÜpC^Jt¡=z´½¹¿óÎ;Á§p Ž7ξæÎ�|ŠF Lë¹çžkÓþƒ>Ø.í¿÷Þ{6XQÚOšþüñGsÓM7™c�=6v™O<ñ„9å”S8§"p>@ÇCPë‘G1×\s�­uYrÉ%Í¡‡jÎ:ë,sÕUW™3Î8Ãl¼ñÆö{Ê<éý_|aߣͧŸ~j~øaûR† �aæÌ™æä“O6¯¾úª}¯ —öõRZßsÏ=ÍòË/oÿ¯ô¯ÚÉBe•ÐwÜq6‘ð2õR ‰¿L&*@Î'èxJ`æÌ™c®»î:;Ý­[73aÂsÐA™-¶ØÂ¬µÖZf«­¶2矾9ûì³íw¾ýö[sÁØ¿@£Rº¿ð s�øé§Ÿn®¾úê\Ú×Kiÿ˜cŽ1ãÇ�7ÿöoÿfçûá‡ì¹˜Üyç�¶öEËTó¡ð2õ:òÈ#íy¦ ßücÇŽ¥VÐa”tpjf¢6ï²Ì2˘sÎ9Çþ�Ò³gO[,óæÍ3Ï=÷œ�†1Ý»w7'N´¯fì ÒlTú~ñÅÛi¥÷K/½´`©N�:™>}úØ F5 LÎ;ï¼v5†Zî£�>j§8àÓ»wo;GA¿–£ EË^ýõƒÿtlœOÐñ”tp*ÑU‰±(àXqÅítœí·ßÞtîÜÙN?õÔSö/ÐhÔ´ÊÕ�Œ1"qve–‡ f§US¨>>5;rËU�H xÔ¼‹€Б”tp¯¼òŠÍD©´X# ³ôÒK›ƒ>ØNÏš5«hßõU9ðÀÍ©§žj3qª™Q;~µË×çzrÈ!f̘1æ£�> æÊç/㫯¾2wÜq‡éß¿nÞ¨&/ZÏ´iÓÚ­GMiæÏŸ|k1·Ž¸åùTîÖÿä“Oæ}Vh~�læo»^'œp‚íK�´)œ[FÔïŠë,]ÎþkfáÚŒRƒ� 6ØÀl¹å–v:É9�&ΧÅ8Ÿ ¹”tpï¾û®ý»Æk˜e—]ÖN£ïŠ2*qŸ(ê¬Ri5SñG#RSeÕþ¾Pg_­oÔ¨Qæ¾ûî³�TehŽ:ê¨v£ˆi=nÄ#eÚüŒ‹«Òw^xá…àÓh.�Sÿõ9HBG�îÞv�¤Ô¡Ú‹8þ2¢~—:KßsÏ=Á§í%ÝÍοRj3|ªÙØÿýí´ÒÐË/¿l§e•UVÉ5ïºþúëgŽËÁùÄùÍ„ ¤ÓMõƒ>°Ó«®ºjl_’0?㥠KÊ@ <ØŽr¤1ÕÕ©øÚk¯µ¯ä–§ …+- SÉ¥æWS7ÿ•W^™WÒ­¦hj†æ2�ʘ¹õø�Š•iÓ¼Žj€¶Ùf;­¾2q™Ií3=ÇE”�J²ÏüÕ‰©_¿~¹m>|¸Ý&ý6ý&5§‹â/C¿ÿòË/·ªï½÷޼ߥ÷q©$û¯ÙùÇoíµ×.ù¹#Îzë­—kƨŒ°£ÏÔÄKÔïê°Ã3W\q…m.¦Ì¹{©v¡”€>Œó‰ó š AI¦ �kzQí¸)㦠€š~iä.u*^}õÕíK¥ÓèF®IŒF‹k£ÒT•Núó;ʨ¹Œ‘EL�Ý÷Ô©X£ˆ¹ÎúÊ ù7ôk¡Nüo¼ñ†yþùç—²‡·Iªûöí›Û&5™ó·)Š¿Œ�wÞÙfru¼”�S©½û]ûì³�ýŽJnãš�ÚM)�x˜ö»;güæKú\T—–EA�žY2räÈÜKµ ƒ ²M~ 5ŠÃùÄù͆ 5³õÖ[Û6×Q”8úè£m&F¥ªzX]˜2.z]\Frúô鹡X }o—]vÉe8&OžœË°ù¥Üq�øŸyæûW¨$%¢n›´-…F6ó·)Ì-C OG¥ò…šÌÛ/�ˆW‹Ò²:ëÄ]Ï#Ùpà ƒÿDSS!õiˆ«ÑˆÃùÄùÍ„ 5¡¸:Ò¥K[r)QM>”ÑZmµÕ‚wùüf I28®Wëp}´|eô$ª³JXg̘a§·Ýv[û·›6Ûl³¢#›©´5¬”æ-þ ¥î¿ŽBû Úµ‚ŽJÜõ<’‹.º(7¼­{¹fBJ‡âšZ%í$ÍùÄù͆ ¤ó3hÕ.=NÚ~_ß‹S¨3¾2î7$É଼òÊf¹å–³Ó~Ÿ×AW�péò›o¾™+aMÒ!·Ômòû)8þ2ÔŒäÌ3Ï,øºõÖ[íw£”2˜AGðñÇ·Ëh&å—r‚�{ ZTR¯¾ W"…ó‰ó š AI¦Œ‘k]J̓Jc«áý÷ß7ß|óMð®8 íùõ×_ïÊ×AW™÷�ɤrÓØ&Ê´¾þúë_:.Rêþë(üÒ{�ÎTj_Çe¨E�­+á1ì2Ìiã|jÃùõ‹ ¤ƒs%©¥Ütõ]©fó…RK!ý’ÚJ¹¦(~¦UP�Ø£@,I‡\Ic›üehˆÒp3 ¸—F*Ö¼¥£Úd“Mìñ-¥f"Ìõ…Pf:Éó} )·p œOm8Ÿ ~”tpʬˆ2Bþó⨄³”vÝNÒRi÷l�RGFRÆÎ5£Iò¤yWb•)R“uÐõ3­îY IÚ×;þ6ùMZâø¥ïN©¿ Źã+…FVŠ£Ìô”)Sìô~ûí—wèüxì±Ç‚wÉhžR‡ææ|â|€fCPÒÁùMG êèalÂS’´ëv’”Jë™'îIÛ.ã�”2®YŽÖS,£©çF(S¤ùµ=þ²4š�š«¹@¬”߬å¸pT¢(~sŸ¿Œ$¿KË)·IRG¡}êF¦Rº<ï¼óï3¥Ñ‹/¾ØN+“¿Ç{Øiç¶Ûn³Ï«ÐÓrÃâJÒtÏùÄù͆ ¤ƒÓMÚ:µžy×|DO@v?•ÉØn»íìtRq(î Óºùß|óÍ6c•ÙKŠt¡§i?òÈ#±%ÝŽßAWÛ­L£¶«Ô¦:þ¨DãÆ�‹Ý&?Ø sË(ö»T*¬ç3hxÙRKÿ;õ…ÒÈX¢ý©}æšdÅQú×÷”FU# §©‡kIjT,C«ÚÉBAN!œOœOÐL:™-Lç ß;Yu:Œñ@û›U­öŸ2?ü°ùþûïmg[ýU3�$/=•]+QÓeÔõóÏ?·¥�¢›vkk«yíµ×lG™ ­C™ =99IõðƒÓTBª\e Õ¶ûïÿ»]¾`¦§#k›¹feâ–¡öàzþ€Ûî0XÿüÏÿl3„îw¸f+ÿ÷ÿgKV5 «Ë@)°Òs¢hYûÛßl)ö[o½e?Óƒî~õ«_Ùé0w,”Ô0¬?ûÙÏìç+¬°‚ùéOj·_MNÜïÐòÝ6]vÙefêÔ©öþãÿ°ûX�ƒÝèJþ2ô»ž~úi»ïTâ«}¡¦3ùË_ìr´¼_üâv¸X·Ÿ’î¿Z¨ô|Ió|[wÝusé^û]ÍyôRß*5EÒ±üŸÿù›f5¬¯2ß:6Ú1+Üìh‰%–°™lͧ&^:Ó¦M³ÇKËs¬•Á}饗l†øþûïÏ-3œî£p>5çù”å}�ϯ™á¨D³_¿~f„ ±™”RôìÙ3·ÿw(#¤L†~ƒJ“Še&TŠî:D—Ò!7Šö­JÆ]ÓG¿}È�!¶T¶ØöôîÝÛ–L‡��û]—_~¹9á„RÉ$u$ªe8ÿüós3 §}£C=Ô¦íß$ÁƒÒ Ò³ø�vÚ)r™î˜i¹I–ÆùÄùÍ„š’ QÂUX=•Ò#{õXSÒH8Ÿš÷T¢#Ô”hTÃ�>úÈNrÈ!öo9T#®ÑÿDFþÀþÿD2 ¨j^úä“OïÚjoÝ€^W9ÊÝÔ”ePsKõkÕ« Ürô\(Ÿÿ?½4’¢ú¤•CµËþ²ÜˆƒNx]弚A àQm‡+)•õÅCéJ€�Ûo¿ÝŽÒ—”jV4O)Ž?þx3qâÄ’_͈ ¨ˆ”d¨fE5,â–�äJPU%G}’ÑpÆùÕ·×^{å‚ u²×ó‰ŠQ�Š `VZi¥Üƒ©‘A àѳ ÿ•qÔùÜuŒïÛ·¯}À*JCPx4$°ÆW5%ª1‰£ÿ»NñªaÙ}÷Ýí4JCP„èA´K-µ”�VÓ¬¸!‚o¹å–`ʘƒ>87JCP„¬¹æš¹Z¿ÏˆOC»!ªfÅH"JCPDPm‰:®‹?º–¨æDJÕŽ¨–¤Tï¿ÿ¾}Â{)¯¸›FGPDðƒ�ðÁS¦L1}ô‘�V�J9Ãßwß}æÜsÏ-éõÙgŸs7‚ †p¨¹–^jÎ¥·D5)q •!(@Ýò;Ž—ò„õ°¯¾ú*˜Ê_fGuT0ÕÖé]Ͷü!€]¯RpÀæüóÏ/éլà ·˜�´Ó9­cûS(¦eÐÔ` @9J¹Þp¾¡£à>Œ¤zôèL3{öì`ª¹(ø¯ÿú/;­LùÆol§Kuá…š™3gÚé &˜å—_ÞNË=÷Ü“«ù8þøãÍN;íd§}—\r‰­%ñ©åâ‹/nä¨ï‡šZ‰–¥e:IÖU-‘÷Ñq{µS™¢¦uk�5Ö¦ŒY°`A0U:÷dvÕjøIRª-  œ‚4­UßþSî tœCêÁZk­LóöÛoS¥Q³/×ôË_^)V[mµ¼#2pºJд:ÏÿSî tœCê�žâ<úè£yÃò&å�šå/¯T"Øõí8öØcƒO‘‚Ô-5·Ú{ï½í´ÿl�¤Þxã óØc�Ùi5Û*çy"Žšj©O‹^åvnG4:º£iu„Î@5qõ¯£œ§ F˜k‚¥ EµÅús¨ÉèÑ£sÏ0`@^,'íÎçtt/5%¨k >üæR�K™þBM¹ô`ÂaÆå’ 6Ø 2 A} (@ÝS§rÕt¸‘³Ô,ë„N°/ ׫5í:ï¼óÌ¡‡jî¸ãŽÜ³Dzöìi†j§QŸJÐTÓqõÕWÛ ÃQm‰ž¢&QªAyå•WrÁˆú}œtÒIæì³Ï¦H�£O šíá�Êpõ¯#Ÿ§z¢jKæÏŸo½T‹¢víÚÕ>ßd»í¶KôLõQ0#ª‘Ñ2*¡&c®s½–åœöºJQÏ}JJдÈP•áêç)JAGwˆAP S%2EP S%2EP S%2EP S%2EP S%2EP S%2Õb>ÐLM¥û§SÆÌÙñ²` @RœC@ýãHb��W5ûý²‹ýÛõgËŸ.öù×ßÛí˜6û»Ÿ¾ùþÁ*Sl½þï·æñ7þf��þÖ†.5% !üºÛʹz—1¯„[–^+ýÓRÁ§m�¸ÿíÞ£sðiqË.µ„ùÝÎ]sóÜþ‹–•¿ìB´.7¯ Š|iïƒB´Ž§ÿ‹yðø­ÌÀÖ‰ D¿OÁÔøþ¿4ó.ÜÉœ¸s7»Ê¥õÎͧ`äÊGç™îÃÿbn™± øÔØ€D�F’ß®¦_~ó«I³Úå[ï°I¯Ûïùý�T[âÿ4‚€•¾ûÝ$M¸ößìç¹ ¸2ó.3�´ —¿­Û¯¨eæýš {^û\^ H!ÚÞ£&¼dk7Õ2�?ò—Á»há BëUÿ¤ëÕ÷TcãwúWmIµûÜ ]%üšŠÍB}¢ì·ÙjÁ”1÷üõƒ¼»’4áêå%µ®%Q^ýHõÙt×+e�¤¥Ú Õt8ûoÖž¢„׫`Lë-•¶S‘ dª† �ƒ  Â3ó½7hâ7Ž­ ÙìçÁ;c&½¸ÐLö2æIšpù5%þºkaèîëÚßà ºëÕŠ†ö=é�³óæ?oïõƒ©|áõªoJ¹ëU@¢ÀDÁ št•2Œ3²GPAM°…:m+ q™kͧ ²:ª'mÂ¥¿¹‘¿îjÓ¶ièbGͧ*]¿~·ßŒË=øÐW�õª ×oþðbâ¦_¨%”±ö3·zFHœpÓ-çÖ§wü.Ԅ˯%Ñ:ã:wWÃ=VÍ«­ðƒ‰J¨ó»ß/&\ÛT­õ¢1”Äð‡åíÕmÅ`*Ÿ­ 5Ýrî~~q€R¨ W^’¼û¢àÀQ‘V@¤fX¯}øeðN�[~MQµÖ‹ÆDPã‰7ü‡(F×”D5ÝrÔaÜ=èOß9t«èÚ¿¦Ä_g-lôó僩ô;Ø¿øîâÌÂÍߪ¹^4‚Ðpô\‹Ö±}*z%ñô¼Ï‚©¶~~s#ÇoºuëÓïS‹ùŸÅ5áòŸQ⯳ü¾,¯-ÌRz¥^_¸¸¦$\KTÍõ¢ñ”Äеn4($uYÁN;Êh»¦[úžßtËñG�R.?3. v\†]ËðŸ�R Ë.Õ)˜Ê"Ò6üÚ’j®�‡  †‚¿æ"<‚”FÔò›nE=ðP}%ü~"áQ¸Âµ$.ÊÂ/V‰a¬á&[q£b¥½^4‚Ðpô<Š–AS+z%å×\l¶ÖO‚©6~s,Ô­0ÿ™%á&\þ2k]K"óÿöU0Õöö4ùÁ†‚-?àò”´×‹ÆCPP@~g÷ÅÒÕäÊÕœ(³ÕtËÑ38œp.¿ö¥Ö�Üåó¯¦ò_ü`Ã~ÄJÔ„ A @�{�‚2Ù®ÿG’¦[Ž2àþƒ]®p?]µ2möÇÁT[çóp‡ôJlÔeñ[yë?S3¯4׋ÆCPP@ø¿^·­6!iÓ-ÇÿŽ›×ÑKë(ØT‹ž‚îºûºÁTeTä×”L›ýI0Õ&üôößn³V0…Žˆ  ?­&N¥4Ýrô×§Â5áòŸ}Τ׊‚!?09qçn7§R 5ö7ïÚ׉úÏø}h.ÞÃv#“•JÇåŒÝ×£Ö¥”ñÌüÅ£gm¶(Ãî7Ýš4ëÃD5QM¸ü§Äû먵a“^¦‚€â°M‚wåQ`à×’\2í­¼NîΈÿšLµ­÷ŠƒºïÊ3þÈ_ÚàfÎðÌþ¡'È£¾”á?q¼÷«ä5Ýš<ë£`ª¸p.¿F"˧š«ÆÂžŠjƒ†ï½~ð®4úMCw[/x×öTûqÓßÞå›4kaÞpɇn¹†ÙcãUƒw¥Q � DTãâw¤Gý#((B™vW¢¦A.ãl›nÍúÐN'nÂåj´ì,†ö ºóU@8çíµ¾û›ÒjLÌ8ý_rµHú]}¯!²–ÄùÍfæÕ4Ýì¯Jî_¢Ú¿–eÄsó‚Ô?‚€¢j2�Êp‡)ó…J²¬%q\áwXÇÌ9o‡¢µ ®îø+ûr‰öËoþðb^ E}ZŽºõ¥Ü~ÔüãûÿÒü²]íFZÔ¿DÏrQS²•þiIû™ÕÂ|óý�õÛ™�æ[%cG¡i‘¡*Ã9„<%@r%%£ù€L”ÈA €L”ÈA €L”ÈÔ¡i1œ)PÎ!äqCHŽ!�£¦ A­Úòyî42¢74-Jy�Êpõ�óÍ‚š™"(�)‚™"(�)‚™"(�)‚™"(�)‚™"(�)‚™"(�)‚™"(�)‚™"(�)‚™"(�)‚™"(�©–à/ÐÐzôè±Gkk믃·ÎyÁ_üµ}÷µEîÞÞFmtbKKËÁÛ$®œ3gÎIÁ4€àPO8O3öÚTòÒ)aG¦�LRÝZµåsóqëJÁ;Ô•r/äœo5µÔ¢lÎ÷fÉà€zÄyš!‚’Ô°#Ó@& (]¥AIïÁö5÷ø˜¶¿%©aG¦�L�\¥rÎ7@ÖJRGGw™"(�)‚™"(�):ç¤�Ž·@rtt*²ÒrK™=6^ÕlôóåÍ2K.a~Ým%óâ‚ÿ5_|ýƒ™ÿ·¯ÍC¯~l>üßoƒoWNëÛ|íŸÚõýü§ËŸ.:…?üÒ®÷µÿ/øè@èèž:vdjœI:qçnf¥j�|Ò‹ íM¡ZtãÛï—]Ìþ›wÉ»ÉÓó>7÷<ÿ�¹ûù÷+¾ú¿©ÝtÛ^_Ù¿h0%@Y|œ·×úöº\Œî #þk®™4kaðIézoð³Eëûgû·�x`nªÁP×JRÇŽLC�3Ió.ÜÉtýÙrvú¨ /™[f,°ÓiÒòÇþf“D7¿Ï¿þÞ\2í-3jÚ›Á'¥óS)t3|ü�¿™«�_Õà )"(J¢šŠ±‡mlÝr�à“äTx4èÎWJº>nôóÌÿ¯{¢ë¿ÏÝ ®|tžùæûŸMŠ $uìÈ44YP²ùZ?5��ÜËÞÃtƒS~Ôÿ´Úžr””øT"8lÒëUiJ mûí6kïÚ~+µ4e"(S€ðàñ[µ»>ª0批ŸÚk²€e—jkƵ٢ë÷þ›u ¾ÕFÁÂoþð¢­Í(Fë™qú¿äÕŒk~ÍûúÂ/s×=}ï«,gkÑÃ÷m[ßq/Øù€¦EP’:vdš((Ñ�hÎðòn2ãþò޹ç¯Ø�£¥n€ï¿aÞÍKµ% Jåÿ&³ ”êõ꺒]ø&-º uëK5Yˆ¢æ ��Ô+xgÌNW<“·?P‚ ]c²UÞõX×{5“*T(¢k²®Í~AŠô÷ׂ×F­GR*˜;—üéMså#ó ªÁÑúük²jfvúý3&h^%©cô-äºûºy7ÀAw½b_á ¸j#tsÜfôçÕL¨oH¸ïI©&/ºiÿ¯¹±¯=¯}Ît;û1Ó2hª-ýóÛ0kÛïø«v7ch$ºŽÞõo=s×cºö© ªX-­®‰úž¾ï7£ÒòT ç¼½ÿ9/ é{ý_í5·X`¡~…º&ûR~ñ­ù懃wPA rt¸ý/‚wÆføUKRˆnŽÛŒž‘ LÔ„@�M­èfØ}ø_ÚÕ©?L±Î™P¯T¸â×<ìtÅÓ‰š_ùô}Íç]ŸÕL£ø×Ì“þcvÉëSM¹îª�Q@ãDP A r®8¨{îf¥ªweø“P)Úˆþ'xgl`SimI)l“­ /Ù¦ þÍ÷®Û<öæ[*Õ©fƽhº ZÔ'DM·uWß‘rh>Íï(Ðñ ŸÕȸZ)5 qtßð¯Å�A ,Uéû#»h•RLšõa®•�ZÖ–8*�ó;Ú+0Òð™ÐHÎÛ{ñuK×ÕrúéùÂCõêú.° ÷Ñó¿µ@PË/!ÓÍHAF)T*æ2Y5�R)�ß‘S}\Â7_+Ú>½Âí«UZ¨Ï‡/Ê„G°qóèå/S%šîó¨Î÷…hýIæÕúô�3v_ÏŒïÿKÛ´CÛ¨!;“¬³Üß  ¶t-ö¯ÇηÒZͯå8ºìÑ#¸ß׿�Zøën+SP%°4̯óâ»ÿ[ÖMпÔÌyšôÀ0G™ùðÍWy�¤¥×ÐÝÚjt¼h°Ï~¿«ý\5,û…2èn½üæi‡l¹zîs IiÛüevýÙ?ÿɧNû\²‹ýŽQGÁƒ¶QíÃµÝ T\‡Ø(åþfµ¥!v]‡ÓIPËñk?vÜ`•`ª�Öå_ÃÕœ·Ð5ÒFPKãÍ;åVÛë ëŽnfYÝÐtcõG‹ ß|} Ô)^7àr)=¸Ñ —r¢`É7Úæp?í?ÅQ ¢¡œUãQL¿@uhÈs'\{Q)89Q× ¿‰®c3ÏÚÎ^p�P %°ü&=oZÞCÃÃTúµ/µöÚ‡_SùMÓÂÔ�fàëØic Üs44qúÝ~iæïvîLæïúéíG9Óó ü¡�ÕYUC0»Îö«}Ä×ì08ª9ñkq¢¤ñ›T‡¤ý X?ȉºNèà&Ú^¨FÕÕÖ꣦žjJªÀ&i! CPË¿¿Þ¸æHµà—ˤ‹†²ÔÐÂz(¢:ËklþRšMøA…2ýÅj6\_QPÒX¥“nô=G>iNú㜼xô¹†lÖv»f¶ä°Mìt1•þféûùŠ‹¯W³ü=˜J‡žÈîD]Õ„KCúF�¼¨ï뚥¦ jꩦ¤ RT›Ò:¶�™sÞ6XIr½€(%°üI%A‰O䬸7ße—êLEÓ X£ÛøM¾J¥ÒE?0(öðÆc·_;˜j{B³ß‡G¢nøŽ2 ~[ï0'úŽ[†ú›ë°žÆo�>¿@ÿ.¤A4,F×]Oô0D\èú’„ Z¬¨V¥Øõ¢” �•–[2˜j\¥Dª�Hí3Þ ¦Œ9r›5ƒ©ö´mþðË~ŸQ³.·ý X’<EÍ<.ùÓ›Á»¶Î÷…¤õ›¤Ë/Ú°KºM`ýfºÅ $´*¸P3Q5Um­jUõ™šy¹kS¸ÀD×.õƒS� ”‚ –ßv¹’fW+ýÓâ€&i [5l¶ÖO‚)Ý\wÀÓ�7­í7ýíܲÔÎ:®Ó¹W3¥æRáš)¿�v)}<ãÓ`*?ó–æo�.?XØ|ítûkø×ÅÏ¿ú!˜JÆ Æ¡Ú5õT“O) Vº�øKÞåþcóŸHÅ”Àò3Æ¿øÙ²ÁTiÔìÀozÎl×R~‰`üÍ·PÀR*5µðÛbûM´|ÇnßÖÉ\n õ%?#¢‘ÃÔN;Ék÷�ƒ¹ýþ.…‚’ô~3€tùýáÒ,Ä¿.>=ï³`ªr*ÔRí«ÿðZÕ˜ì¿ÙσwPA ,?€(·£¢ßASÒ9¦~¦ü ¯!,íÀÉ€¤nÈá}©Z×�]û'ܱ\ß÷;uxW;í$/�†ã(CWJ™e° °i³? ¦Ú zÒªmвü뢿ž´¨I—_0ã×Ì@1%°üŽáå6ßJk¯J©y”ë“!¥>�¾jå mƒßwDü¾&×O7˜€64Ãïàžtˆñb4x†».jùþпizfÞÁTúÍÏ47‚X~“•äê“çÈ_/ÎpgUK¢›îuÞ©ŸÅßj ù×ý …¶màö¿°ÓÊ„‡5~�€ãžKRê‹ ñ¨O‰ú§9ºf”s=öéšîžM$~ÿ7Ÿ_˜“®AJAPë¡ÙçݤâúCÄQ³#¿ýpÔÃká®ë™×d*‹Q¦üiT{äF¡Ñ0™î¦¯LAÜè7~›rÚd�š�ºÚ]3î?v‹²Íç?»HËõ›™:úÞc'ýÚöO«„úÁ9³ ea%°Â7*•Εrºûº¹ï+C^ë‡ð©½ô]ÿ¶yÞó9ô`ÁBÏ÷¨&?(sÛó;¸/>8Ìêr±¡}4iØ]G5%z’ºßß, ]“5<¯?¢Ÿ–®%Ñ÷4Z–jTÔ?ÍNR)T³G�ÅCû#@1%Èñ«ôÝM*I`¢~®Y’Œø¯Å7ÓZP óúo¨½ôIÿ1;xW{jšåö¥nÒºY»ŒAÔØþ>¿”T™„¤%—ª•áÁe@sÐл~ᎆìä^¹�2Šq�Œ]Ôò´Üb4hÆŒÓÿ%ñºDëñﺧ9€æGP‚œpm‰2Òº1�K£C©†Â݈jQKâšD)³þØI½lI _‚¨L¿ÿ„ó,hÝnà9á‡%†)˜ÑÊ•\ 4”yPÓ íí�bOtPÿ~ó‡™yÒU°¡k²Îñ¸ç é;c³‰™yævyßÑuYË‹¢ëUßëÿš·.]S´®ûþÊöG‰ºh]º6Í9o‡¼û€úf} ÐxZ‚¿¨ÄF}ZíßÞƒíŸjSi¸?ÒU¹Tšï�+/.íúAˆ2ÉOÏûÜ<³è¥¿º9õê¶â¢›ÖÊy%iê#±ÓïŸ)«ÉTZ¿Ið*Ö�D7QÝÔ%jÄiÛ'˜jë€^¬§öÓ—ì¼k£}©'$£ã  �ßìBûÞ6½øâ[»�•áÐþïµèå!Ê|ô÷×à]›rsU<>¦íïkSË»þÔø|²¦Á;TE×…o¾ÿqÑ5£SÞõØ—äº(ºî¨éVܺ’P@²çµÏÑÉͯÒ{Ú¡¦y\‰™_̵ۡ2½ºY©fB%bºiù7@e¶Ë HÒ íÕ“…³èØGû$\kT¬–ÄÑqÐþôGèÒþVsŒ™gmg$ ? Ñ÷ãJC4&]×ôôtÕ‡éºà (ÂôýmFÿwâ뢮;ú®®¥ vJ¡ë�æÕÓÝ H”ƒ íØÀdÜ_•vé»*…ë>ü/5 H´.5�t×+ö&¨íÍ* *ÄB´¯ü £Õ<©FCÇA7üB\æCß×z4�ã L0¨ygÜõNŸ«ßˆ¾§ï—\ˆ–¡ë‰j„u�UáŠÖï_[tMÒgº¦é;ú®îP.ªœÒÐäÍIÔaR¥ñªÚïÕu%;:Ô_ÿ`‡®ÕPÂd‚kCÍÛt,Â5T“^\X4h©+4ß4:šo¥Ž™2I@r%€FGP’:šoÈA €L”ÈA €L”ÈA €L1ŒYÜ¥’«tH`²Æ�À©¡¦¨wÆüfƤ3ǼªIEND®B`‚umf/docs/config/0000755000000000000000000000000014544400200010721 5ustar umf/docs/config/Doxyfile0000644000000000000000000034043114544400200012434 0ustar # Doxyfile 1.9.1 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a double hash (##) is considered a comment and is placed in # front of the TAG it is preceding. # # All text after a single hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists, items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (\" \"). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the configuration # file that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # https://www.gnu.org/software/libiconv/ for the list of possible encodings. # The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded by # double-quotes, unless you are using Doxywizard) that should identify the # project for which the documentation is generated. This name is used in the # title of most generated pages and in a few other places. # The default value is: My Project. PROJECT_NAME = "Intel Unified Memory Framework" # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version # control system is used. PROJECT_NUMBER = # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = # With the PROJECT_LOGO tag one can specify a logo or an icon that is included # in the documentation. The maximum height of the logo should not exceed 55 # pixels and the maximum width should not exceed 200 pixels. Doxygen will copy # the logo to the output directory. PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. OUTPUT_DIRECTORY = # If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and # will distribute the generated files over these directories. Enabling this # option can be useful when feeding doxygen a huge amount of source files, where # putting all generated files in the same directory would otherwise causes # performance problems for the file system. # The default value is: NO. CREATE_SUBDIRS = NO # If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII # characters to appear in the names of generated files. If set to NO, non-ASCII # characters will be escaped, for example _xE3_x81_x84 will be used for Unicode # U+3044. # The default value is: NO. ALLOW_UNICODE_NAMES = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, # Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), # Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, # Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), # Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, # Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, # Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, # Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English # The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all generated output in the proper direction. # Possible values are: None, LTR, RTL and Context. # The default value is: None. OUTPUT_TEXT_DIRECTION = None # If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. # The default value is: YES. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief # description of a member or function before the detailed description # # Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. # The default value is: YES. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator that is # used to form the text in various listings. Each string in this list, if found # as the leading text of the brief description, will be stripped from the text # and the result, after processing the whole list, is used as the annotated # text. Otherwise, the brief description is used as-is. If left blank, the # following values are used ($name is automatically replaced with the name of # the entity):The $name class, The $name widget, The $name file, is, provides, # specifies, contains, represents, a, an and the. ABBREVIATE_BRIEF = "The $name class" \ "The $name widget" \ "The $name file" \ is \ provides \ specifies \ contains \ represents \ a \ an \ the # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # doxygen will generate a detailed section even if there is only a brief # description. # The default value is: NO. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. # The default value is: NO. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path # before files name in the file list and in the header files. If set to NO the # shortest path that makes the file name unique will be used # The default value is: YES. FULL_PATH_NAMES = YES # The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. # Stripping is only done if one of the specified strings matches the left-hand # part of the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the path to # strip. # # Note that you can specify absolute paths here, but also relative paths, which # will be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which # header file to include in order to use a class. If left blank only the name of # the header file containing the class definition is used. Otherwise one should # specify the list of include paths that are normally passed to the compiler # using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but # less readable) file names. This can be useful is your file systems doesn't # support long names like on DOS, Mac, or CD-ROM. # The default value is: NO. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the # first line (until the first dot) of a Javadoc-style comment as the brief # description. If set to NO, the Javadoc-style will behave just like regular Qt- # style comments (thus requiring an explicit @brief command for a brief # description.) # The default value is: NO. JAVADOC_AUTOBRIEF = NO # If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line # such as # /*************** # as being the beginning of a Javadoc-style comment "banner". If set to NO, the # Javadoc-style will behave just like regular comments and it will not be # interpreted by doxygen. # The default value is: NO. JAVADOC_BANNER = NO # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first # line (until the first dot) of a Qt-style comment as the brief description. If # set to NO, the Qt-style will behave just like regular Qt-style comments (thus # requiring an explicit \brief command for a brief description.) # The default value is: NO. QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a # multi-line C++ special comment block (i.e. a block of //! or /// comments) as # a brief description. This used to be the default behavior. The new default is # to treat a multi-line C++ comment block as a detailed description. Set this # tag to YES if you prefer the old behavior instead. # # Note that setting this tag to YES also means that rational rose comments are # not recognized any more. # The default value is: NO. MULTILINE_CPP_IS_BRIEF = NO # By default Python docstrings are displayed as preformatted text and doxygen's # special commands cannot be used. By setting PYTHON_DOCSTRING to NO the # doxygen's special commands can be used and the contents of the docstring # documentation blocks is shown as doxygen documentation. # The default value is: YES. PYTHON_DOCSTRING = YES # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # documentation from any documented member that it re-implements. # The default value is: YES. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new # page for each member. If set to NO, the documentation of a member will be part # of the file/class/namespace that contains it. # The default value is: NO. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen # uses this value to replace tabs by spaces in code fragments. # Minimum value: 1, maximum value: 16, default value: 4. TAB_SIZE = 4 # This tag can be used to specify a number of aliases that act as commands in # the documentation. An alias has the form: # name=value # For example adding # "sideeffect=@par Side Effects:\n" # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading # "Side Effects:". You can put \n's in the value part of an alias to insert # newlines (in the resulting output). You can put ^^ in the value part of an # alias to insert a newline as if a physical newline was in the original file. # When you need a literal { or } or , in the value part of an alias you have to # escape them by means of a backslash (\), this can lead to conflicts with the # commands \{ and \} for these it is advised to use the version @{ and @} or use # a double escape (\\{ and \\}) ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For # instance, some of the names that are used will be different. The list of all # members will be omitted, etc. # The default value is: NO. OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or # Python sources only. Doxygen will then generate output that is more tailored # for that language. For instance, namespaces will be presented as packages, # qualified scopes will look different, etc. # The default value is: NO. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources. Doxygen will then generate output that is tailored for Fortran. # The default value is: NO. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for VHDL. # The default value is: NO. OPTIMIZE_OUTPUT_VHDL = NO # Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice # sources only. Doxygen will then generate output that is more tailored for that # language. For instance, namespaces will be presented as modules, types will be # separated into more groups, etc. # The default value is: NO. OPTIMIZE_OUTPUT_SLICE = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and # language is one of the parsers supported by doxygen: IDL, Java, JavaScript, # Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL, # Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: # FortranFree, unknown formatted Fortran: Fortran. In the later case the parser # tries to guess whether the code is fixed or free formatted code, this is the # default for Fortran type files). For instance to make doxygen treat .inc files # as Fortran files (default is PHP), and .f files as C (default is Fortran), # use: inc=Fortran f=C. # # Note: For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise # the files are not read by doxygen. When specifying no_extension you should add # * to the FILE_PATTERNS. # # Note see also the list of default file extension mappings. EXTENSION_MAPPING = # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable # documentation. See https://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you can # mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. # The default value is: YES. MARKDOWN_SUPPORT = YES # When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up # to that level are automatically included in the table of contents, even if # they do not have an id attribute. # Note: This feature currently applies only to Markdown headings. # Minimum value: 0, maximum value: 99, default value: 5. # This tag requires that the tag MARKDOWN_SUPPORT is set to YES. TOC_INCLUDE_HEADINGS = 5 # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by putting a % sign in front of the word or # globally by setting AUTOLINK_SUPPORT to NO. # The default value is: YES. AUTOLINK_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should set this # tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); # versus func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. # The default value is: NO. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. # The default value is: NO. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: # https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen # will parse them like normal C++ but will assume all classes use public instead # of private inheritance when no explicit protection keyword is present. # The default value is: NO. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES will make # doxygen to replace the get and set methods by a property in the documentation. # This will only work if the methods are indeed getting or setting a simple # type. If this is not the case, or you want to show the methods anyway, you # should set this option to NO. # The default value is: YES. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. # The default value is: NO. DISTRIBUTE_GROUP_DOC = NO # If one adds a struct or class to a group and this option is enabled, then also # any nested class or struct is added to the same group. By default this option # is disabled and one has to add nested compounds explicitly via \ingroup. # The default value is: NO. GROUP_NESTED_COMPOUNDS = NO # Set the SUBGROUPING tag to YES to allow class member groups of the same type # (for instance a group of public functions) to be put as a subgroup of that # type (e.g. under the Public Functions section). Set it to NO to prevent # subgrouping. Alternatively, this can be done per class using the # \nosubgrouping command. # The default value is: YES. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions # are shown inside the group in which they are included (e.g. using \ingroup) # instead of on a separate page (for HTML and Man pages) or section (for LaTeX # and RTF). # # Note that this feature does not work in combination with # SEPARATE_MEMBER_PAGES. # The default value is: NO. INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions # with only public data fields or simple typedef fields will be shown inline in # the documentation of the scope in which they are defined (i.e. file, # namespace, or group documentation), provided this scope is documented. If set # to NO, structs, classes, and unions are shown on a separate page (for HTML and # Man pages) or section (for LaTeX and RTF). # The default value is: NO. INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or # enum is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically be # useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. # The default value is: NO. TYPEDEF_HIDES_STRUCT = YES # The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This # cache is used to resolve symbols given their name and scope. Since this can be # an expensive process and often the same symbol appears multiple times in the # code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small # doxygen will become slower. If the cache is too large, memory is wasted. The # cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range # is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 # symbols. At the end of a run doxygen will report the cache usage and suggest # the optimal cache size from a speed point of view. # Minimum value: 0, maximum value: 9, default value: 0. LOOKUP_CACHE_SIZE = 0 # The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use # during processing. When set to 0 doxygen will based this on the number of # cores available in the system. You can set it explicitly to a value larger # than 0 to get more control over the balance between CPU load and processing # speed. At this moment only the input processing can be done using multiple # threads. Since this is still an experimental feature the default is set to 1, # which efficively disables parallel processing. Please report any issues you # encounter. Generating dot graphs in parallel is controlled by the # DOT_NUM_THREADS setting. # Minimum value: 0, maximum value: 32, default value: 1. NUM_PROC_THREADS = 1 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in # documentation are documented, even if no documentation was available. Private # class members and static file members will be hidden unless the # EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. # Note: This will also disable the warnings about undocumented members that are # normally produced when WARNINGS is set to YES. # The default value is: NO. EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will # be included in the documentation. # The default value is: NO. EXTRACT_PRIVATE = NO # If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual # methods of a class will be included in the documentation. # The default value is: NO. EXTRACT_PRIV_VIRTUAL = NO # If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal # scope will be included in the documentation. # The default value is: NO. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES, all static members of a file will be # included in the documentation. # The default value is: NO. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined # locally in source files will be included in the documentation. If set to NO, # only classes defined in header files are included. Does not have any effect # for Java sources. # The default value is: YES. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. If set to YES, local methods, # which are defined in the implementation section but not in the interface are # included in the documentation. If set to NO, only methods in the interface are # included. # The default value is: NO. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base name of # the file that contains the anonymous namespace. By default anonymous namespace # are hidden. # The default value is: NO. EXTRACT_ANON_NSPACES = NO # If this flag is set to YES, the name of an unnamed parameter in a declaration # will be determined by the corresponding definition. By default unnamed # parameters remain unnamed in the output. # The default value is: YES. RESOLVE_UNNAMED_PARAMS = YES # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation # section is generated. This option has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set # to NO, these classes will be included in the various overviews. This option # has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend # declarations. If set to NO, these declarations will be included in the # documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any # documentation blocks found inside the body of a function. If set to NO, these # blocks will be appended to the function's detailed documentation block. # The default value is: NO. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation that is typed after a # \internal command is included. If the tag is set to NO then the documentation # will be excluded. Set it to YES to include the internal documentation. # The default value is: NO. INTERNAL_DOCS = NO # With the correct setting of option CASE_SENSE_NAMES doxygen will better be # able to match the capabilities of the underlying filesystem. In case the # filesystem is case sensitive (i.e. it supports files in the same directory # whose names only differ in casing), the option must be set to YES to properly # deal with such files in case they appear in the input. For filesystems that # are not case sensitive the option should be be set to NO to properly deal with # output files written for symbols that only differ in casing, such as for two # classes, one named CLASS and the other named Class, and to also support # references to files without having to specify the exact matching casing. On # Windows (including Cygwin) and MacOS, users should typically set this option # to NO, whereas on Linux or other Unix flavors it should typically be set to # YES. # The default value is: system dependent. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with # their full class and namespace scopes in the documentation. If set to YES, the # scope will be hidden. # The default value is: NO. HIDE_SCOPE_NAMES = NO # If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will # append additional text to a page's title, such as Class Reference. If set to # YES the compound reference will be hidden. # The default value is: NO. HIDE_COMPOUND_REFERENCE= NO # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. SHOW_INCLUDE_FILES = YES # If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each # grouped member an include statement to the documentation, telling the reader # which file to include in order to use the member. # The default value is: NO. SHOW_GROUPED_MEMB_INC = NO # If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include # files with double quotes in the documentation rather than with sharp brackets. # The default value is: NO. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the # documentation for inline members. # The default value is: YES. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the # (detailed) documentation of file and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. # The default value is: YES. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. Note that # this will also influence the order of the classes in the class list. # The default value is: NO. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the # (brief and detailed) documentation of class members so that constructors and # destructors are listed first. If set to NO the constructors will appear in the # respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. # Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief # member documentation. # Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting # detailed member documentation. # The default value is: NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy # of group names into alphabetical order. If set to NO the group names will # appear in their defined order. # The default value is: NO. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by # fully-qualified names, including namespaces. If set to NO, the class list will # be sorted only by class name, not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the alphabetical # list. # The default value is: NO. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper # type resolution of all parameters of a function it will reject a match between # the prototype and the implementation of a member function even if there is # only one candidate or it is obvious which candidate to choose by doing a # simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still # accept a match between prototype and implementation in such cases. # The default value is: NO. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo # list. This list is created by putting \todo commands in the documentation. # The default value is: YES. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test # list. This list is created by putting \test commands in the documentation. # The default value is: YES. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug # list. This list is created by putting \bug commands in the documentation. # The default value is: YES. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) # the deprecated list. This list is created by putting \deprecated commands in # the documentation. # The default value is: YES. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional documentation # sections, marked by \if ... \endif and \cond # ... \endcond blocks. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the # initial value of a variable or macro / define can have for it to appear in the # documentation. If the initializer consists of more lines than specified here # it will be hidden. Use a value of 0 to hide initializers completely. The # appearance of the value of individual variables and macros / defines can be # controlled using \showinitializer or \hideinitializer command in the # documentation regardless of this setting. # Minimum value: 0, maximum value: 10000, default value: 30. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated at # the bottom of the documentation of classes and structs. If set to YES, the # list will mention the files that were used to generate the documentation. # The default value is: YES. SHOW_USED_FILES = YES # Set the SHOW_FILES tag to NO to disable the generation of the Files page. This # will remove the Files entry from the Quick Index and from the Folder Tree View # (if specified). # The default value is: YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces # page. This will remove the Namespaces entry from the Quick Index and from the # Folder Tree View (if specified). # The default value is: YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command command input-file, where command is the value of the # FILE_VERSION_FILTER tag, and input-file is the name of an input file provided # by doxygen. Whatever the program writes to standard output is used as the file # version. For an example see the documentation. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. You can # optionally specify a file name after the option, if omitted DoxygenLayout.xml # will be used as the name of the layout file. # # Note that if you run doxygen from a directory containing a file called # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE # tag is left empty. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool # to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. See also \cite for info how to create references. CITE_BIB_FILES = #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated to # standard output by doxygen. If QUIET is set to YES this implies that the # messages are off. # The default value is: NO. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated to standard error (stderr) by doxygen. If WARNINGS is set to YES # this implies that the warnings are on. # # Tip: Turn warnings on while writing the documentation. # The default value is: YES. WARNINGS = YES # If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: YES. WARN_IF_UNDOCUMENTED = YES # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some parameters # in a documented function, or documenting parameters that don't exist or using # markup commands wrongly. # The default value is: YES. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return # value. If set to NO, doxygen will only warn about wrong or incomplete # parameter documentation, but not about the absence of documentation. If # EXTRACT_ALL is set to YES then this flag will automatically be disabled. # The default value is: NO. WARN_NO_PARAMDOC = YES # If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when # a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS # then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but # at the end of the doxygen process doxygen will return with a non-zero status. # Possible values are: NO, YES and FAIL_ON_WARNINGS. # The default value is: NO. WARN_AS_ERROR = FAIL_ON_WARNINGS # The WARN_FORMAT tag determines the format of the warning messages that doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated # and the warning text. Optionally the format may contain $version, which will # be replaced by the version of the file (if it could be obtained via # FILE_VERSION_FILTER) # The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning and error # messages should be written. If left blank the output is written to standard # error (stderr). WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag is used to specify the files and/or directories that contain # documented source files. You may enter file names like myfile.cpp or # directories like /usr/src/myproject. Separate the files or directories with # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. INPUT = ../include # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv # documentation (see: # https://www.gnu.org/software/libiconv/) for the list of possible encodings. # The default value is: UTF-8. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and # *.h) to filter out the source-files in the directories. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # read by doxygen. # # Note the list of default checked file patterns might differ from the list of # default file extension mappings. # # If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, # *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, # *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, # *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment), # *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, *.vhdl, # *.ucf, *.qsf and *.ice. FILE_PATTERNS = *.h # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. # The default value is: NO. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. # The default value is: NO. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories use the pattern */test/* EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include # command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank all # files are included. EXAMPLE_PATTERNS = * # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude commands # irrespective of the value of the RECURSIVE tag. # The default value is: NO. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or directories # that contain images that are to be included in the documentation (see the # \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command: # # # # where is the value of the INPUT_FILTER tag, and is the # name of an input file. Doxygen will then use the output that the filter # program writes to standard output. If FILTER_PATTERNS is specified, this tag # will be ignored. # # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: pattern=filter # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how # filters are used. If the FILTER_PATTERNS tag is empty or if none of the # patterns match the file name, INPUT_FILTER is applied. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will also be used to filter the input files that are used for # producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). # The default value is: NO. FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) and # it is also possible to disable source filtering for a specific pattern using # *.ext= (so without naming a filter). # This tag requires that the tag FILTER_SOURCE_FILES is set to YES. FILTER_SOURCE_PATTERNS = # If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the doxygen output. USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will be # generated. Documented entities will be cross-referenced with these sources. # # Note: To get rid of all source code in the generated output, make sure that # also VERBATIM_HEADERS is set to NO. # The default value is: NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body of functions, # classes and enums directly into the documentation. # The default value is: NO. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any # special comment blocks from generated source code fragments. Normal C, C++ and # Fortran comments will always remain visible. # The default value is: YES. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES then for each documented # entity all documented functions referencing it will be listed. # The default value is: NO. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES then for each documented function # all documented entities called/used by that function will be listed. # The default value is: NO. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set # to YES then the hyperlinks from functions in REFERENCES_RELATION and # REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will # link to the documentation. # The default value is: YES. REFERENCES_LINK_SOURCE = YES # If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the # source code will show a tooltip with additional information such as prototype, # brief description and links to the definition and documentation. Since this # will make the HTML file larger and loading of large files a bit slower, you # can opt to disable this feature. # The default value is: YES. # This tag requires that the tag SOURCE_BROWSER is set to YES. SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will # point to the HTML generated by the htags(1) tool instead of doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system # (see https://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. # # To use it do the following: # - Install the latest version of global # - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file # - Make sure the INPUT points to the root of the source tree # - Run doxygen as normal # # Doxygen will invoke htags (and that will in turn invoke gtags), so these # tools must be available from the command line (i.e. in the search path). # # The result: instead of the source browser generated by doxygen, the links to # source code will now point to the output of htags. # The default value is: NO. # This tag requires that the tag SOURCE_BROWSER is set to YES. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a # verbatim copy of the header file for each class for which an include is # specified. Set to NO to disable this. # See also: Section \class. # The default value is: YES. VERBATIM_HEADERS = YES # If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the # clang parser (see: # http://clang.llvm.org/) for more accurate parsing at the cost of reduced # performance. This can be particularly helpful with template rich C++ code for # which doxygen's built-in parser lacks the necessary type information. # Note: The availability of this option depends on whether or not doxygen was # generated with the -Duse_libclang=ON option for CMake. # The default value is: NO. CLANG_ASSISTED_PARSING = NO # If clang assisted parsing is enabled and the CLANG_ADD_INC_PATHS tag is set to # YES then doxygen will add the directory of each input to the include path. # The default value is: YES. CLANG_ADD_INC_PATHS = YES # If clang assisted parsing is enabled you can provide the compiler with command # line options that you would normally use when invoking the compiler. Note that # the include paths will already be set by doxygen for the files and directories # specified with INPUT and INCLUDE_PATH. # This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. CLANG_OPTIONS = # If clang assisted parsing is enabled you can provide the clang parser with the # path to the directory containing a file called compile_commands.json. This # file is the compilation database (see: # http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the # options used when the source files were built. This is equivalent to # specifying the -p option to a clang tool, such as clang-check. These options # will then be passed to the parser. Any options specified with CLANG_OPTIONS # will be added as well. # Note: The availability of this option depends on whether or not doxygen was # generated with the -Duse_libclang=ON option for CMake. CLANG_DATABASE_PATH = #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all # compounds will be generated. Enable this if the project contains a lot of # classes, structs, unions or interfaces. # The default value is: YES. ALPHABETICAL_INDEX = YES # In case all classes in a project start with a common prefix, all classes will # be put under the same header in the alphabetical index. The IGNORE_PREFIX tag # can be used to specify a prefix (or a list of prefixes) that should be ignored # while generating the index headers. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output # The default value is: YES. GENERATE_HTML = NO # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of # it. # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). # The default value is: .html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a user-defined HTML header file for # each generated HTML page. If the tag is left blank doxygen will generate a # standard header. # # To get valid HTML the header file that includes any scripts and style sheets # that doxygen needs, which is dependent on the configuration options used (e.g. # the setting GENERATE_TREEVIEW). It is highly recommended to start with a # default header using # doxygen -w html new_header.html new_footer.html new_stylesheet.css # YourConfigFile # and then modify the file new_header.html. See also section "Doxygen usage" # for information on how to generate the default header that doxygen normally # uses. # Note: The header is subject to change so you typically have to regenerate the # default header when upgrading to a newer version of doxygen. For a description # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each # generated HTML page. If the tag is left blank doxygen will generate a standard # footer. See HTML_HEADER for more information on how to generate a default # footer and what special commands can be used inside the footer. See also # section "Doxygen usage" for information on how to generate the default footer # that doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # sheet that is used by each HTML page. It can be used to fine-tune the look of # the HTML output. If left blank doxygen will generate a default style sheet. # See also section "Doxygen usage" for information on how to generate the style # sheet that doxygen normally uses. # Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as # it is more robust and this tag (HTML_STYLESHEET) will in the future become # obsolete. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined # cascading style sheets that are included after the standard style sheets # created by doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the # standard style sheet and is therefore more robust against future updates. # Doxygen will copy the style sheet files to the output directory. # Note: The order of the extra style sheet files is of importance (e.g. the last # style sheet in the list overrules the setting of the previous ones in the # list). For an example see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that the # files will be copied as-is; there are no commands or markers available. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the style sheet and background images according to # this color. Hue is specified as an angle on a colorwheel, see # https://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors # in the HTML output. For a value of 0 the output will use grayscales only. A # value of 255 will produce the most vivid colors. # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the # luminance component of the colors in the HTML output. Values below 100 # gradually make the output lighter, whereas values above 100 make the output # darker. The value divided by 100 is the actual gamma applied, so 80 represents # a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not # change the gamma. # Minimum value: 40, maximum value: 240, default value: 80. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting this # to YES can help to show when doxygen was last run and thus if the # documentation is up to date. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_TIMESTAMP = NO # If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML # documentation will contain a main index with vertical navigation menus that # are dynamically created via JavaScript. If disabled, the navigation index will # consists of multiple levels of tabs that are statically embedded in every HTML # page. Disable this option to support browsers that do not have JavaScript, # like the Qt help browser. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_MENUS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_SECTIONS = NO # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries # shown in the various tree structured indices initially; the user can expand # and collapse entries dynamically later on. Doxygen will expand the tree to # such a level that at most the specified number of entries are visible (unless # a fully collapsed tree already exceeds this amount). So setting the number of # entries 1 will produce a full collapsed tree by default. 0 is a special value # representing an infinite number of entries and will result in a full expanded # tree by default. # Minimum value: 0, maximum value: 9999, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development # environment (see: # https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To # create a documentation set, doxygen will generate a Makefile in the HTML # output directory. Running make will produce the docset in that directory and # running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at # startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy # genXcode/_index.html for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_DOCSET = NO # This tag determines the name of the docset feed. A documentation feed provides # an umbrella under which multiple documentation sets from a single provider # (such as a company or product suite) can be grouped. # The default value is: Doxygen generated docs. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_FEEDNAME = "Doxygen generated docs" # This tag specifies a string that should uniquely identify the documentation # set bundle. This should be a reverse domain-name style string, e.g. # com.mycompany.MyDocSet. Doxygen will append .docset to the name. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_BUNDLE_ID = org.doxygen.Project # The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. # The default value is: org.doxygen.Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. # The default value is: Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop # (see: # https://www.microsoft.com/en-us/download/details.aspx?id=21138) on Windows. # # The HTML Help Workshop contains a compiler that can convert all HTML output # generated by doxygen into a single compiled HTML file (.chm). Compiled HTML # files are now used as the Windows 98 help format, and will replace the old # Windows help format (.hlp) on all Windows platforms in the future. Compressed # HTML files also contain an index, a table of contents, and you can search for # words in the documentation. The HTML workshop also contains a viewer for # compressed HTML files. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_HTMLHELP = NO # The CHM_FILE tag can be used to specify the file name of the resulting .chm # file. You can add a path in front of the file if the result should not be # written to the html output directory. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_FILE = # The HHC_LOCATION tag can be used to specify the location (absolute path # including file name) of the HTML help compiler (hhc.exe). If non-empty, # doxygen will try to run the HTML help compiler on the generated index.hhp. # The file has to be specified with full path. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. HHC_LOCATION = # The GENERATE_CHI flag controls if a separate .chi index file is generated # (YES) or that it should be included in the main .chm file (NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. GENERATE_CHI = NO # The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) # and project file content. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_INDEX_ENCODING = # The BINARY_TOC flag controls whether a binary table of contents is generated # (YES) or a normal table of contents (NO) in the .chm file. Furthermore it # enables the Previous and Next buttons. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members to # the table of contents of the HTML help documentation and to the tree view. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that # can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help # (.qch) of the generated HTML documentation. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify # the file name of the resulting .qch file. The path specified is relative to # the HTML output folder. # This tag requires that the tag GENERATE_QHP is set to YES. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace # (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual # Folders (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom # Filters (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom # Filters (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = # The QHG_LOCATION tag can be used to specify the location (absolute path # including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to # run qhelpgenerator on the generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be # generated, together with the HTML files, they form an Eclipse help plugin. To # install this plugin and make it available under the help contents menu in # Eclipse, the contents of the directory containing the HTML and XML files needs # to be copied into the plugins directory of eclipse. The name of the directory # within the plugins directory should be the same as the ECLIPSE_DOC_ID value. # After copying Eclipse needs to be restarted before the help appears. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_ECLIPSEHELP = NO # A unique identifier for the Eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have this # name. Each documentation set should have its own identifier. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. ECLIPSE_DOC_ID = org.doxygen.Project # If you want full control over the layout of the generated HTML pages it might # be necessary to disable the index and replace it with your own. The # DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top # of each HTML page. A value of NO enables the index and the value YES disables # it. Since the tabs in the index contain the same information as the navigation # tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. If the tag # value is set to YES, a side panel will be generated containing a tree-like # index structure (just like the one that is generated for HTML Help). For this # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the # HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can # further fine-tune the look of the index. As an example, the default style # sheet generated by doxygen has an example that shows how to put an image at # the root of the tree instead of the PROJECT_NAME. Since the tree basically has # the same information as the tab index, you could consider setting # DISABLE_INDEX to YES when enabling this option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. # # Note that a value of 0 will completely suppress the enum values from appearing # in the overview section. # Minimum value: 0, maximum value: 20, default value: 4. # This tag requires that the tag GENERATE_HTML is set to YES. ENUM_VALUES_PER_LINE = 4 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used # to set the initial width (in pixels) of the frame in which the tree is shown. # Minimum value: 0, maximum value: 1500, default value: 250. # This tag requires that the tag GENERATE_HTML is set to YES. TREEVIEW_WIDTH = 250 # If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to # external symbols imported via tag files in a separate window. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. EXT_LINKS_IN_WINDOW = NO # If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg # tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see # https://inkscape.org) to generate formulas as SVG images instead of PNGs for # the HTML output. These images will generally look nicer at scaled resolutions. # Possible values are: png (the default) and svg (looks nicer but requires the # pdf2svg or inkscape tool). # The default value is: png. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FORMULA_FORMAT = png # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful # doxygen run you need to manually remove any form_*.png images from the HTML # output directory to force them to be regenerated. # Minimum value: 8, maximum value: 50, default value: 10. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANSPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are not # supported properly for IE 6.0, but are supported on all modern browsers. # # Note that when changing this option you need to delete any form_*.png files in # the HTML output directory before the changes have effect. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_TRANSPARENT = YES # The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands # to create new LaTeX commands to be used in formulas as building blocks. See # the section "Including formulas" for details. FORMULA_MACROFILE = # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see # https://www.mathjax.org) which uses client side JavaScript for the rendering # instead of using pre-rendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path # to it using the MATHJAX_RELPATH option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. USE_MATHJAX = NO # When MathJax is enabled you can set the default output format to be used for # the MathJax output. See the MathJax site (see: # http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. # Possible values are: HTML-CSS (which is slower, but has the best # compatibility), NativeMML (i.e. MathML) and SVG. # The default value is: HTML-CSS. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_FORMAT = HTML-CSS # When MathJax is enabled you need to specify the location relative to the HTML # output directory using the MATHJAX_RELPATH option. The destination directory # should contain the MathJax.js script. For instance, if the mathjax directory # is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of # MathJax from https://www.mathjax.org before deployment. # The default value is: https://cdn.jsdelivr.net/npm/mathjax@2. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = https://cdn.jsdelivr.net/npm/mathjax@2 # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site # (see: # http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_CODEFILE = # When the SEARCHENGINE tag is enabled doxygen will generate a search box for # the HTML output. The underlying search engine uses javascript and DHTML and # should work on any modern browser. Note that when using HTML help # (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) # there is already a search function so this one should typically be disabled. # For large projects the javascript based search engine can be slow, then # enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to # search using the keyboard; to jump to the search box use + S # (what the is depends on the OS and browser, but it is typically # , /