From 2f74e5e8a9f1f04952ad1aff425545314a2fefc1 Mon Sep 17 00:00:00 2001 From: Youmu Liu <105042599+Nitload-NSI@users.noreply.github.com> Date: Thu, 2 Apr 2026 18:13:32 +0800 Subject: [PATCH] add startup numa sense --- .../ProcessorAbstraction/LogicalCore.cs | 48 +++++++++++ .../ProcessorAbstraction/Numa.cs | 79 +++++++++++++++++++ .../ProcessorAbstraction/NumaNode.cs | 54 +++++++++++++ StgSharp.Common/StgSharp.Common.csproj | 4 - StgSharp.Native/StgSharp.Native.vcxproj | 3 +- .../StgSharp.Native.vcxproj.filters | 5 +- StgSharp.Native/sn-head/StgSharpNative.h | 12 ++- StgSharp.Native/sn-src/simdid.x86.c | 10 --- StgSharp.Native/sn-src/thread.linux.c | 39 +++++++++ .../sn-src/{t.affinity.c => thread.windows.c} | 64 +++++++++------ 10 files changed, 273 insertions(+), 45 deletions(-) create mode 100644 StgSharp.Common/HighPerformance/ProcessorAbstraction/LogicalCore.cs create mode 100644 StgSharp.Common/HighPerformance/ProcessorAbstraction/Numa.cs create mode 100644 StgSharp.Common/HighPerformance/ProcessorAbstraction/NumaNode.cs create mode 100644 StgSharp.Native/sn-src/thread.linux.c rename StgSharp.Native/sn-src/{t.affinity.c => thread.windows.c} (52%) diff --git a/StgSharp.Common/HighPerformance/ProcessorAbstraction/LogicalCore.cs b/StgSharp.Common/HighPerformance/ProcessorAbstraction/LogicalCore.cs new file mode 100644 index 00000000..ba3487ac --- /dev/null +++ b/StgSharp.Common/HighPerformance/ProcessorAbstraction/LogicalCore.cs @@ -0,0 +1,48 @@ +//----------------------------------------------------------------------- +// ----------------------------------------------------------------------- +// file="LogicalCore" +// Project: StgSharp +// AuthorGroup: Nitload +// Copyright (c) Nitload. All rights reserved. +// +// 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. +// +// ----------------------------------------------------------------------- +// ----------------------------------------------------------------------- +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace StgSharp.Common.HighPerformance.ProcessorAbstraction +{ + public readonly struct LogicalCore + { + + public readonly uint GlobalCpuId; + public readonly uint NumaGroupId; + + /// + /// Stays 0 on linux. + /// + public readonly uint NumaNodeId; + + } +} diff --git a/StgSharp.Common/HighPerformance/ProcessorAbstraction/Numa.cs b/StgSharp.Common/HighPerformance/ProcessorAbstraction/Numa.cs new file mode 100644 index 00000000..0501fa82 --- /dev/null +++ b/StgSharp.Common/HighPerformance/ProcessorAbstraction/Numa.cs @@ -0,0 +1,79 @@ +//----------------------------------------------------------------------- +// ----------------------------------------------------------------------- +// file="Numa" +// Project: StgSharp +// AuthorGroup: Nitload +// Copyright (c) Nitload. All rights reserved. +// +// 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. +// +// ----------------------------------------------------------------------- +// ----------------------------------------------------------------------- +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace StgSharp.Common.HighPerformance.ProcessorAbstraction +{ + public static partial class Numa + { + + private static NumaNode[] _numaNodes; + + public static int NumaNodeCount { get; private set; } + + public static ReadOnlySpan AllNumaNode => _numaNodes; + + [LibraryImport(Native.LibName, EntryPoint = "sn_get_numa_each")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory)] + public static partial int GetNumaEach( + int numaId, + Span coreCountInNuma, + Span globalCpuIdInNuma + ); + + [LibraryImport(Native.LibName, EntryPoint = "sn_get_numa_overall")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory)] + public static partial int GetNumaOverall( + ref int count, + Span coreCountInNuma + ); + + internal static void Initialize() + { + Span coreCountInNuma = stackalloc int[(Environment.ProcessorCount / 32) + 1]; + int numaNodeCount = 0; + if (0 == GetNumaOverall(ref numaNodeCount, coreCountInNuma)) { + throw new InvalidOperationException("Failed to get NUMA overall information."); + } + _numaNodes = new NumaNode[numaNodeCount]; + for (int i = 0; i < numaNodeCount; i++) + { + NumaNode node = new(coreCountInNuma[i]); + GetNumaEach(i, coreCountInNuma, node.GlobalCpuIds); + } + } + + } +} diff --git a/StgSharp.Common/HighPerformance/ProcessorAbstraction/NumaNode.cs b/StgSharp.Common/HighPerformance/ProcessorAbstraction/NumaNode.cs new file mode 100644 index 00000000..df888c63 --- /dev/null +++ b/StgSharp.Common/HighPerformance/ProcessorAbstraction/NumaNode.cs @@ -0,0 +1,54 @@ +//----------------------------------------------------------------------- +// ----------------------------------------------------------------------- +// file="NumaNode" +// Project: StgSharp +// AuthorGroup: Nitload +// Copyright (c) Nitload. All rights reserved. +// +// 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. +// +// ----------------------------------------------------------------------- +// ----------------------------------------------------------------------- +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace StgSharp.Common.HighPerformance.ProcessorAbstraction +{ + public class NumaNode + { + + private LogicalCore[] _logicalCores; + + public NumaNode( + int count + ) + { + _logicalCores = new LogicalCore[count]; + } + + public int LogicalCoreCount => _logicalCores.Length; + + public ReadOnlySpan LogicalCores => _logicalCores; + + } +} diff --git a/StgSharp.Common/StgSharp.Common.csproj b/StgSharp.Common/StgSharp.Common.csproj index 2fea7354..3c451513 100644 --- a/StgSharp.Common/StgSharp.Common.csproj +++ b/StgSharp.Common/StgSharp.Common.csproj @@ -23,8 +23,4 @@ - - - - \ No newline at end of file diff --git a/StgSharp.Native/StgSharp.Native.vcxproj b/StgSharp.Native/StgSharp.Native.vcxproj index 65b3a599..3abfa5da 100644 --- a/StgSharp.Native/StgSharp.Native.vcxproj +++ b/StgSharp.Native/StgSharp.Native.vcxproj @@ -185,7 +185,8 @@ - + + diff --git a/StgSharp.Native/StgSharp.Native.vcxproj.filters b/StgSharp.Native/StgSharp.Native.vcxproj.filters index f0b379d9..11bae1bb 100644 --- a/StgSharp.Native/StgSharp.Native.vcxproj.filters +++ b/StgSharp.Native/StgSharp.Native.vcxproj.filters @@ -60,12 +60,15 @@ 源文件 - + 源文件 源文件 + + 源文件 + diff --git a/StgSharp.Native/sn-head/StgSharpNative.h b/StgSharp.Native/sn-head/StgSharpNative.h index 9cf62068..2ebb8ccf 100644 --- a/StgSharp.Native/sn-head/StgSharpNative.h +++ b/StgSharp.Native/sn-head/StgSharpNative.h @@ -66,14 +66,18 @@ SN_API void SN_DECL unloadImageData(Image *out); SN_API char *SN_DECL readLog(void); SN_API void SN_DECL load_glfw_functions(GLFWFunctionTable *table); SN_API void SN_DECL load_intrinsic_function(void *intrinsic_context, uint64_t id); -SN_API SIMDID SN_DECL sn_get_simd_level_global(); -/* Per-core variant: always re-executes CPUID, never caches. - * Must be called on the target core (after affinity is set). */ -SN_API SIMDID SN_DECL sn_get_simd_level_local(); + +#pragma region numa SN_API SIMDID SN_DECL sn_get_unite_simd(SIMDID left, SIMDID right); SN_API int SN_DECL sn_compare_simd(SIMDID left, SIMDID right); SN_API void SN_DECL sn_set_thread_affinity(int core_id); +SN_API void SN_DECL sn_get_numa_overall(int *out_numa_count, int *count_of_each_numa); +SN_API void SN_DECL sn_get_numa_each(int numa_id, uint64_t *in_numa_cpuid, uint64_t *global_cpuid); +/* Per-core variant: always re-executes CPUID, never caches. + * Must be called on the target core (after affinity is set). */ +SN_API SIMDID SN_DECL sn_get_simd_level_local(); +#pragma endregion #ifdef __cplusplus } #endif diff --git a/StgSharp.Native/sn-src/simdid.x86.c b/StgSharp.Native/sn-src/simdid.x86.c index 4717149f..8cf545e8 100644 --- a/StgSharp.Native/sn-src/simdid.x86.c +++ b/StgSharp.Native/sn-src/simdid.x86.c @@ -340,16 +340,6 @@ SN_API SIMDID SN_DECL sn_get_simd_level_local() return simdid; } -SN_API SIMDID SN_DECL sn_get_simd_level_global() -{ - if (global_simdid.mask) { - return global_simdid; - } - - global_simdid = sn_get_simd_level_local(); - return global_simdid; -} - #else SN_API SIMDID SN_DECL sn_get_simd_level_local() diff --git a/StgSharp.Native/sn-src/thread.linux.c b/StgSharp.Native/sn-src/thread.linux.c new file mode 100644 index 00000000..566338bd --- /dev/null +++ b/StgSharp.Native/sn-src/thread.linux.c @@ -0,0 +1,39 @@ +#include "sn_target.h" +#include "StgSharpNative.h" + +#if SN_IS_OS(SN_OS_LINUX) +#define _GNU_SOURCE +#include +#include +#include + +/* Pin the calling thread to a single logical processor. + * core_id >= 0 : bind to that logical processor (global index, across groups). + * core_id < 0 : clear affinity constraint within the current group. */ +SN_API void SN_DECL sn_set_thread_affinity(int core_id) +{ + long ncpus = sysconf(_SC_NPROCESSORS_ONLN); + + cpu_set_t *set = CPU_ALLOC(ncpus); + size_t size = CPU_ALLOC_SIZE(ncpus); + CPU_ZERO_S(size, set); + + if (core_id < 0) { + for (long i = 0; i < ncpus; i++) + CPU_SET_S((int)i, size, set); + } else { + CPU_SET_S(core_id, size, set); + } + + pthread_setaffinity_np(pthread_self(), size, set); + CPU_FREE(set); +} + +SN_API void SN_DECL sn_get_numa_overall(int *out_numa_count, int *count_of_each_numa) +{ +} + +SN_API void SN_DECL sn_get_numa_each(int numa_id, int *in_numa_cpuid, int *global_cpuid) +{ +} +#endif diff --git a/StgSharp.Native/sn-src/t.affinity.c b/StgSharp.Native/sn-src/thread.windows.c similarity index 52% rename from StgSharp.Native/sn-src/t.affinity.c rename to StgSharp.Native/sn-src/thread.windows.c index 2a16de01..85d6ba96 100644 --- a/StgSharp.Native/sn-src/t.affinity.c +++ b/StgSharp.Native/sn-src/thread.windows.c @@ -1,19 +1,13 @@ #include "sn_target.h" #include "StgSharpNative.h" -#if SN_IS_OS(SN_OS_LINUX) -#define _GNU_SOURCE -#include -#include -#include -#endif +#if SN_IS_OS(SN_OS_WINDOWS) /* Pin the calling thread to a single logical processor. * core_id >= 0 : bind to that logical processor (global index, across groups). * core_id < 0 : clear affinity constraint within the current group. */ SN_API void SN_DECL sn_set_thread_affinity(int core_id) { -#if SN_IS_OS(SN_OS_WINDOWS) if (core_id < 0) { /* Query which group the thread currently belongs to */ GROUP_AFFINITY current; @@ -23,33 +17,53 @@ SN_API void SN_DECL sn_set_thread_affinity(int core_id) GROUP_AFFINITY ga; memset(&ga, 0, sizeof(ga)); ga.Group = current.Group; - ga.Mask = (count >= 64) ? ~(KAFFINITY)0 - : ((KAFFINITY)1 << count) - 1; + ga.Mask = (count >= 64) ? ~(KAFFINITY)0 : ((KAFFINITY)1 << count) - 1; SetThreadGroupAffinity(GetCurrentThread(), &ga, NULL); return; } WORD group = (WORD)(core_id / 64); - int index = core_id % 64; + int index = core_id % 64; DWORD count = GetActiveProcessorCount(group); if ((DWORD)index >= count) return; GROUP_AFFINITY ga; memset(&ga, 0, sizeof(ga)); ga.Group = group; - ga.Mask = (KAFFINITY)1 << index; + ga.Mask = (KAFFINITY)1 << index; SetThreadGroupAffinity(GetCurrentThread(), &ga, NULL); - -#elif SN_IS_OS(SN_OS_LINUX) - cpu_set_t set; - if (core_id < 0) { - CPU_ZERO(&set); - long ncpus = sysconf(_SC_NPROCESSORS_ONLN); - for (long i = 0; i < ncpus; i++) - CPU_SET((int)i, &set); - } else { - CPU_ZERO(&set); - CPU_SET(core_id, &set); - } - pthread_setaffinity_np(pthread_self(), sizeof(set), &set); -#endif } + +SN_API int SN_DECL sn_get_numa_overall(int *out_numa_count, int *count_of_each_numa) +{ + ULONG highest; + if (!GetNumaHighestNodeNumber(&highest)) + return 0; + *out_numa_count = (int)highest + 1; + + for (UCHAR n = 0; n <= (UCHAR)highest; n++) { + ULONGLONG mask = 0; + GetNumaNodeProcessorMask(n, &mask); + count_of_each_numa[n] = (int)__popcnt64(mask); + } + return 1; +} + +SN_API int SN_DECL sn_get_numa_each(int numa_id, uint64_t *internal, uint64_t *global) +{ + ULONGLONG mask = 0; + if (!GetNumaNodeProcessorMask((UCHAR)numa_id, &mask)) + return 0; + + int intra = 0; + while (mask) { + unsigned long idx; + _BitScanForward64(&idx, mask); + internal[intra] = intra; + global[intra] = (int)idx; + intra++; + mask &= mask - 1; + } + return 1; +} + +#endif