From 683662e630b855947b7e083ca6cde0cfe7694687 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Tue, 27 Nov 2018 17:13:54 +0100 Subject: Add cross-platform NUMA library Makes it simple to use NUMA libraries on various platforms. --- intern/numaapi/AUTHORS | 1 + intern/numaapi/CMakeLists.txt | 39 ++++ intern/numaapi/LICENSE | 19 ++ intern/numaapi/README | 7 + intern/numaapi/README.blender | 5 + intern/numaapi/include/numaapi.h | 108 ++++++++++ intern/numaapi/source/build_config.h | 379 ++++++++++++++++++++++++++++++++++ intern/numaapi/source/numaapi.c | 37 ++++ intern/numaapi/source/numaapi_linux.c | 272 ++++++++++++++++++++++++ intern/numaapi/source/numaapi_stub.c | 82 ++++++++ intern/numaapi/source/numaapi_win32.c | 253 +++++++++++++++++++++++ 11 files changed, 1202 insertions(+) create mode 100644 intern/numaapi/AUTHORS create mode 100644 intern/numaapi/CMakeLists.txt create mode 100644 intern/numaapi/LICENSE create mode 100644 intern/numaapi/README create mode 100644 intern/numaapi/README.blender create mode 100644 intern/numaapi/include/numaapi.h create mode 100644 intern/numaapi/source/build_config.h create mode 100644 intern/numaapi/source/numaapi.c create mode 100644 intern/numaapi/source/numaapi_linux.c create mode 100644 intern/numaapi/source/numaapi_stub.c create mode 100644 intern/numaapi/source/numaapi_win32.c (limited to 'intern/numaapi') diff --git a/intern/numaapi/AUTHORS b/intern/numaapi/AUTHORS new file mode 100644 index 00000000000..a824c03d9ff --- /dev/null +++ b/intern/numaapi/AUTHORS @@ -0,0 +1 @@ +Sergey Sharybin diff --git a/intern/numaapi/CMakeLists.txt b/intern/numaapi/CMakeLists.txt new file mode 100644 index 00000000000..587a00b9514 --- /dev/null +++ b/intern/numaapi/CMakeLists.txt @@ -0,0 +1,39 @@ +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ***** END GPL LICENSE BLOCK ***** + +set(INC + ./include +) + +set(INC_SYS + +) + +set(SRC + source/numaapi.c + source/numaapi_linux.c + source/numaapi_stub.c + source/numaapi_win32.c + + include/numaapi.h + source/build_config.h +) + +add_definitions(-DWITH_DYNLOAD) + +blender_add_lib(bf_intern_numaapi "${SRC}" "${INC}" "${INC_SYS}") diff --git a/intern/numaapi/LICENSE b/intern/numaapi/LICENSE new file mode 100644 index 00000000000..3562d7ac274 --- /dev/null +++ b/intern/numaapi/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2016 libnumaapi authors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +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 OR COPYRIGHT HOLDERS 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. diff --git a/intern/numaapi/README b/intern/numaapi/README new file mode 100644 index 00000000000..a510ff12548 --- /dev/null +++ b/intern/numaapi/README @@ -0,0 +1,7 @@ +LibNumaAPI is aimed to provide one common cross-platform API for all +possible platforms, so cross-platform applications might not worry +about implementation details. + +LICENSE + +LibNumaAPI library is released under the MIT license. diff --git a/intern/numaapi/README.blender b/intern/numaapi/README.blender new file mode 100644 index 00000000000..661073712b9 --- /dev/null +++ b/intern/numaapi/README.blender @@ -0,0 +1,5 @@ +Project: LibNumaAPI +URL: https://github.com/Nazg-Gul/libNumaAPI +License: MIT +Upstream version: f83d41ec4d7 +Local modifications: None diff --git a/intern/numaapi/include/numaapi.h b/intern/numaapi/include/numaapi.h new file mode 100644 index 00000000000..a4f32d88458 --- /dev/null +++ b/intern/numaapi/include/numaapi.h @@ -0,0 +1,108 @@ +// Copyright (c) 2016, libnumaapi authors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// 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 OR COPYRIGHT HOLDERS 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. +// +// Author: Sergey Sharybin (sergey.vfx@gmail.com) + +#ifndef __LIBNUMAAPI_H__ +#define __LIBNUMAAPI_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define NUMAAPI_VERSION_MAJOR 1 +#define NUMAAPI_VERSION_MINOR 0 + +typedef enum NUMAAPI_Result { + NUMAAPI_SUCCESS = 0, + // NUMA is not available on this platform. + NUMAAPI_NOT_AVAILABLE = 1, + // Generic error, no real details are available, + NUMAAPI_ERROR = 2, + // Error installing atexit() handlers. + NUMAAPI_ERROR_ATEXIT = 3, +} NUMAAPI_Result; + +//////////////////////////////////////////////////////////////////////////////// +// Initialization. + +// Initialize NUMA API. +// +// This is first call which should be called before any other NUMA functions +// can be used. +NUMAAPI_Result numaAPI_Initialize(void); + +// Get string representation of NUMAPIResult. +const char* numaAPI_ResultAsString(NUMAAPI_Result result); + +//////////////////////////////////////////////////////////////////////////////// +// Topology query. + +// Get number of available nodes. +// +// This is in fact an index of last node plus one and it's not guaranteed +// that all nodes up to this one are available. +int numaAPI_GetNumNodes(void); + +// Returns truth if the given node is available for compute. +bool numaAPI_IsNodeAvailable(int node); + +// Getnumber of available processors on a given node. +int numaAPI_GetNumNodeProcessors(int node); + +//////////////////////////////////////////////////////////////////////////////// +// Affinities. + +// Runs the current process and its children on a specific node. +// +// Returns truth if affinity has successfully changed. +// +// NOTE: This function can not change active CPU group. Mainly designed to deal +// with Threadripper 2 topology, to make it possible to gain maximum performance +// for the main application thread. +bool numaAPI_RunProcessOnNode(int node); + +// Runs the current thread and its children on a specific node. +// +// Returns truth if affinity has successfully changed. +bool numaAPI_RunThreadOnNode(int node); + +//////////////////////////////////////////////////////////////////////////////// +// Memory management. + +// Allocate memory on a given node, +void* numaAPI_AllocateOnNode(size_t size, int node); + +// Allocate memory in the local memory, closest to the current node. +void* numaAPI_AllocateLocal(size_t size); + +// Frees size bytes of memory starting at start. +// +// TODO(sergey): Consider making it regular free() semantic. +void numaAPI_Free(void* start, size_t size); + +#ifdef __cplusplus +} +#endif + +#endif // __LIBNUMAAPI_H__ diff --git a/intern/numaapi/source/build_config.h b/intern/numaapi/source/build_config.h new file mode 100644 index 00000000000..444adcc0c71 --- /dev/null +++ b/intern/numaapi/source/build_config.h @@ -0,0 +1,379 @@ +// Copyright (c) 2018, libnumaapi authors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// 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 OR COPYRIGHT HOLDERS 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. +// +// Author: Sergey Sharybin (sergey.vfx@gmail.com) + +#ifndef __BUILD_CONFIG_H__ +#define __BUILD_CONFIG_H__ + +#include +#include + +// Initially is based on Chromium's build_config.h, with tweaks and extensions +// needed for this project. +// +// NOTE: All commonly used symbols (which are checked on a "top" level, from +// outside of any platform-specific ifdef block) are to be explicitly defined +// to 0 when they are not "active". This is extra lines of code in this file, +// but is not being edited that often. Such approach helps catching cases when +// one attempted to access build configuration variable without including the +// header by simply using -Wundef compiler attribute. +// +// NOTE: Not having things explicitly defined to 0 is harmless (in terms it +// follows same rules as Google projects) and will simply cause compiler to +// become more noisy, which is simple to correct. + +//////////////////////////////////////////////////////////////////////////////// +// A set of macros to use for platform detection. + +#if defined(__native_client__) +// __native_client__ must be first, so that other OS_ defines are not set. +# define OS_NACL 1 +#elif defined(_AIX) +# define OS_AIX 1 +#elif defined(ANDROID) +# define OS_ANDROID 1 +#elif defined(__APPLE__) +// Only include TargetConditions after testing ANDROID as some android builds +// on mac don't have this header available and it's not needed unless the target +// is really mac/ios. +# include +# define OS_MACOSX 1 +# if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE +# define OS_IOS 1 +# endif // defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE +#elif defined(__HAIKU__) +# define OS_HAIKU 1 +#elif defined(__hpux) +# define OS_HPUX 1 +#elif defined(__linux__) +# define OS_LINUX 1 +// Include a system header to pull in features.h for glibc/uclibc macros. +# include +# if defined(__GLIBC__) && !defined(__UCLIBC__) +// We really are using glibc, not uClibc pretending to be glibc. +# define LIBC_GLIBC 1 +# endif +#elif defined(__sgi) +# define OS_IRIX 1 +#elif defined(_WIN32) +# define OS_WIN 1 +#elif defined(__FreeBSD__) +# define OS_FREEBSD 1 +#elif defined(__NetBSD__) +# define OS_NETBSD 1 +#elif defined(__OpenBSD__) +# define OS_OPENBSD 1 +#elif defined(__sun) +# define OS_SOLARIS 1 +#elif defined(__QNXNTO__) +# define OS_QNX 1 +#else +# error Please add support for your platform in build_config.h +#endif + +#if !defined(OS_AIX) +# define OS_AIX 0 +#endif +#if !defined(OS_NACL) +# define OS_NACL 0 +#endif +#if !defined(OS_ANDROID) +# define OS_ANDROID 0 +#endif +#if !defined(OS_MACOSX) +# define OS_MACOSX 0 +#endif +#if !defined(OS_IOS) +# define OS_IOS 0 +#endif +#if !defined(OS_HAIKU) +# define OS_HAIKU 0 +#endif +#if !defined(OS_HPUX) +# define OS_HPUX 0 +#endif +#if !defined(OS_IRIX) +# define OS_IRIX 0 +#endif +#if !defined(OS_LINUX) +# define OS_LINUX 0 +#endif +#if !defined(LIBC_GLIBC) +# define LIBC_GLIBC 0 +#endif +#if !defined(OS_WIN) +# define OS_WIN 0 +#endif +#if !defined(OS_FREEBSD) +# define OS_FREEBSD 0 +#endif +#if !defined(OS_NETBSD) +# define OS_NETBSD 0 +#endif +#if !defined(OS_OPENBSD) +# define OS_OPENBSD 0 +#endif +#if !defined(OS_SOLARIS) +# define OS_SOLARIS 0 +#endif +#if !defined(OS_QNX) +# define OS_QNX 0 +#endif + +//////////////////////////////////////////////////////////////////////////////// +// *BSD OS family detection. +// +// For access to standard BSD features, use OS_BSD instead of a +// more specific macro. +#if OS_FREEBSD || OS_OPENBSD || OS_NETBSD +# define OS_BSD 1 +#else +# define OS_BSD 0 +#endif + +//////////////////////////////////////////////////////////////////////////////// +// POSIX system detection. +// +// For access to standard POSIXish features use OS_POSIX instead of a +// more specific macro. +#if OS_MACOSX || OS_LINUX || OS_BSD || OS_SOLARIS ||OS_ANDROID || OS_NACL || \ + OS_QNX || OS_HAIKU || OS_AIX || OS_HPUX || OS_IRIX +# define OS_POSIX 1 +#else +# define OS_POSIX 0 +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Compiler detection, including its capabilities. + +#if defined(__clang__) +# define COMPILER_CLANG 1 +#elif defined(__GNUC__) +# define COMPILER_GCC 1 +# define COMPILER_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) +#elif defined(_MSC_VER) +# define COMPILER_MSVC 1 +# define COMPILER_MSVC_VERSION (_MSC_VER) +#elif defined(__MINGW32__) +# define COMPILER_MINGW32 1 +#elif defined(__MINGW64__) +# define COMPILER_MINGW64 1 +#else +# error Please add support for your compiler in build_config.h +#endif + +#if !defined(COMPILER_CLANG) +# define COMPILER_CLANG 0 +#endif +#if !defined(COMPILER_GCC) +# define COMPILER_GCC 0 +#endif +#if !defined(COMPILER_MSVC) +# define COMPILER_MSVC 0 +#endif +#if !defined(COMPILER_MINGW32) +# define COMPILER_MINGW32 0 +#endif +#if !defined(COMPILER_MINGW64) +# define COMPILER_MINGW64 0 +#endif + +// Compiler is any of MinGW family. +#if COMPILER_MINGW32 || COMPILER_MINGW64 +# define COMPILER_MINGW 1 +#else +# define COMPILER_MINGW 0 +#endif + +// Check what is the latest C++ specification the compiler supports. +// +// NOTE: Use explicit definition here to avoid expansion-to-defined warning from +// being geenrated. While this will most likely a false-positive warning in this +// particular case, that warning might be helpful to catch errors elsewhere. + +// C++11 check. +#if ((defined(__cplusplus) && (__cplusplus > 199711L)) || \ + (defined(_MSC_VER) && (_MSC_VER >= 1800))) +# define COMPILER_SUPPORTS_CXX11 1 +#else +# define COMPILER_SUPPORTS_CXX11 0 +#endif +// C++14 check. +#if (defined(__cplusplus) && (__cplusplus > 201311L)) +# define COMPILER_SUPPORTS_CXX14 1 +#else +# define COMPILER_SUPPORTS_CXX14 0 +#endif +// C++17 check. +#if (defined(__cplusplus) && (__cplusplus > 201611L)) +# define COMPILER_SUPPORTS_CXX17 1 +#else +# define COMPILER_SUPPORTS_CXX17 0 +#endif +// C++20 check. +#if (defined(__cplusplus) && (__cplusplus > 201911L)) +# define COMPILER_SUPPORTS_CXX20 1 +#else +# define COMPILER_SUPPORTS_CXX20 0 +#endif + +// COMPILER_USE_ADDRESS_SANITIZER is defined when program is detected that +// compilation happened wit haddress sanitizer enabled. This allows to give +// tips to sanitizer, or maybe work around some known issues with third party +// libraries. +#if !defined(COMPILER_USE_ADDRESS_SANITIZER) +# if defined(__has_feature) +# define COMPILER_USE_ADDRESS_SANITIZER 1 +# elif defined(__SANITIZE_ADDRESS__) +# define COMPILER_USE_ADDRESS_SANITIZER 1 +# endif +#endif + +#if !defined(COMPILER_USE_ADDRESS_SANITIZER) +# define COMPILER_USE_ADDRESS_SANITIZER 0 +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Processor architecture detection. +// +// For more info on what's defined, see: +// +// http://msdn.microsoft.com/en-us/library/b0084kay.aspx +// http://www.agner.org/optimize/calling_conventions.pdf +// +// or with gcc, run: "echo | gcc -E -dM -" +#if defined(_M_X64) || defined(__x86_64__) +# define ARCH_CPU_X86_FAMILY 1 +# define ARCH_CPU_X86_64 1 +# define ARCH_CPU_64_BITS 1 +# define ARCH_CPU_LITTLE_ENDIAN 1 +#elif defined(_M_IX86) || defined(__i386__) +# define ARCH_CPU_X86_FAMILY 1 +# define ARCH_CPU_X86 1 +# define ARCH_CPU_32_BITS 1 +# define ARCH_CPU_LITTLE_ENDIAN 1 +#elif defined(__ARMEL__) +# define ARCH_CPU_ARM_FAMILY 1 +# define ARCH_CPU_ARMEL 1 +# define ARCH_CPU_32_BITS 1 +# define ARCH_CPU_LITTLE_ENDIAN 1 +#elif defined(__aarch64__) +# define ARCH_CPU_ARM_FAMILY 1 +# define ARCH_CPU_ARM64 1 +# define ARCH_CPU_64_BITS 1 +# define ARCH_CPU_LITTLE_ENDIAN 1 +#elif defined(__pnacl__) +# define ARCH_CPU_32_BITS 1 +# define ARCH_CPU_LITTLE_ENDIAN 1 +#elif defined(__MIPSEL__) +# if defined(__LP64__) +# define ARCH_CPU_MIPS64_FAMILY 1 +# define ARCH_CPU_MIPS64EL 1 +# define ARCH_CPU_64_BITS 1 +# define ARCH_CPU_LITTLE_ENDIAN 1 +# else +# define ARCH_CPU_MIPS_FAMILY 1 +# define ARCH_CPU_MIPSEL 1 +# define ARCH_CPU_32_BITS 1 +# define ARCH_CPU_LITTLE_ENDIAN 1 +# endif +#elif defined(__MIPSEB__) +# if defined(__LP64__) +# define ARCH_CPU_MIPS64_FAMILY 1 +# define ARCH_CPU_MIPS64EB 1 +# define ARCH_CPU_64_BITS 1 +# define ARCH_CPU_BIG_ENDIAN 1 +# else +# define ARCH_CPU_MIPS_FAMILY 1 +# define ARCH_CPU_MIPSEB 1 +# define ARCH_CPU_32_BITS 1 +# define ARCH_CPU_BIG_ENDIAN 1 +# endif +#else +# error Please add support for your architecture in build_config.h +#endif + +#if !defined(ARCH_CPU_LITTLE_ENDIAN) +# define ARCH_CPU_LITTLE_ENDIAN 0 +#endif +#if !defined(ARCH_CPU_BIG_ENDIAN) +# define ARCH_CPU_BIG_ENDIAN 0 +#endif + +#if !defined(ARCH_CPU_32_BITS) +# define ARCH_CPU_32_BITS 0 +#endif +#if !defined(ARCH_CPU_64_BITS) +# define ARCH_CPU_64_BITS 0 +#endif + +#if !defined(ARCH_CPU_X86_FAMILY) +# define ARCH_CPU_X86_FAMILY 0 +#endif +#if !defined(ARCH_CPU_ARM_FAMILY) +# define ARCH_CPU_ARM_FAMILY 0 +#endif +#if !defined(ARCH_CPU_MIPS_FAMILY) +# define ARCH_CPU_MIPS_FAMILY 0 +#endif +#if !defined(ARCH_CPU_MIPS64_FAMILY) +# define ARCH_CPU_MIPS64_FAMILY 0 +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Sizes of platform-dependent types. + +#if defined(__SIZEOF_POINTER__) +# define PLATFORM_SIZEOF_PTR __SIZEOF_POINTER__ +#elif defined(UINTPTR_MAX) +# if (UINTPTR_MAX == 0xffffffff) +# define PLATFORM_SIZEOF_PTR 4 +# elif (UINTPTR_MAX == 0xffffffffffffffff) // NOLINT +# define PLATFORM_SIZEOF_PTR 8 +# endif +#elif defined(__WORDSIZE) +# if (__WORDSIZE == 32) +# define PLATFORM_SIZEOF_PTR 4 +# else if (__WORDSIZE == 64) +# define PLATFORM_SIZEOF_PTR 8 +# endif +#endif +#if !defined(PLATFORM_SIZEOF_PTR) +# error "Cannot find pointer size" +#endif + +#if (UINT_MAX == 0xffffffff) +# define PLATFORM_SIZEOF_INT 4 +#elif (UINT_MAX == 0xffffffffffffffff) // NOLINT +# define PLATFORM_SIZEOF_INT 8 +#else +# error "Cannot find int size" +#endif + +#if (USHRT_MAX == 0xffffffff) +# define PLATFORM_SIZEOF_SHORT 4 +#elif (USHRT_MAX == 0xffff) // NOLINT +# define PLATFORM_SIZEOF_SHORT 2 +#else +# error "Cannot find short size" +#endif + +#endif // __BUILD_CONFIG_H__ diff --git a/intern/numaapi/source/numaapi.c b/intern/numaapi/source/numaapi.c new file mode 100644 index 00000000000..ddd9199eeff --- /dev/null +++ b/intern/numaapi/source/numaapi.c @@ -0,0 +1,37 @@ +// Copyright (c) 2018, libnumaapi authors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// 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 OR COPYRIGHT HOLDERS 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. +// +// Author: Sergey Sharybin (sergey.vfx@gmail.com) + +#include "numaapi.h" + +#include + +// Get string representation of NUMAPIResult. +const char* numaAPI_ResultAsString(NUMAAPI_Result result) { + switch (result) { + case NUMAAPI_SUCCESS: return "SUCCESS"; + case NUMAAPI_NOT_AVAILABLE: return "NOT_AVAILABLE"; + case NUMAAPI_ERROR: return "ERROR"; + case NUMAAPI_ERROR_ATEXIT: return "ERROR_AT_EXIT"; + } + assert(!"Unknown result was passed to numapi_ResultAsString()."); + return "UNKNOWN"; +} diff --git a/intern/numaapi/source/numaapi_linux.c b/intern/numaapi/source/numaapi_linux.c new file mode 100644 index 00000000000..559e97b67d3 --- /dev/null +++ b/intern/numaapi/source/numaapi_linux.c @@ -0,0 +1,272 @@ +// Copyright (c) 2016, libnumaapi authors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// 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 OR COPYRIGHT HOLDERS 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. +// +// Author: Sergey Sharybin (sergey.vfx@gmail.com) + +#include "build_config.h" + +#if OS_LINUX + +#include "numaapi.h" + +#include + +#ifndef WITH_DYNLOAD +# include +#else +# include +#endif + +#ifdef WITH_DYNLOAD + +// Descriptor numa library. +static void* numa_lib; + +// Types of all symbols which are read from the library. +struct bitmask; +typedef int tnuma_available(void); +typedef int tnuma_max_node(void); +typedef int tnuma_node_to_cpus(int node, struct bitmask* mask); +typedef long tnuma_node_size(int node, long* freep); +typedef int tnuma_run_on_node(int node); +typedef void* tnuma_alloc_onnode(size_t size, int node); +typedef void* tnuma_alloc_local(size_t size); +typedef void tnuma_free(void* start, size_t size); +typedef struct bitmask* tnuma_bitmask_clearall(struct bitmask *bitmask); +typedef int tnuma_bitmask_isbitset(const struct bitmask *bitmask, + unsigned int n); +typedef struct bitmask* tnuma_bitmask_setbit(struct bitmask *bitmask, + unsigned int n); +typedef unsigned int tnuma_bitmask_nbytes(struct bitmask *bitmask); +typedef void tnuma_bitmask_free(struct bitmask *bitmask); +typedef struct bitmask* tnuma_allocate_cpumask(void); +typedef struct bitmask* tnuma_allocate_nodemask(void); +typedef void tnuma_free_cpumask(struct bitmask* bitmask); +typedef void tnuma_free_nodemask(struct bitmask* bitmask); +typedef int tnuma_run_on_node_mask(struct bitmask *nodemask); +typedef void tnuma_set_interleave_mask(struct bitmask *nodemask); +typedef void tnuma_set_localalloc(void); + +// Actual symbols. +static tnuma_available* numa_available; +static tnuma_max_node* numa_max_node; +static tnuma_node_to_cpus* numa_node_to_cpus; +static tnuma_node_size* numa_node_size; +static tnuma_run_on_node* numa_run_on_node; +static tnuma_alloc_onnode* numa_alloc_onnode; +static tnuma_alloc_local* numa_alloc_local; +static tnuma_free* numa_free; +static tnuma_bitmask_clearall* numa_bitmask_clearall; +static tnuma_bitmask_isbitset* numa_bitmask_isbitset; +static tnuma_bitmask_setbit* numa_bitmask_setbit; +static tnuma_bitmask_nbytes* numa_bitmask_nbytes; +static tnuma_bitmask_free* numa_bitmask_free; +static tnuma_allocate_cpumask* numa_allocate_cpumask; +static tnuma_allocate_nodemask* numa_allocate_nodemask; +static tnuma_free_nodemask* numa_free_nodemask; +static tnuma_free_cpumask* numa_free_cpumask; +static tnuma_run_on_node_mask* numa_run_on_node_mask; +static tnuma_set_interleave_mask* numa_set_interleave_mask; +static tnuma_set_localalloc* numa_set_localalloc; + +static void* findLibrary(const char** paths) { + int i = 0; + while (paths[i] != NULL) { + void* lib = dlopen(paths[i], RTLD_LAZY); + if (lib != NULL) { + return lib; + } + ++i; + } + return NULL; +} + +static void numaExit(void) { + if (numa_lib == NULL) { + return; + } + dlclose(numa_lib); + numa_lib = NULL; +} + +static NUMAAPI_Result loadNumaSymbols(void) { + // Prevent multiple initializations. + static bool initialized = false; + static NUMAAPI_Result result = NUMAAPI_NOT_AVAILABLE; + if (initialized) { + return result; + } + initialized = true; + // Find appropriate .so library. + const char* numa_paths[] = { + "libnuma.so.1", + "libnuma.so", + NULL}; + // Register de-initialization. + const int error = atexit(numaExit); + if (error) { + result = NUMAAPI_ERROR_ATEXIT; + return result; + } + // Load library. + numa_lib = findLibrary(numa_paths); + if (numa_lib == NULL) { + result = NUMAAPI_NOT_AVAILABLE; + return result; + } + // Load symbols. + +#define _LIBRARY_FIND(lib, name) \ + do { \ + name = (t##name *)dlsym(lib, #name); \ + } while (0) +#define NUMA_LIBRARY_FIND(name) _LIBRARY_FIND(numa_lib, name) + + NUMA_LIBRARY_FIND(numa_available); + NUMA_LIBRARY_FIND(numa_max_node); + NUMA_LIBRARY_FIND(numa_node_to_cpus); + NUMA_LIBRARY_FIND(numa_node_size); + NUMA_LIBRARY_FIND(numa_run_on_node); + NUMA_LIBRARY_FIND(numa_alloc_onnode); + NUMA_LIBRARY_FIND(numa_alloc_local); + NUMA_LIBRARY_FIND(numa_free); + NUMA_LIBRARY_FIND(numa_bitmask_clearall); + NUMA_LIBRARY_FIND(numa_bitmask_isbitset); + NUMA_LIBRARY_FIND(numa_bitmask_setbit); + NUMA_LIBRARY_FIND(numa_bitmask_nbytes); + NUMA_LIBRARY_FIND(numa_bitmask_free); + NUMA_LIBRARY_FIND(numa_allocate_cpumask); + NUMA_LIBRARY_FIND(numa_allocate_nodemask); + NUMA_LIBRARY_FIND(numa_free_cpumask); + NUMA_LIBRARY_FIND(numa_free_nodemask); + NUMA_LIBRARY_FIND(numa_run_on_node_mask); + NUMA_LIBRARY_FIND(numa_set_interleave_mask); + NUMA_LIBRARY_FIND(numa_set_localalloc); + +#undef NUMA_LIBRARY_FIND +#undef _LIBRARY_FIND + + result = NUMAAPI_SUCCESS; + return result; +} +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Initialization. + +NUMAAPI_Result numaAPI_Initialize(void) { +#ifdef WITH_DYNLOAD + NUMAAPI_Result result = loadNumaSymbols(); + if (result != NUMAAPI_SUCCESS) { + return result; + } +#endif + if (numa_available() < 0) { + return NUMAAPI_NOT_AVAILABLE; + } + return NUMAAPI_SUCCESS; +} + +//////////////////////////////////////////////////////////////////////////////// +// Topology query. + +int numaAPI_GetNumNodes(void) { + return numa_max_node() + 1; +} + +bool numaAPI_IsNodeAvailable(int node) { + if (numa_node_size(node, NULL) > 0) { + return true; + } + return false; +} + +int numaAPI_GetNumNodeProcessors(int node) { + struct bitmask* cpu_mask = numa_allocate_cpumask(); + numa_node_to_cpus(node, cpu_mask); + const unsigned int num_bytes = numa_bitmask_nbytes(cpu_mask); + const unsigned int num_bits = num_bytes *8; + // TODO(sergey): There might be faster way calculating number of set bits. + int num_processors = 0; + for (unsigned int bit = 0; bit < num_bits; ++bit) { + if (numa_bitmask_isbitset(cpu_mask, bit)) { + ++num_processors; + } + } +#ifdef WITH_DYNLOAD + if (numa_free_cpumask != NULL) { + numa_free_cpumask(cpu_mask); + } else { + numa_bitmask_free(cpu_mask); + } +#else + numa_free_cpumask(cpu_mask); +#endif + return num_processors; +} + +//////////////////////////////////////////////////////////////////////////////// +// Affinities. + +bool numaAPI_RunProcessOnNode(int node) { + numaAPI_RunThreadOnNode(node); + return true; +} + +bool numaAPI_RunThreadOnNode(int node) { + // Construct bit mask from node index. + struct bitmask* node_mask = numa_allocate_nodemask(); + numa_bitmask_clearall(node_mask); + numa_bitmask_setbit(node_mask, node); + numa_run_on_node_mask(node_mask); + // TODO(sergey): The following commands are based on x265 code, we might want + // to make those optional, or require to call those explicitly. + // + // Current assumption is that this is similar to SetThreadGroupAffinity(). + numa_set_interleave_mask(node_mask); + numa_set_localalloc(); +#ifdef WITH_DYNLOAD + if (numa_free_nodemask != NULL) { + numa_free_nodemask(node_mask); + } else { + numa_bitmask_free(node_mask); + } +#else + numa_free_nodemask(node_mask); +#endif + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// Memory management. + +void* numaAPI_AllocateOnNode(size_t size, int node) { + return numa_alloc_onnode(size, node); +} + +void* numaAPI_AllocateLocal(size_t size) { + return numa_alloc_local(size); +} + +void numaAPI_Free(void* start, size_t size) { + numa_free(start, size); +} + +#endif // OS_LINUX diff --git a/intern/numaapi/source/numaapi_stub.c b/intern/numaapi/source/numaapi_stub.c new file mode 100644 index 00000000000..318fd72369d --- /dev/null +++ b/intern/numaapi/source/numaapi_stub.c @@ -0,0 +1,82 @@ +// Copyright (c) 2016, libnumaapi authors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// 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 OR COPYRIGHT HOLDERS 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. +// +// Author: Sergey Sharybin (sergey.vfx@gmail.com) + +#include "numaapi.h" + +#include "build_config.h" + +// Stub implementation for platforms which doesn't have NUMA support. + +#if !OS_LINUX && !OS_WIN + +//////////////////////////////////////////////////////////////////////////////// +// Initialization. + +NUMAPIResult numaAPI_Initialize(void) { + return UMAAPI_NOT_AVAILABLE; +} + +//////////////////////////////////////////////////////////////////////////////// +// Topology query. + +int numaAPI_GetNumNodes(void) { + return 0; +} + +bool numApiIsNodeAvailable(int node) { + (void) node; // Ignored. + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// Affinities. + +bool numaAPI_RunProcessOnNode(int node) { + (void) node; // Ignored. + return false; +} + +bool numaAPI_RunThreadOnNode(int node) { + (void) node; // Ignored. + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// Memory management. + +void* numaAPI_AllocateOnNode(size_t size, int node) { + (void) size; // Ignored. + (void) node; // Ignored. + return 0; +} + +void* numaAPI_AllocateLocal(size_t size) { + (void) size; // Ignored. + return NULL; +} + +void numaAPI_Free(void* start, size_t size) { + (void) start; // Ignored. + (void) size; // Ignored. +} + +#endif // !OS_LINUX && !OS_WIN diff --git a/intern/numaapi/source/numaapi_win32.c b/intern/numaapi/source/numaapi_win32.c new file mode 100644 index 00000000000..a000b3ce208 --- /dev/null +++ b/intern/numaapi/source/numaapi_win32.c @@ -0,0 +1,253 @@ +// Copyright (c) 2016, libnumaapi authors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// 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 OR COPYRIGHT HOLDERS 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. +// +// Author: Sergey Sharybin (sergey.vfx@gmail.com) + +#include "build_config.h" + +#if OS_WIN + +#include "numaapi.h" + +#ifndef NOGDI +# define NOGDI +#endif +#ifndef NOMINMAX +# define NOMINMAX +#endif +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#ifndef NOCOMM +# define NOCOMM +#endif + +#include +#include +#include + +#if ARCH_CPU_64_BITS +# include +#endif + +#include + +//////////////////////////////////////////////////////////////////////////////// +// Initialization. + +// Kernel library, from where the symbols come. +static HMODULE kernel_lib; + +// Types of all symbols which are read from the library. + +// NUMA function types. +typedef BOOL t_GetNumaHighestNodeNumber(PULONG highest_node_number); +typedef BOOL t_GetNumaNodeProcessorMask(UCHAR node, ULONGLONG* processor_mask); +typedef BOOL t_GetNumaNodeProcessorMaskEx(USHORT node, + GROUP_AFFINITY* processor_mask); +typedef BOOL t_GetNumaProcessorNode(UCHAR processor, UCHAR* node_number); +typedef void* t_VirtualAllocExNuma(HANDLE process_handle, + LPVOID address, + SIZE_T size, + DWORD allocation_type, + DWORD protect, + DWORD preferred); +typedef BOOL t_VirtualFree(void* address, SIZE_T size, DWORD free_type); +// Threading function types. +typedef BOOL t_SetProcessAffinityMask(HANDLE process_handle, + DWORD_PTR process_affinity_mask); +typedef BOOL t_SetThreadGroupAffinity(HANDLE thread_handle, + const GROUP_AFFINITY* GroupAffinity, + GROUP_AFFINITY* PreviousGroupAffinity); +typedef DWORD t_GetCurrentProcessorNumber(void); + +// NUMA symbols. +static t_GetNumaHighestNodeNumber* _GetNumaHighestNodeNumber; +static t_GetNumaNodeProcessorMask* _GetNumaNodeProcessorMask; +static t_GetNumaNodeProcessorMaskEx* _GetNumaNodeProcessorMaskEx; +static t_GetNumaProcessorNode* _GetNumaProcessorNode; +static t_VirtualAllocExNuma* _VirtualAllocExNuma; +static t_VirtualFree* _VirtualFree; +// Threading symbols. +static t_SetProcessAffinityMask* _SetProcessAffinityMask; +static t_SetThreadGroupAffinity* _SetThreadGroupAffinity; +static t_GetCurrentProcessorNumber* _GetCurrentProcessorNumber; + +static void numaExit(void) { + // TODO(sergey): Consider closing library here. +} + +static NUMAAPI_Result loadNumaSymbols(void) { + // Prevent multiple initializations. + static bool initialized = false; + static NUMAAPI_Result result = NUMAAPI_NOT_AVAILABLE; + if (initialized) { + return result; + } + initialized = true; + // Register de-initialization. + const int error = atexit(numaExit); + if (error) { + result = NUMAAPI_ERROR_ATEXIT; + return result; + } + // Load library. + kernel_lib = LoadLibraryA("Kernel32.dll"); + // Load symbols. + +#define _LIBRARY_FIND(lib, name) \ + do { \ + _##name = (t_##name *)GetProcAddress(lib, #name); \ + } while (0) +#define KERNEL_LIBRARY_FIND(name) _LIBRARY_FIND(kernel_lib, name) + + // NUMA. + KERNEL_LIBRARY_FIND(GetNumaHighestNodeNumber); + KERNEL_LIBRARY_FIND(GetNumaNodeProcessorMask); + KERNEL_LIBRARY_FIND(GetNumaNodeProcessorMaskEx); + KERNEL_LIBRARY_FIND(GetNumaProcessorNode); + KERNEL_LIBRARY_FIND(VirtualAllocExNuma); + KERNEL_LIBRARY_FIND(VirtualFree); + // Threading. + KERNEL_LIBRARY_FIND(SetProcessAffinityMask); + KERNEL_LIBRARY_FIND(SetThreadGroupAffinity); + KERNEL_LIBRARY_FIND(GetCurrentProcessorNumber); + +#undef KERNEL_LIBRARY_FIND +#undef _LIBRARY_FIND + + result = NUMAAPI_SUCCESS; + return result; +} + +NUMAAPI_Result numaAPI_Initialize(void) { +#if !ARCH_CPU_64_BITS + // No NUMA on 32 bit platforms. + return LIBNUMAAPI_NOT_AVAILABLE; +#else + if (!IsWindows7OrGreater()) { + // Require Windows 7 or higher. + NUMAAPI_NOT_AVAILABLE; + } + loadNumaSymbols(); + return NUMAAPI_SUCCESS; +#endif +} + +//////////////////////////////////////////////////////////////////////////////// +// Topology query. + +int numaAPI_GetNumNodes(void) { + ULONG highest_node_number; + if (!_GetNumaHighestNodeNumber(&highest_node_number)) { + return 0; + } + // TODO(sergey): Resolve the type narrowing. + // NOTE: This is not necessarily a total amount of nodes in the system. + return (int)highest_node_number + 1; +} + +bool numaAPI_IsNodeAvailable(int node) { + // Trick to detect whether the node is usable or not: check whether + // there are any processors associated with it. + // + // This is needed because numaApiGetNumNodes() is not guaranteed to + // give total amount of nodes and some nodes might be unavailable. + ULONGLONG processor_mask; + if (!_GetNumaNodeProcessorMask(node, &processor_mask)) { + return false; + } + if (processor_mask == 0) { + return false; + } + return true; +} + +int numaAPI_GetNumNodeProcessors(int node) { + ULONGLONG processor_mask; + if (!_GetNumaNodeProcessorMask(node, &processor_mask)) { + return 0; + } + // TODO(sergey): There might be faster way calculating number of set bits. + int num_processors = 0; + while (processor_mask != 0) { + num_processors += (processor_mask & 1); + processor_mask = (processor_mask >> 1); + } + return num_processors; +} + +//////////////////////////////////////////////////////////////////////////////// +// Affinities. + +bool numaAPI_RunProcessOnNode(int node) { + // TODO(sergey): Make sure requested node is within active CPU group. + // Change affinity of the proces to make it to run on a given node. + HANDLE process_handle = GetCurrentProcess(); + ULONGLONG processor_mask; + if (_GetNumaNodeProcessorMask(node, &processor_mask) == 0) { + return false; + } + if (_SetProcessAffinityMask(process_handle, processor_mask) == 0) { + return false; + } + return true; +} + +bool numaAPI_RunThreadOnNode(int node) { + HANDLE thread_handle = GetCurrentThread(); + GROUP_AFFINITY group_affinity = { 0 }; + if (_GetNumaNodeProcessorMaskEx(node, &group_affinity) == 0) { + return false; + } + if (_SetThreadGroupAffinity(thread_handle, &group_affinity, NULL) == 0) { + return false; + } + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// Memory management. + +void* numaAPI_AllocateOnNode(size_t size, int node) { + return _VirtualAllocExNuma(GetCurrentProcess(), + NULL, + size, + MEM_RESERVE | MEM_COMMIT, + PAGE_READWRITE, + node); +} + +void* numaAPI_AllocateLocal(size_t size) { + UCHAR current_processor = (UCHAR)_GetCurrentProcessorNumber(); + UCHAR node; + if (!_GetNumaProcessorNode(current_processor, &node)) { + return NULL; + } + return numaAPI_AllocateOnNode(size, node); +} + +void numaAPI_Free(void* start, size_t size) { + if (!_VirtualFree(start, size, MEM_RELEASE)) { + // TODO(sergey): Throw an error! + } +} + +#endif // OS_WIN -- cgit v1.2.3