From 7ee9c749e5f60309cc862b2d9a206625a18d9b64 Mon Sep 17 00:00:00 2001 From: Youmu Liu <105042599+Nitload-NSI@users.noreply.github.com> Date: Tue, 24 Feb 2026 18:05:16 +0800 Subject: [PATCH] working on per-PoolThread context loading and global context loading --- .../Numeric/MatrixParallel.cs | 28 +- .../Numeric/MatrixParallelTaskPackage.cs | 1 + .../Numeric/MatrixParallelThread.Model.cs | 2 +- .../Numeric/MatrixParallelThread.cs | 55 +- .../StgSharp.Mathematics.csproj | 5 + StgSharp.Native/StgSharp.Native.vcxproj | 3 +- .../StgSharp.Native.vcxproj.filters | 5 +- StgSharp.Native/sn-head/StgSharpNative.h | 4 +- StgSharp.Native/sn-src/load_intrinsic.x86.c | 2 +- .../sn-src/{cpuid.x86.c => simdid.x86.c} | 488 +++++++++--------- StgSharp.Native/sn-src/simdid_compare.x86.c | 199 +++++++ StgSharp.Native/sn-src/t.affinity.c | 48 +- 12 files changed, 535 insertions(+), 305 deletions(-) rename StgSharp.Native/sn-src/{cpuid.x86.c => simdid.x86.c} (96%) create mode 100644 StgSharp.Native/sn-src/simdid_compare.x86.c diff --git a/StgSharp.Mathematics/Numeric/MatrixParallel.cs b/StgSharp.Mathematics/Numeric/MatrixParallel.cs index ae5b6fd8..a46476ac 100644 --- a/StgSharp.Mathematics/Numeric/MatrixParallel.cs +++ b/StgSharp.Mathematics/Numeric/MatrixParallel.cs @@ -26,6 +26,8 @@ // ----------------------------------------------------------------------- // ----------------------------------------------------------------------- using StgSharp.Collections; +using StgSharp.HighPerformance.Memory; +using StgSharp.Internal.Intrinsic; using System; using System.Collections.Generic; using System.Linq; @@ -41,45 +43,31 @@ namespace StgSharp.Mathematics.Numeric private static CapacityFixedStack _threads; - private static readonly -#if NET10_0_OR_GREATER - Lock -#else - object -#endif - _lock = new(); + private static SlabAllocator contextAllocator; - // private static ThreadPriority _AfterWork = ThreadPriority.Lowest; - - public static bool SupportParallel { get; } = Environment.ProcessorCount > 6; + public static bool IsParallelSupported { get; } = Environment.ProcessorCount > 6; public static bool IsParallelMeaningful => Environment.ProcessorCount > 6 && IsInitialized; private static bool IsInitialized { get; set; } = false; - private static Queue LeaderTask { get; set; } - - public static void EndParallel() - { - Monitor.Exit(_lock); - } - internal static void Init() { if (IsInitialized) { return; } - MatrixParallelFactory.Init(); - if (!SupportParallel) + if (!IsParallelSupported) { IsInitialized = true; return; } + MatrixParallelFactory.Init(); + contextAllocator = SlabAllocator.Create(4, SlabBufferLayout.Chunked, false); int count = Environment.ProcessorCount - 4; _threads = new(count); for (int i = 0; i < count; i++) { - MatrixParallelThread thread = new MatrixParallelThread(); + MatrixParallelThread thread = new MatrixParallelThread(i); _threads.Push(thread); } _awaitingLeaderHandle = new AwaitingQueue(); diff --git a/StgSharp.Mathematics/Numeric/MatrixParallelTaskPackage.cs b/StgSharp.Mathematics/Numeric/MatrixParallelTaskPackage.cs index 1e571b3d..6a49c273 100644 --- a/StgSharp.Mathematics/Numeric/MatrixParallelTaskPackage.cs +++ b/StgSharp.Mathematics/Numeric/MatrixParallelTaskPackage.cs @@ -62,6 +62,7 @@ namespace StgSharp.Mathematics.Numeric public enum MatrixIndexStyle : short { + SPECIAL = 0, BUFFER_INDEX = 1, } diff --git a/StgSharp.Mathematics/Numeric/MatrixParallelThread.Model.cs b/StgSharp.Mathematics/Numeric/MatrixParallelThread.Model.cs index 62908fcc..024dccd4 100644 --- a/StgSharp.Mathematics/Numeric/MatrixParallelThread.Model.cs +++ b/StgSharp.Mathematics/Numeric/MatrixParallelThread.Model.cs @@ -31,7 +31,7 @@ using System.Runtime.CompilerServices; namespace StgSharp.Mathematics.Numeric { - internal static class MatrixComputeModel + public partial class MatrixParallelThread { internal static unsafe void BufferComputeBinary( diff --git a/StgSharp.Mathematics/Numeric/MatrixParallelThread.cs b/StgSharp.Mathematics/Numeric/MatrixParallelThread.cs index 019bc438..90c0e10e 100644 --- a/StgSharp.Mathematics/Numeric/MatrixParallelThread.cs +++ b/StgSharp.Mathematics/Numeric/MatrixParallelThread.cs @@ -26,6 +26,7 @@ // ----------------------------------------------------------------------- // ----------------------------------------------------------------------- using StgSharp.Internal; +using StgSharp.Internal.Intrinsic; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; @@ -46,34 +47,30 @@ namespace StgSharp.Mathematics.Numeric private readonly ManualResetEventSlim _resetEvent; private readonly Thread _managedThread; - public MatrixParallelThread() + internal MatrixParallelThread( + int poolId + ) { _resetEvent = new ManualResetEventSlim(false); - _managedThread = new Thread(MatrixParallelWorker); + _managedThread = new Thread(MatrixParallelWorkLoad); _managedThread.Start(); + PoolId = poolId; } - // ~MatrixComputeThread() - // { - // Dispose(disposing: false); - // } + public int PoolId { get; init; } public ThreadPriority AfterWork { get; set; } internal ManualResetEventSlim? LeaderEvent { get; set; } - [DllImport( - dllName:Native.LibName, - EntryPoint = "sn_set_thread_affinity", - CallingConvention = CallingConvention.Cdecl)] - internal static extern void SetThreadAffinity( - ulong affinityMask - ); - - #region wrap belong + internal IntrinsicContext* Intrinsic { get; set; } internal MatrixParallelWrap BindedWrap { get; set; } + internal RoleType Role { get; set; } + + internal SIMDID Simd { get; set; } + public void Dispose() { Dispose(disposing:true); @@ -146,6 +143,14 @@ namespace StgSharp.Mathematics.Numeric current.Priority = BindedWrap.AfterWork; } + [DllImport( + dllName:Native.LibName, + EntryPoint = "sn_set_thread_affinity", + CallingConvention = CallingConvention.Cdecl)] + internal static extern void SetThreadAffinity( + int coreId + ); + protected virtual void Dispose( bool disposing ) @@ -162,17 +167,19 @@ namespace StgSharp.Mathematics.Numeric } } - private unsafe void MatrixParallelWorker() + private unsafe void MatrixParallelWorkLoad() { // Bind OS-thread affinity once when the worker starts running + SetThreadAffinity(PoolId); + Thread.Sleep(0); while (true) { // Thread self = Thread.CurrentThread; - AfterWork = BindedWrap.AfterWork; - _managedThread.Priority = ThreadPriority.Highest; _resetEvent.Wait(); _resetEvent.Reset(); + AfterWork = BindedWrap.AfterWork; + _managedThread.Priority = ThreadPriority.Highest; switch (Role) { case RoleType.Worker://worker @@ -183,7 +190,9 @@ namespace StgSharp.Mathematics.Numeric // Console.WriteLine($"worker {Environment.CurrentManagedThreadId} end"); break; - case RoleType.Leader://leader, error because leader should be at outer thread but not here + case RoleType.SingleExternal: + + // TODO 将外部线程绑定到当前线程上,并执行任务包 break; default: break; @@ -192,16 +201,14 @@ namespace StgSharp.Mathematics.Numeric } } - public RoleType Role { get; internal set; } - - public enum RoleType : int + internal enum RoleType : int { Worker = 0, - Leader = 2, + Leader = 1, + SingleExternal = 2, } - #endregion } } diff --git a/StgSharp.Mathematics/StgSharp.Mathematics.csproj b/StgSharp.Mathematics/StgSharp.Mathematics.csproj index b36a8fd9..3c81b0d0 100644 --- a/StgSharp.Mathematics/StgSharp.Mathematics.csproj +++ b/StgSharp.Mathematics/StgSharp.Mathematics.csproj @@ -10,6 +10,11 @@ Debug;Release;PerformanceDebug + + + + + diff --git a/StgSharp.Native/StgSharp.Native.vcxproj b/StgSharp.Native/StgSharp.Native.vcxproj index 0944cd4d..f4fadcaf 100644 --- a/StgSharp.Native/StgSharp.Native.vcxproj +++ b/StgSharp.Native/StgSharp.Native.vcxproj @@ -168,7 +168,8 @@ CCode - + + diff --git a/StgSharp.Native/StgSharp.Native.vcxproj.filters b/StgSharp.Native/StgSharp.Native.vcxproj.filters index bb28e3c8..ff16b8a8 100644 --- a/StgSharp.Native/StgSharp.Native.vcxproj.filters +++ b/StgSharp.Native/StgSharp.Native.vcxproj.filters @@ -60,7 +60,7 @@ 源文件 - + 源文件 @@ -69,6 +69,9 @@ 源文件 + + 源文件 + diff --git a/StgSharp.Native/sn-head/StgSharpNative.h b/StgSharp.Native/sn-head/StgSharpNative.h index 1c071ce6..f4e64187 100644 --- a/StgSharp.Native/sn-head/StgSharpNative.h +++ b/StgSharp.Native/sn-head/StgSharpNative.h @@ -66,10 +66,12 @@ 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 SIMDID SN_DECL load_intrinsic_function(void *intrinsic_context, uint64_t id); -SN_API SIMDID SN_DECL sn_get_simd_level(); +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(); +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); #ifdef __cplusplus diff --git a/StgSharp.Native/sn-src/load_intrinsic.x86.c b/StgSharp.Native/sn-src/load_intrinsic.x86.c index b0ecf588..0ca2f3a3 100644 --- a/StgSharp.Native/sn-src/load_intrinsic.x86.c +++ b/StgSharp.Native/sn-src/load_intrinsic.x86.c @@ -27,7 +27,7 @@ SN_API SIMDID load_intrinsic_function(void *intrinsic_context, uint64_t id) if (id) { simdid.mask = id; } else { - simdid = sn_get_simd_level(); + simdid = sn_get_simd_level_global(); } sn_intrinsic *context = (sn_intrinsic *)intrinsic_context; diff --git a/StgSharp.Native/sn-src/cpuid.x86.c b/StgSharp.Native/sn-src/simdid.x86.c similarity index 96% rename from StgSharp.Native/sn-src/cpuid.x86.c rename to StgSharp.Native/sn-src/simdid.x86.c index 356ec70c..5d8fde2a 100644 --- a/StgSharp.Native/sn-src/cpuid.x86.c +++ b/StgSharp.Native/sn-src/simdid.x86.c @@ -1,244 +1,244 @@ -#include "sn_target.h" -#include "sn_intrinsic.h" -#include "StgSharpNative.h" - -static SIMDID global_simdid = { 0 }; - -#if SN_IS_ARCH(SN_ARCH_X86_64) - -#include "sn_intrincontext.x86.h" - -#include -#include -#if defined(_MSC_VER) -#include -#else -#include -#endif - -static void get_cpuid(int leaf, int subleaf, int *eax, int *ebx, int *ecx, int *edx) -{ -#if defined(_MSC_VER) - int regs[4]; - __cpuidex(regs, leaf, subleaf); - *eax = regs[0]; - *ebx = regs[1]; - *ecx = regs[2]; - *edx = regs[3]; -#else - unsigned int a, b, c, d; - __cpuid_count((unsigned int)leaf, (unsigned int)subleaf, a, b, c, d); - *eax = (int)a; - *ebx = (int)b; - *ecx = (int)c; - *edx = (int)d; -#endif -} - -static int xgetbv_has_xmm_ymm(void) -{ -#if defined(_MSC_VER) - unsigned __int64 xcr0 = _xgetbv(0); -#else - unsigned long long xcr0 = __builtin_ia32_xgetbv(0); -#endif - return ((xcr0 & 0x6) == 0x6); -} - -static int xgetbv_has_avx512_state(void) -{ - const unsigned long long mask = (1ull << 0) | (1ull << 1) | (1ull << 2) | (1ull << 5) | - (1ull << 6); -#if defined(_MSC_VER) - unsigned __int64 xcr0 = _xgetbv(0); -#else - unsigned long long xcr0 = __builtin_ia32_xgetbv(0); -#endif - return ((xcr0 & mask) == mask); -} - -static int is_intel_dual_avx512(int family, int model) -{ - return (family == 6) && (model == 0x55); /* SKX/CLX/CPX */ -} - -static int is_intel_native_avx512(int family, int model) -{ - if (family != 6) - return 0; - switch (model) { - case 0x6A: /* ICX */ - case 0x6C: /* ICX stepping */ - case 0x8F: /* SPR/EMR */ - case 0xCF: /* GNR */ - return 1; - default: - return 0; - } -} - -SN_API SIMDID SN_DECL sn_get_simd_level_local() -{ - SIMDID _simdid = { 0 }; - - int eax = 0, ebx = 0, ecx = 0, edx = 0; - - /* Vendor */ - get_cpuid(0, 0, &eax, &ebx, &ecx, &edx); - char vendor[13]; - ((int *)vendor)[0] = ebx; - ((int *)vendor)[1] = edx; - ((int *)vendor)[2] = ecx; - vendor[12] = '\0'; - const int is_intel = (strcmp(vendor, "GenuineIntel") == 0); - const int is_amd = (strcmp(vendor, "AuthenticAMD") == 0) || - (strcmp(vendor, "HygonGenuine") == 0); - - /* CPUID.1 */ - get_cpuid(1, 0, &eax, &ebx, &ecx, &edx); - int family = ((eax >> 8) & 0xF) + ((eax >> 20) & 0xFF0); - int model = ((eax >> 4) & 0xF) | ((eax >> 12) & 0xF0); - - int has_sse42 = (ecx & (1 << 20)) != 0; - int has_avx = (ecx & (1 << 28)) != 0; - int has_osxsave = (ecx & (1 << 27)) != 0; - int has_fma = (ecx & (1 << 12)) != 0; - int has_f16c = (ecx & (1 << 29)) != 0; - - uint64_t root = SIMDID_PACK_ROOT(SIMDID_ROOT_X86_64); - uint64_t manu = SIMDID_PACK_MANU(is_intel ? SIMDID_MANU_INTEL : - is_amd ? SIMDID_MANU_AMD : - SIMDID_MANU_UNKNOWN); - uint64_t main_lvl = SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_NONE); - uint64_t avx512_bits = 0; - uint64_t amx_bits = 0; - uint64_t avx_bits = 0; - uint64_t avx10_bits = 0; - - /* Baseline main level */ - if (has_sse42) - main_lvl = SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_SSE); - - if (has_avx && has_osxsave && xgetbv_has_xmm_ymm()) { - avx_bits |= SIMDID_AVX; - - /* CPUID.7.0 */ - get_cpuid(7, 0, &eax, &ebx, &ecx, &edx); - const int has_bmi1 = (ebx & (1 << 3)) != 0; - const int has_bmi2 = (ebx & (1 << 8)) != 0; - const int has_avx2 = (ebx & (1 << 5)) != 0; - const int has_avx512f = (ebx & (1 << 16)) != 0; - const int has_avx512dq = (ebx & (1 << 17)) != 0; - const int has_avx512bw = (ebx & (1 << 30)) != 0; - const int has_avx512vl = (ebx & (1u << 31)) != 0; - const int has_avx512_vnni = (ebx & (1 << 11)) != 0; - const int has_vbmi = (ecx & (1 << 1)) != 0; - const int has_vbmi2 = (ecx & (1 << 6)) != 0; - - /* AMX in leaf 7 subleaf 0 EDX bits 24/25/26 — capture before - overwriting regs with subleaf 1 */ - const int has_amx_tile = (edx & (1 << 24)) != 0; - const int has_amx_int8 = (edx & (1 << 25)) != 0; - const int has_amx_bf16 = (edx & (1 << 22)) != 0; - - /* leaf 7, subleaf 1 for BF16/FP16/AVX10 convergence */ - get_cpuid(7, 1, &eax, &ebx, &ecx, &edx); - const int has_bf16 = (eax & (1 << 5)) != 0; /* AVX512_BF16 */ - const int has_fp16 = (eax & (1 << 23)) != 0; /* AVX512_FP16 */ - const int has_avx10 = (edx & (1 << 19)) != 0; /* AVX10 convergence */ - - if (has_avx2) { - avx_bits |= SIMDID_AVX2; - if (has_fma) - avx_bits |= SIMDID_AVX_FMA; - if (has_f16c) - avx_bits |= SIMDID_AVX_F16C; - - main_lvl = SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_AVX2); - } - - if (has_avx512f && xgetbv_has_avx512_state()) { - /* Ensure base subset present */ - if (has_avx512bw && has_avx512vl && has_avx512dq) { - avx512_bits |= SIMDID_AVX512_BASE; - } - if (has_avx512_vnni) - avx512_bits |= SIMDID_AVX512_VNNI; - if (has_bf16) - avx512_bits |= SIMDID_AVX512_BF16; - if (has_fp16) - avx512_bits |= SIMDID_AVX512_FP16; - if (has_vbmi) - avx512_bits |= SIMDID_AVX512_VBMI; - if (has_vbmi2) - avx512_bits |= SIMDID_AVX512_VBMI2; - - /* impl classification */ - const int glue = is_amd || is_intel_dual_avx512(family, model); - const int native512 = is_intel_native_avx512(family, model); - if (glue) { - avx512_bits |= SIMDID_AVX512_IMPL_DUAL; - } else if (native512) { - avx512_bits |= SIMDID_AVX512_IMPL_NATIVE; - } else { - avx512_bits |= SIMDID_AVX512_IMPL_UNKNOWN; - } - - /* main level for AVX512 */ - main_lvl = SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_AVX512); - - /* AMX promotion if present */ - if (has_amx_tile) { - amx_bits |= SIMDID_AMX_TILE; - if (has_amx_int8) - amx_bits |= SIMDID_AMX_INT8; - if (has_amx_bf16) - amx_bits |= SIMDID_AMX_BF16; - main_lvl = SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_AMX); - } - - /* AVX10 detection — requires AVX10 convergence bit - (leaf 7 subleaf 1 EDX[19]) and leaf 0x24 */ - if (has_avx10) { - int a10_eax = 0, a10_ebx = 0, a10_ecx = 0, a10_edx = 0; - get_cpuid(0x24, 0, &a10_eax, &a10_ebx, &a10_ecx, &a10_edx); - const int avx10_ver = a10_ebx & 0xFF; - if (avx10_ver >= 1) { - avx10_bits |= SIMDID_AVX10 | SIMDID_AVX10_V1; - if (avx10_ver >= 2) - avx10_bits |= SIMDID_AVX10_V2; - if (a10_ebx & (1 << 17)) - avx10_bits |= SIMDID_AVX10_256W; - if (a10_ebx & (1 << 18)) - avx10_bits |= SIMDID_AVX10_512W; - main_lvl = SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_AVX10); - } - } - } - } - - _simdid.mask = root | manu | main_lvl | SIMDID_PACK_AVX512(avx512_bits) | - SIMDID_PACK_AMX(amx_bits) | SIMDID_PACK_AVX(avx_bits) | - SIMDID_PACK_AVX10(avx10_bits); - return _simdid; -} - -SN_API SIMDID SN_DECL sn_get_simd_level() -{ - if (global_simdid.mask) { - return global_simdid; - } - global_simdid = sn_get_simd_level_local(); - return global_simdid; -} - -#else - -SIMDID sn_get_simd_level() -{ - SIMDID simd_id; - simd_id.mask = 0; - return simd_id; /* non-x86: no SIMD level probing */ -} - -#endif +#include "sn_target.h" +#include "sn_intrinsic.h" +#include "StgSharpNative.h" + +static SIMDID global_simdid = { 0 }; + +#if SN_IS_ARCH(SN_ARCH_X86_64) + +#include "sn_intrincontext.x86.h" + +#include +#include +#if defined(_MSC_VER) +#include +#else +#include +#endif + +static void get_cpuid(int leaf, int subleaf, int *eax, int *ebx, int *ecx, int *edx) +{ +#if defined(_MSC_VER) + int regs[4]; + __cpuidex(regs, leaf, subleaf); + *eax = regs[0]; + *ebx = regs[1]; + *ecx = regs[2]; + *edx = regs[3]; +#else + unsigned int a, b, c, d; + __cpuid_count((unsigned int)leaf, (unsigned int)subleaf, a, b, c, d); + *eax = (int)a; + *ebx = (int)b; + *ecx = (int)c; + *edx = (int)d; +#endif +} + +static int xgetbv_has_xmm_ymm(void) +{ +#if defined(_MSC_VER) + unsigned __int64 xcr0 = _xgetbv(0); +#else + unsigned long long xcr0 = __builtin_ia32_xgetbv(0); +#endif + return ((xcr0 & 0x6) == 0x6); +} + +static int xgetbv_has_avx512_state(void) +{ + const unsigned long long mask = (1ull << 0) | (1ull << 1) | (1ull << 2) | (1ull << 5) | + (1ull << 6); +#if defined(_MSC_VER) + unsigned __int64 xcr0 = _xgetbv(0); +#else + unsigned long long xcr0 = __builtin_ia32_xgetbv(0); +#endif + return ((xcr0 & mask) == mask); +} + +static int is_intel_dual_avx512(int family, int model) +{ + return (family == 6) && (model == 0x55); /* SKX/CLX/CPX */ +} + +static int is_intel_native_avx512(int family, int model) +{ + if (family != 6) + return 0; + switch (model) { + case 0x6A: /* ICX */ + case 0x6C: /* ICX stepping */ + case 0x8F: /* SPR/EMR */ + case 0xCF: /* GNR */ + return 1; + default: + return 0; + } +} + +SN_API SIMDID SN_DECL sn_get_simd_level_local() +{ + SIMDID _simdid = { 0 }; + + int eax = 0, ebx = 0, ecx = 0, edx = 0; + + /* Vendor */ + get_cpuid(0, 0, &eax, &ebx, &ecx, &edx); + char vendor[13]; + ((int *)vendor)[0] = ebx; + ((int *)vendor)[1] = edx; + ((int *)vendor)[2] = ecx; + vendor[12] = '\0'; + const int is_intel = (strcmp(vendor, "GenuineIntel") == 0); + const int is_amd = (strcmp(vendor, "AuthenticAMD") == 0) || + (strcmp(vendor, "HygonGenuine") == 0); + + /* CPUID.1 */ + get_cpuid(1, 0, &eax, &ebx, &ecx, &edx); + int family = ((eax >> 8) & 0xF) + ((eax >> 20) & 0xFF0); + int model = ((eax >> 4) & 0xF) | ((eax >> 12) & 0xF0); + + int has_sse42 = (ecx & (1 << 20)) != 0; + int has_avx = (ecx & (1 << 28)) != 0; + int has_osxsave = (ecx & (1 << 27)) != 0; + int has_fma = (ecx & (1 << 12)) != 0; + int has_f16c = (ecx & (1 << 29)) != 0; + + uint64_t root = SIMDID_PACK_ROOT(SIMDID_ROOT_X86_64); + uint64_t manu = SIMDID_PACK_MANU(is_intel ? SIMDID_MANU_INTEL : + is_amd ? SIMDID_MANU_AMD : + SIMDID_MANU_UNKNOWN); + uint64_t main_lvl = SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_NONE); + uint64_t avx512_bits = 0; + uint64_t amx_bits = 0; + uint64_t avx_bits = 0; + uint64_t avx10_bits = 0; + + /* Baseline main level */ + if (has_sse42) + main_lvl = SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_SSE); + + if (has_avx && has_osxsave && xgetbv_has_xmm_ymm()) { + avx_bits |= SIMDID_AVX; + + /* CPUID.7.0 */ + get_cpuid(7, 0, &eax, &ebx, &ecx, &edx); + const int has_bmi1 = (ebx & (1 << 3)) != 0; + const int has_bmi2 = (ebx & (1 << 8)) != 0; + const int has_avx2 = (ebx & (1 << 5)) != 0; + const int has_avx512f = (ebx & (1 << 16)) != 0; + const int has_avx512dq = (ebx & (1 << 17)) != 0; + const int has_avx512bw = (ebx & (1 << 30)) != 0; + const int has_avx512vl = (ebx & (1u << 31)) != 0; + const int has_avx512_vnni = (ebx & (1 << 11)) != 0; + const int has_vbmi = (ecx & (1 << 1)) != 0; + const int has_vbmi2 = (ecx & (1 << 6)) != 0; + + /* AMX in leaf 7 subleaf 0 EDX bits 24/25/26 — capture before + overwriting regs with subleaf 1 */ + const int has_amx_tile = (edx & (1 << 24)) != 0; + const int has_amx_int8 = (edx & (1 << 25)) != 0; + const int has_amx_bf16 = (edx & (1 << 22)) != 0; + + /* leaf 7, subleaf 1 for BF16/FP16/AVX10 convergence */ + get_cpuid(7, 1, &eax, &ebx, &ecx, &edx); + const int has_bf16 = (eax & (1 << 5)) != 0; /* AVX512_BF16 */ + const int has_fp16 = (eax & (1 << 23)) != 0; /* AVX512_FP16 */ + const int has_avx10 = (edx & (1 << 19)) != 0; /* AVX10 convergence */ + + if (has_avx2) { + avx_bits |= SIMDID_AVX2; + if (has_fma) + avx_bits |= SIMDID_AVX_FMA; + if (has_f16c) + avx_bits |= SIMDID_AVX_F16C; + + main_lvl = SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_AVX2); + } + + if (has_avx512f && xgetbv_has_avx512_state()) { + /* Ensure base subset present */ + if (has_avx512bw && has_avx512vl && has_avx512dq) { + avx512_bits |= SIMDID_AVX512_BASE; + } + if (has_avx512_vnni) + avx512_bits |= SIMDID_AVX512_VNNI; + if (has_bf16) + avx512_bits |= SIMDID_AVX512_BF16; + if (has_fp16) + avx512_bits |= SIMDID_AVX512_FP16; + if (has_vbmi) + avx512_bits |= SIMDID_AVX512_VBMI; + if (has_vbmi2) + avx512_bits |= SIMDID_AVX512_VBMI2; + + /* impl classification */ + const int glue = is_amd || is_intel_dual_avx512(family, model); + const int native512 = is_intel_native_avx512(family, model); + if (glue) { + avx512_bits |= SIMDID_AVX512_IMPL_DUAL; + } else if (native512) { + avx512_bits |= SIMDID_AVX512_IMPL_NATIVE; + } else { + avx512_bits |= SIMDID_AVX512_IMPL_UNKNOWN; + } + + /* main level for AVX512 */ + main_lvl = SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_AVX512); + + /* AMX promotion if present */ + if (has_amx_tile) { + amx_bits |= SIMDID_AMX_TILE; + if (has_amx_int8) + amx_bits |= SIMDID_AMX_INT8; + if (has_amx_bf16) + amx_bits |= SIMDID_AMX_BF16; + main_lvl = SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_AMX); + } + + /* AVX10 detection — requires AVX10 convergence bit + (leaf 7 subleaf 1 EDX[19]) and leaf 0x24 */ + if (has_avx10) { + int a10_eax = 0, a10_ebx = 0, a10_ecx = 0, a10_edx = 0; + get_cpuid(0x24, 0, &a10_eax, &a10_ebx, &a10_ecx, &a10_edx); + const int avx10_ver = a10_ebx & 0xFF; + if (avx10_ver >= 1) { + avx10_bits |= SIMDID_AVX10 | SIMDID_AVX10_V1; + if (avx10_ver >= 2) + avx10_bits |= SIMDID_AVX10_V2; + if (a10_ebx & (1 << 17)) + avx10_bits |= SIMDID_AVX10_256W; + if (a10_ebx & (1 << 18)) + avx10_bits |= SIMDID_AVX10_512W; + main_lvl = SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_AVX10); + } + } + } + } + + _simdid.mask = root | manu | main_lvl | SIMDID_PACK_AVX512(avx512_bits) | + SIMDID_PACK_AMX(amx_bits) | SIMDID_PACK_AVX(avx_bits) | + SIMDID_PACK_AVX10(avx10_bits); + 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 + +SIMDID sn_get_simd_level() +{ + SIMDID simd_id; + simd_id.mask = 0; + return simd_id; /* non-x86: no SIMD level probing */ +} + +#endif diff --git a/StgSharp.Native/sn-src/simdid_compare.x86.c b/StgSharp.Native/sn-src/simdid_compare.x86.c new file mode 100644 index 00000000..72849620 --- /dev/null +++ b/StgSharp.Native/sn-src/simdid_compare.x86.c @@ -0,0 +1,199 @@ +#include "sn_target.h" + +#if SN_IS_ARCH(SN_ARCH_X86_64) + +#include "StgSharpNative.h" +#include "sn_intrinsic.h" +#include "sn_intrincontext.x86.h" + +/* ----------------------------------------------------------------------- + * Field extraction helpers. + * + * SIMDID layout (64 bits, little-endian byte order in make_byte[]): + * [3..0] Root ISA make_byte[0] low nibble + * [7..4] Manufacture make_byte[0] high nibble + * [15..8] MainLevel make_byte[1] + * [31..16] AVX512 Feature make_byte[2..3] (16 bits) + * [39..32] AMX Feature make_byte[4] + * [47..40] AVX Feature make_byte[5] + * [55..48] AVX10 Feature make_byte[6] + * [63..56] uArchHi make_byte[7] + * ----------------------------------------------------------------------- */ + +#define FIELD_MAIN(id) ((unsigned char)(id).make_byte[1]) +#define FIELD_AVX512(id) ((uint16_t)((unsigned char)(id).make_byte[2] | \ + ((unsigned char)(id).make_byte[3] << 8))) +#define FIELD_AMX(id) ((unsigned char)(id).make_byte[4]) +#define FIELD_AVX(id) ((unsigned char)(id).make_byte[5]) +#define FIELD_AVX10(id) ((unsigned char)(id).make_byte[6]) +#define FIELD_UARCH(id) ((unsigned char)(id).make_byte[7]) + +/* Portable popcount for a 16-bit value. */ +static int popcount16(uint16_t v) +{ + int c = 0; + while (v) { v &= v - 1; c++; } + return c; +} + +/* Map AVX512_IMPL 2-bit field to a strength ordinal for tie-breaking. + * DUAL(1) => 0 (weakest), UNKNOWN(0) => 1, NATIVE(2) => 2 (strongest) */ +static int impl_strength(uint16_t avx512) +{ + unsigned impl = (avx512 >> SIMDID_AVX512_IMPL_SHIFT) & 0x3u; + switch (impl) { + case 1: return 0; /* DUAL — glue / downclock risk */ + case 0: return 1; /* UNKNOWN — conservatively mid */ + case 2: return 2; /* NATIVE — full-width ALU */ + default: return 1; /* reserved, treat as unknown */ + } +} + +/* ----------------------------------------------------------------------- + * sn_get_unite_simd + * + * Produce the conservative intersection of two SIMDIDs — the strongest + * capability set that is safe to use on BOTH cores: + * - MainLevel : min (only assume what both cores support) + * - AVX512 features : bitwise AND (IMPL field: pick the weaker one) + * - AMX features : bitwise AND + * - AVX features : bitwise AND + * - AVX10 features : bitwise AND + * - Root / Manu : keep left (must be same arch in practice) + * - uArchHi : min (most conservative policy) + * ----------------------------------------------------------------------- */ +SN_API SIMDID SN_DECL sn_get_unite_simd(SIMDID left, SIMDID right) +{ + SIMDID out; + out.mask = 0; + + /* Root + Manufacture (byte 0): prefer left (same arch assumed) */ + unsigned char lb0 = (unsigned char)left.make_byte[0]; + unsigned char rb0 = (unsigned char)right.make_byte[0]; + out.make_byte[0] = (char)(lb0 ? lb0 : rb0); + + /* MainLevel (byte 1): min */ + unsigned char lm = FIELD_MAIN(left); + unsigned char rm = FIELD_MAIN(right); + out.make_byte[1] = (char)(lm < rm ? lm : rm); + + /* AVX512 features (bytes 2-3): AND all bits, then fix IMPL to weaker */ + uint16_t l512 = FIELD_AVX512(left); + uint16_t r512 = FIELD_AVX512(right); + uint16_t merged = l512 & r512; + + /* Clear the IMPL field, then set the weaker one */ + int ls = impl_strength(l512); + int rs = impl_strength(r512); + unsigned weak_impl = (ls <= rs) + ? ((l512 >> SIMDID_AVX512_IMPL_SHIFT) & 0x3u) + : ((r512 >> SIMDID_AVX512_IMPL_SHIFT) & 0x3u); + merged = (merged & ~(uint16_t)SIMDID_AVX512_IMPL_MASK) + | (uint16_t)(weak_impl << SIMDID_AVX512_IMPL_SHIFT); + out.make_byte[2] = (char)(merged & 0xFF); + out.make_byte[3] = (char)((merged >> 8) & 0xFF); + + /* AMX (byte 4): AND */ + out.make_byte[4] = left.make_byte[4] & right.make_byte[4]; + + /* AVX (byte 5): AND */ + out.make_byte[5] = left.make_byte[5] & right.make_byte[5]; + + /* AVX10 (byte 6): AND */ + out.make_byte[6] = left.make_byte[6] & right.make_byte[6]; + + /* uArchHi (byte 7): min */ + unsigned char lu = FIELD_UARCH(left); + unsigned char ru = FIELD_UARCH(right); + out.make_byte[7] = (char)(lu < ru ? lu : ru); + + return out; +} + +/* ----------------------------------------------------------------------- + * sn_compare_simd + * + * Compare two SIMDIDs by ISA strength. Returns: + * +1 if left is strictly stronger than right + * -1 if left is strictly weaker than right + * 0 if equal + * + * Comparison priority (most significant first): + * + * 1. MainLevel ([15..8]) + * Higher value = stronger. This is the primary discriminator: + * NONE < SSE < AVX2 < AVX512 < AMX < AVX10 + * + * 2. AVX10 vector width ([55..48]) + * Only meaningful when both MainLevel == AVX10. + * 512W > 256W-only. Compared by the full AVX10 byte value since + * width bits are in higher positions than version bits. + * + * 3. AVX512 BASE bit (bit 4 of [31..16]) + * Only meaningful when both MainLevel >= AVX512. + * BASE=1 (F+CD+VL+DQ+BW all present) > BASE=0. + * + * 4. AVX512 extension popcount + * Count of set extension bits (VNNI/BF16/FP16/VBMI/VBMI2/AI_RESERVED). + * The IMPL and BASE bits are masked out before counting so they don't + * inflate the extension count. + * + * 5. AVX512 IMPL strength (tie-break, performance hint) + * DUAL(weakest) < UNKNOWN < NATIVE(strongest). + * This is not ISA capability but execution-width risk. + * + * 6. uArchHi (final tie-break) + * Higher value = more specific / stronger policy hint. + * ----------------------------------------------------------------------- */ +SN_API int SN_DECL sn_compare_simd(SIMDID left, SIMDID right) +{ + /* 1. MainLevel */ + unsigned lm = FIELD_MAIN(left); + unsigned rm = FIELD_MAIN(right); + if (lm != rm) + return lm > rm ? 1 : -1; + + /* 2. AVX10 width (only when MainLevel == AVX10) */ + if (lm >= SIMDID_MAIN_LVL_AVX10) { + unsigned la = FIELD_AVX10(left); + unsigned ra = FIELD_AVX10(right); + if (la != ra) + return la > ra ? 1 : -1; + } + + /* 3. AVX512 BASE (only when MainLevel >= AVX512) */ + if (lm >= SIMDID_MAIN_LVL_AVX512) { + uint16_t l512 = FIELD_AVX512(left); + uint16_t r512 = FIELD_AVX512(right); + + int lb = (l512 & SIMDID_AVX512_BASE) != 0; + int rb = (r512 & SIMDID_AVX512_BASE) != 0; + if (lb != rb) + return lb > rb ? 1 : -1; + + /* 4. Extension popcount (mask out BASE + IMPL + reserved low nibble) */ + uint16_t ext_mask = (uint16_t)~(SIMDID_AVX512_BASE + | SIMDID_AVX512_IMPL_MASK + | 0x000Fu); + int lp = popcount16(l512 & ext_mask); + int rp = popcount16(r512 & ext_mask); + if (lp != rp) + return lp > rp ? 1 : -1; + + /* 5. IMPL strength */ + int ls = impl_strength(l512); + int rs = impl_strength(r512); + if (ls != rs) + return ls > rs ? 1 : -1; + } + + /* 6. uArchHi (final tie-break) */ + unsigned lu = FIELD_UARCH(left); + unsigned ru = FIELD_UARCH(right); + if (lu != ru) + return lu > ru ? 1 : -1; + + return 0; +} + +#endif \ No newline at end of file diff --git a/StgSharp.Native/sn-src/t.affinity.c b/StgSharp.Native/sn-src/t.affinity.c index 6d6d0d7c..2a16de01 100644 --- a/StgSharp.Native/sn-src/t.affinity.c +++ b/StgSharp.Native/sn-src/t.affinity.c @@ -5,27 +5,51 @@ #define _GNU_SOURCE #include #include +#include #endif /* Pin the calling thread to a single logical processor. - * core_id: zero-based logical processor index. - * Silently ignored if core_id is out of range or the OS call fails. */ + * 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 (core_id < 0) - return; - #if SN_IS_OS(SN_OS_WINDOWS) - /* windows.h already included via sn_internal.h */ - if (core_id >= 64) + if (core_id < 0) { + /* Query which group the thread currently belongs to */ + GROUP_AFFINITY current; + GetThreadGroupAffinity(GetCurrentThread(), ¤t); + /* Build a full mask for that group's actual core count */ + DWORD count = GetActiveProcessorCount(current.Group); + GROUP_AFFINITY ga; + memset(&ga, 0, sizeof(ga)); + ga.Group = current.Group; + ga.Mask = (count >= 64) ? ~(KAFFINITY)0 + : ((KAFFINITY)1 << count) - 1; + SetThreadGroupAffinity(GetCurrentThread(), &ga, NULL); return; - DWORD_PTR mask = (DWORD_PTR)1 << core_id; - SetThreadAffinityMask(GetCurrentThread(), mask); + } + WORD group = (WORD)(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; + SetThreadGroupAffinity(GetCurrentThread(), &ga, NULL); #elif SN_IS_OS(SN_OS_LINUX) cpu_set_t set; - CPU_ZERO(&set); - CPU_SET(core_id, &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 -} \ No newline at end of file +}