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