working on per-PoolThread context loading and global context loading
This commit is contained in:
@@ -26,6 +26,8 @@
|
|||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
using StgSharp.Collections;
|
using StgSharp.Collections;
|
||||||
|
using StgSharp.HighPerformance.Memory;
|
||||||
|
using StgSharp.Internal.Intrinsic;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -41,45 +43,31 @@ namespace StgSharp.Mathematics.Numeric
|
|||||||
|
|
||||||
private static CapacityFixedStack<MatrixParallelThread> _threads;
|
private static CapacityFixedStack<MatrixParallelThread> _threads;
|
||||||
|
|
||||||
private static readonly
|
private static SlabAllocator<IntrinsicContext> contextAllocator;
|
||||||
#if NET10_0_OR_GREATER
|
|
||||||
Lock
|
|
||||||
#else
|
|
||||||
object
|
|
||||||
#endif
|
|
||||||
_lock = new();
|
|
||||||
|
|
||||||
// private static ThreadPriority _AfterWork = ThreadPriority.Lowest;
|
public static bool IsParallelSupported { get; } = Environment.ProcessorCount > 6;
|
||||||
|
|
||||||
public static bool SupportParallel { get; } = Environment.ProcessorCount > 6;
|
|
||||||
|
|
||||||
public static bool IsParallelMeaningful => Environment.ProcessorCount > 6 && IsInitialized;
|
public static bool IsParallelMeaningful => Environment.ProcessorCount > 6 && IsInitialized;
|
||||||
|
|
||||||
private static bool IsInitialized { get; set; } = false;
|
private static bool IsInitialized { get; set; } = false;
|
||||||
|
|
||||||
private static Queue<IntPtr> LeaderTask { get; set; }
|
|
||||||
|
|
||||||
public static void EndParallel()
|
|
||||||
{
|
|
||||||
Monitor.Exit(_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void Init()
|
internal static void Init()
|
||||||
{
|
{
|
||||||
if (IsInitialized) {
|
if (IsInitialized) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
MatrixParallelFactory.Init();
|
if (!IsParallelSupported)
|
||||||
if (!SupportParallel)
|
|
||||||
{
|
{
|
||||||
IsInitialized = true;
|
IsInitialized = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
MatrixParallelFactory.Init();
|
||||||
|
contextAllocator = SlabAllocator<IntrinsicContext>.Create(4, SlabBufferLayout.Chunked, false);
|
||||||
int count = Environment.ProcessorCount - 4;
|
int count = Environment.ProcessorCount - 4;
|
||||||
_threads = new(count);
|
_threads = new(count);
|
||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
MatrixParallelThread thread = new MatrixParallelThread();
|
MatrixParallelThread thread = new MatrixParallelThread(i);
|
||||||
_threads.Push(thread);
|
_threads.Push(thread);
|
||||||
}
|
}
|
||||||
_awaitingLeaderHandle = new AwaitingQueue();
|
_awaitingLeaderHandle = new AwaitingQueue();
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ namespace StgSharp.Mathematics.Numeric
|
|||||||
public enum MatrixIndexStyle : short
|
public enum MatrixIndexStyle : short
|
||||||
{
|
{
|
||||||
|
|
||||||
|
SPECIAL = 0,
|
||||||
BUFFER_INDEX = 1,
|
BUFFER_INDEX = 1,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ using System.Runtime.CompilerServices;
|
|||||||
|
|
||||||
namespace StgSharp.Mathematics.Numeric
|
namespace StgSharp.Mathematics.Numeric
|
||||||
{
|
{
|
||||||
internal static class MatrixComputeModel
|
public partial class MatrixParallelThread
|
||||||
{
|
{
|
||||||
|
|
||||||
internal static unsafe void BufferComputeBinary(
|
internal static unsafe void BufferComputeBinary(
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
using StgSharp.Internal;
|
using StgSharp.Internal;
|
||||||
|
using StgSharp.Internal.Intrinsic;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
@@ -46,34 +47,30 @@ namespace StgSharp.Mathematics.Numeric
|
|||||||
private readonly ManualResetEventSlim _resetEvent;
|
private readonly ManualResetEventSlim _resetEvent;
|
||||||
private readonly Thread _managedThread;
|
private readonly Thread _managedThread;
|
||||||
|
|
||||||
public MatrixParallelThread()
|
internal MatrixParallelThread(
|
||||||
|
int poolId
|
||||||
|
)
|
||||||
{
|
{
|
||||||
_resetEvent = new ManualResetEventSlim(false);
|
_resetEvent = new ManualResetEventSlim(false);
|
||||||
_managedThread = new Thread(MatrixParallelWorker);
|
_managedThread = new Thread(MatrixParallelWorkLoad);
|
||||||
_managedThread.Start();
|
_managedThread.Start();
|
||||||
|
PoolId = poolId;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ~MatrixComputeThread()
|
public int PoolId { get; init; }
|
||||||
// {
|
|
||||||
// Dispose(disposing: false);
|
|
||||||
// }
|
|
||||||
|
|
||||||
public ThreadPriority AfterWork { get; set; }
|
public ThreadPriority AfterWork { get; set; }
|
||||||
|
|
||||||
internal ManualResetEventSlim? LeaderEvent { get; set; }
|
internal ManualResetEventSlim? LeaderEvent { get; set; }
|
||||||
|
|
||||||
[DllImport(
|
internal IntrinsicContext* Intrinsic { get; set; }
|
||||||
dllName:Native.LibName,
|
|
||||||
EntryPoint = "sn_set_thread_affinity",
|
|
||||||
CallingConvention = CallingConvention.Cdecl)]
|
|
||||||
internal static extern void SetThreadAffinity(
|
|
||||||
ulong affinityMask
|
|
||||||
);
|
|
||||||
|
|
||||||
#region wrap belong
|
|
||||||
|
|
||||||
internal MatrixParallelWrap BindedWrap { get; set; }
|
internal MatrixParallelWrap BindedWrap { get; set; }
|
||||||
|
|
||||||
|
internal RoleType Role { get; set; }
|
||||||
|
|
||||||
|
internal SIMDID Simd { get; set; }
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
Dispose(disposing:true);
|
Dispose(disposing:true);
|
||||||
@@ -146,6 +143,14 @@ namespace StgSharp.Mathematics.Numeric
|
|||||||
current.Priority = BindedWrap.AfterWork;
|
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(
|
protected virtual void Dispose(
|
||||||
bool disposing
|
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
|
// Bind OS-thread affinity once when the worker starts running
|
||||||
|
SetThreadAffinity(PoolId);
|
||||||
|
Thread.Sleep(0);
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
// Thread self = Thread.CurrentThread;
|
// Thread self = Thread.CurrentThread;
|
||||||
AfterWork = BindedWrap.AfterWork;
|
|
||||||
_managedThread.Priority = ThreadPriority.Highest;
|
|
||||||
_resetEvent.Wait();
|
_resetEvent.Wait();
|
||||||
_resetEvent.Reset();
|
_resetEvent.Reset();
|
||||||
|
AfterWork = BindedWrap.AfterWork;
|
||||||
|
_managedThread.Priority = ThreadPriority.Highest;
|
||||||
switch (Role)
|
switch (Role)
|
||||||
{
|
{
|
||||||
case RoleType.Worker://worker
|
case RoleType.Worker://worker
|
||||||
@@ -183,7 +190,9 @@ namespace StgSharp.Mathematics.Numeric
|
|||||||
|
|
||||||
// Console.WriteLine($"worker {Environment.CurrentManagedThreadId} end");
|
// Console.WriteLine($"worker {Environment.CurrentManagedThreadId} end");
|
||||||
break;
|
break;
|
||||||
case RoleType.Leader://leader, error because leader should be at outer thread but not here
|
case RoleType.SingleExternal:
|
||||||
|
|
||||||
|
// TODO 将外部线程绑定到当前线程上,并执行任务包
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -192,16 +201,14 @@ namespace StgSharp.Mathematics.Numeric
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public RoleType Role { get; internal set; }
|
internal enum RoleType : int
|
||||||
|
|
||||||
public enum RoleType : int
|
|
||||||
{
|
{
|
||||||
|
|
||||||
Worker = 0,
|
Worker = 0,
|
||||||
Leader = 2,
|
Leader = 1,
|
||||||
|
SingleExternal = 2,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,11 @@
|
|||||||
<Configurations>Debug;Release;PerformanceDebug</Configurations>
|
<Configurations>Debug;Release;PerformanceDebug</Configurations>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Remove="Numeric\MatrixParallelHandle.cs.old" />
|
||||||
|
<None Remove="Numeric\MatrixParallelWrap.cs.old" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\StgSharp.Common\StgSharp.Common.csproj" />
|
<ProjectReference Include="..\StgSharp.Common\StgSharp.Common.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -168,7 +168,8 @@
|
|||||||
<FileType>CCode</FileType>
|
<FileType>CCode</FileType>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="sn-src\load_glfw.c" />
|
<ClCompile Include="sn-src\load_glfw.c" />
|
||||||
<ClCompile Include="sn-src\cpuid.x86.c" />
|
<ClCompile Include="sn-src\simdid.x86.c" />
|
||||||
|
<ClCompile Include="sn-src\simdid_compare.x86.c" />
|
||||||
<ClCompile Include="sn-src\sn_native.c" />
|
<ClCompile Include="sn-src\sn_native.c" />
|
||||||
<ClCompile Include="sn-src\str_string.c" />
|
<ClCompile Include="sn-src\str_string.c" />
|
||||||
<ClCompile Include="sn-src\t.affinity.c" />
|
<ClCompile Include="sn-src\t.affinity.c" />
|
||||||
|
|||||||
@@ -60,7 +60,7 @@
|
|||||||
<ClCompile Include="sn-src\m.solve.x86.c">
|
<ClCompile Include="sn-src\m.solve.x86.c">
|
||||||
<Filter>源文件</Filter>
|
<Filter>源文件</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="sn-src\cpuid.x86.c">
|
<ClCompile Include="sn-src\simdid.x86.c">
|
||||||
<Filter>源文件</Filter>
|
<Filter>源文件</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="sn-src\m.fma_tile.x86.c">
|
<ClCompile Include="sn-src\m.fma_tile.x86.c">
|
||||||
@@ -69,6 +69,9 @@
|
|||||||
<ClCompile Include="sn-src\t.affinity.c">
|
<ClCompile Include="sn-src\t.affinity.c">
|
||||||
<Filter>源文件</Filter>
|
<Filter>源文件</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="sn-src\simdid_compare.x86.c">
|
||||||
|
<Filter>源文件</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="sn-head\gl.h">
|
<ClInclude Include="sn-head\gl.h">
|
||||||
|
|||||||
@@ -66,10 +66,12 @@ SN_API void SN_DECL unloadImageData(Image *out);
|
|||||||
SN_API char *SN_DECL readLog(void);
|
SN_API char *SN_DECL readLog(void);
|
||||||
SN_API void SN_DECL load_glfw_functions(GLFWFunctionTable *table);
|
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 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.
|
/* Per-core variant: always re-executes CPUID, never caches.
|
||||||
* Must be called on the target core (after affinity is set). */
|
* 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_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);
|
SN_API void SN_DECL sn_set_thread_affinity(int core_id);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ SN_API SIMDID load_intrinsic_function(void *intrinsic_context, uint64_t id)
|
|||||||
if (id) {
|
if (id) {
|
||||||
simdid.mask = id;
|
simdid.mask = id;
|
||||||
} else {
|
} else {
|
||||||
simdid = sn_get_simd_level();
|
simdid = sn_get_simd_level_global();
|
||||||
}
|
}
|
||||||
|
|
||||||
sn_intrinsic *context = (sn_intrinsic *)intrinsic_context;
|
sn_intrinsic *context = (sn_intrinsic *)intrinsic_context;
|
||||||
|
|||||||
@@ -1,244 +1,244 @@
|
|||||||
#include "sn_target.h"
|
#include "sn_target.h"
|
||||||
#include "sn_intrinsic.h"
|
#include "sn_intrinsic.h"
|
||||||
#include "StgSharpNative.h"
|
#include "StgSharpNative.h"
|
||||||
|
|
||||||
static SIMDID global_simdid = { 0 };
|
static SIMDID global_simdid = { 0 };
|
||||||
|
|
||||||
#if SN_IS_ARCH(SN_ARCH_X86_64)
|
#if SN_IS_ARCH(SN_ARCH_X86_64)
|
||||||
|
|
||||||
#include "sn_intrincontext.x86.h"
|
#include "sn_intrincontext.x86.h"
|
||||||
|
|
||||||
#include <immintrin.h>
|
#include <immintrin.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#if defined(_MSC_VER)
|
#if defined(_MSC_VER)
|
||||||
#include <intrin.h>
|
#include <intrin.h>
|
||||||
#else
|
#else
|
||||||
#include <cpuid.h>
|
#include <cpuid.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void get_cpuid(int leaf, int subleaf, int *eax, int *ebx, int *ecx, int *edx)
|
static void get_cpuid(int leaf, int subleaf, int *eax, int *ebx, int *ecx, int *edx)
|
||||||
{
|
{
|
||||||
#if defined(_MSC_VER)
|
#if defined(_MSC_VER)
|
||||||
int regs[4];
|
int regs[4];
|
||||||
__cpuidex(regs, leaf, subleaf);
|
__cpuidex(regs, leaf, subleaf);
|
||||||
*eax = regs[0];
|
*eax = regs[0];
|
||||||
*ebx = regs[1];
|
*ebx = regs[1];
|
||||||
*ecx = regs[2];
|
*ecx = regs[2];
|
||||||
*edx = regs[3];
|
*edx = regs[3];
|
||||||
#else
|
#else
|
||||||
unsigned int a, b, c, d;
|
unsigned int a, b, c, d;
|
||||||
__cpuid_count((unsigned int)leaf, (unsigned int)subleaf, a, b, c, d);
|
__cpuid_count((unsigned int)leaf, (unsigned int)subleaf, a, b, c, d);
|
||||||
*eax = (int)a;
|
*eax = (int)a;
|
||||||
*ebx = (int)b;
|
*ebx = (int)b;
|
||||||
*ecx = (int)c;
|
*ecx = (int)c;
|
||||||
*edx = (int)d;
|
*edx = (int)d;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static int xgetbv_has_xmm_ymm(void)
|
static int xgetbv_has_xmm_ymm(void)
|
||||||
{
|
{
|
||||||
#if defined(_MSC_VER)
|
#if defined(_MSC_VER)
|
||||||
unsigned __int64 xcr0 = _xgetbv(0);
|
unsigned __int64 xcr0 = _xgetbv(0);
|
||||||
#else
|
#else
|
||||||
unsigned long long xcr0 = __builtin_ia32_xgetbv(0);
|
unsigned long long xcr0 = __builtin_ia32_xgetbv(0);
|
||||||
#endif
|
#endif
|
||||||
return ((xcr0 & 0x6) == 0x6);
|
return ((xcr0 & 0x6) == 0x6);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int xgetbv_has_avx512_state(void)
|
static int xgetbv_has_avx512_state(void)
|
||||||
{
|
{
|
||||||
const unsigned long long mask = (1ull << 0) | (1ull << 1) | (1ull << 2) | (1ull << 5) |
|
const unsigned long long mask = (1ull << 0) | (1ull << 1) | (1ull << 2) | (1ull << 5) |
|
||||||
(1ull << 6);
|
(1ull << 6);
|
||||||
#if defined(_MSC_VER)
|
#if defined(_MSC_VER)
|
||||||
unsigned __int64 xcr0 = _xgetbv(0);
|
unsigned __int64 xcr0 = _xgetbv(0);
|
||||||
#else
|
#else
|
||||||
unsigned long long xcr0 = __builtin_ia32_xgetbv(0);
|
unsigned long long xcr0 = __builtin_ia32_xgetbv(0);
|
||||||
#endif
|
#endif
|
||||||
return ((xcr0 & mask) == mask);
|
return ((xcr0 & mask) == mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int is_intel_dual_avx512(int family, int model)
|
static int is_intel_dual_avx512(int family, int model)
|
||||||
{
|
{
|
||||||
return (family == 6) && (model == 0x55); /* SKX/CLX/CPX */
|
return (family == 6) && (model == 0x55); /* SKX/CLX/CPX */
|
||||||
}
|
}
|
||||||
|
|
||||||
static int is_intel_native_avx512(int family, int model)
|
static int is_intel_native_avx512(int family, int model)
|
||||||
{
|
{
|
||||||
if (family != 6)
|
if (family != 6)
|
||||||
return 0;
|
return 0;
|
||||||
switch (model) {
|
switch (model) {
|
||||||
case 0x6A: /* ICX */
|
case 0x6A: /* ICX */
|
||||||
case 0x6C: /* ICX stepping */
|
case 0x6C: /* ICX stepping */
|
||||||
case 0x8F: /* SPR/EMR */
|
case 0x8F: /* SPR/EMR */
|
||||||
case 0xCF: /* GNR */
|
case 0xCF: /* GNR */
|
||||||
return 1;
|
return 1;
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SN_API SIMDID SN_DECL sn_get_simd_level_local()
|
SN_API SIMDID SN_DECL sn_get_simd_level_local()
|
||||||
{
|
{
|
||||||
SIMDID _simdid = { 0 };
|
SIMDID _simdid = { 0 };
|
||||||
|
|
||||||
int eax = 0, ebx = 0, ecx = 0, edx = 0;
|
int eax = 0, ebx = 0, ecx = 0, edx = 0;
|
||||||
|
|
||||||
/* Vendor */
|
/* Vendor */
|
||||||
get_cpuid(0, 0, &eax, &ebx, &ecx, &edx);
|
get_cpuid(0, 0, &eax, &ebx, &ecx, &edx);
|
||||||
char vendor[13];
|
char vendor[13];
|
||||||
((int *)vendor)[0] = ebx;
|
((int *)vendor)[0] = ebx;
|
||||||
((int *)vendor)[1] = edx;
|
((int *)vendor)[1] = edx;
|
||||||
((int *)vendor)[2] = ecx;
|
((int *)vendor)[2] = ecx;
|
||||||
vendor[12] = '\0';
|
vendor[12] = '\0';
|
||||||
const int is_intel = (strcmp(vendor, "GenuineIntel") == 0);
|
const int is_intel = (strcmp(vendor, "GenuineIntel") == 0);
|
||||||
const int is_amd = (strcmp(vendor, "AuthenticAMD") == 0) ||
|
const int is_amd = (strcmp(vendor, "AuthenticAMD") == 0) ||
|
||||||
(strcmp(vendor, "HygonGenuine") == 0);
|
(strcmp(vendor, "HygonGenuine") == 0);
|
||||||
|
|
||||||
/* CPUID.1 */
|
/* CPUID.1 */
|
||||||
get_cpuid(1, 0, &eax, &ebx, &ecx, &edx);
|
get_cpuid(1, 0, &eax, &ebx, &ecx, &edx);
|
||||||
int family = ((eax >> 8) & 0xF) + ((eax >> 20) & 0xFF0);
|
int family = ((eax >> 8) & 0xF) + ((eax >> 20) & 0xFF0);
|
||||||
int model = ((eax >> 4) & 0xF) | ((eax >> 12) & 0xF0);
|
int model = ((eax >> 4) & 0xF) | ((eax >> 12) & 0xF0);
|
||||||
|
|
||||||
int has_sse42 = (ecx & (1 << 20)) != 0;
|
int has_sse42 = (ecx & (1 << 20)) != 0;
|
||||||
int has_avx = (ecx & (1 << 28)) != 0;
|
int has_avx = (ecx & (1 << 28)) != 0;
|
||||||
int has_osxsave = (ecx & (1 << 27)) != 0;
|
int has_osxsave = (ecx & (1 << 27)) != 0;
|
||||||
int has_fma = (ecx & (1 << 12)) != 0;
|
int has_fma = (ecx & (1 << 12)) != 0;
|
||||||
int has_f16c = (ecx & (1 << 29)) != 0;
|
int has_f16c = (ecx & (1 << 29)) != 0;
|
||||||
|
|
||||||
uint64_t root = SIMDID_PACK_ROOT(SIMDID_ROOT_X86_64);
|
uint64_t root = SIMDID_PACK_ROOT(SIMDID_ROOT_X86_64);
|
||||||
uint64_t manu = SIMDID_PACK_MANU(is_intel ? SIMDID_MANU_INTEL :
|
uint64_t manu = SIMDID_PACK_MANU(is_intel ? SIMDID_MANU_INTEL :
|
||||||
is_amd ? SIMDID_MANU_AMD :
|
is_amd ? SIMDID_MANU_AMD :
|
||||||
SIMDID_MANU_UNKNOWN);
|
SIMDID_MANU_UNKNOWN);
|
||||||
uint64_t main_lvl = SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_NONE);
|
uint64_t main_lvl = SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_NONE);
|
||||||
uint64_t avx512_bits = 0;
|
uint64_t avx512_bits = 0;
|
||||||
uint64_t amx_bits = 0;
|
uint64_t amx_bits = 0;
|
||||||
uint64_t avx_bits = 0;
|
uint64_t avx_bits = 0;
|
||||||
uint64_t avx10_bits = 0;
|
uint64_t avx10_bits = 0;
|
||||||
|
|
||||||
/* Baseline main level */
|
/* Baseline main level */
|
||||||
if (has_sse42)
|
if (has_sse42)
|
||||||
main_lvl = SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_SSE);
|
main_lvl = SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_SSE);
|
||||||
|
|
||||||
if (has_avx && has_osxsave && xgetbv_has_xmm_ymm()) {
|
if (has_avx && has_osxsave && xgetbv_has_xmm_ymm()) {
|
||||||
avx_bits |= SIMDID_AVX;
|
avx_bits |= SIMDID_AVX;
|
||||||
|
|
||||||
/* CPUID.7.0 */
|
/* CPUID.7.0 */
|
||||||
get_cpuid(7, 0, &eax, &ebx, &ecx, &edx);
|
get_cpuid(7, 0, &eax, &ebx, &ecx, &edx);
|
||||||
const int has_bmi1 = (ebx & (1 << 3)) != 0;
|
const int has_bmi1 = (ebx & (1 << 3)) != 0;
|
||||||
const int has_bmi2 = (ebx & (1 << 8)) != 0;
|
const int has_bmi2 = (ebx & (1 << 8)) != 0;
|
||||||
const int has_avx2 = (ebx & (1 << 5)) != 0;
|
const int has_avx2 = (ebx & (1 << 5)) != 0;
|
||||||
const int has_avx512f = (ebx & (1 << 16)) != 0;
|
const int has_avx512f = (ebx & (1 << 16)) != 0;
|
||||||
const int has_avx512dq = (ebx & (1 << 17)) != 0;
|
const int has_avx512dq = (ebx & (1 << 17)) != 0;
|
||||||
const int has_avx512bw = (ebx & (1 << 30)) != 0;
|
const int has_avx512bw = (ebx & (1 << 30)) != 0;
|
||||||
const int has_avx512vl = (ebx & (1u << 31)) != 0;
|
const int has_avx512vl = (ebx & (1u << 31)) != 0;
|
||||||
const int has_avx512_vnni = (ebx & (1 << 11)) != 0;
|
const int has_avx512_vnni = (ebx & (1 << 11)) != 0;
|
||||||
const int has_vbmi = (ecx & (1 << 1)) != 0;
|
const int has_vbmi = (ecx & (1 << 1)) != 0;
|
||||||
const int has_vbmi2 = (ecx & (1 << 6)) != 0;
|
const int has_vbmi2 = (ecx & (1 << 6)) != 0;
|
||||||
|
|
||||||
/* AMX in leaf 7 subleaf 0 EDX bits 24/25/26 — capture before
|
/* AMX in leaf 7 subleaf 0 EDX bits 24/25/26 — capture before
|
||||||
overwriting regs with subleaf 1 */
|
overwriting regs with subleaf 1 */
|
||||||
const int has_amx_tile = (edx & (1 << 24)) != 0;
|
const int has_amx_tile = (edx & (1 << 24)) != 0;
|
||||||
const int has_amx_int8 = (edx & (1 << 25)) != 0;
|
const int has_amx_int8 = (edx & (1 << 25)) != 0;
|
||||||
const int has_amx_bf16 = (edx & (1 << 22)) != 0;
|
const int has_amx_bf16 = (edx & (1 << 22)) != 0;
|
||||||
|
|
||||||
/* leaf 7, subleaf 1 for BF16/FP16/AVX10 convergence */
|
/* leaf 7, subleaf 1 for BF16/FP16/AVX10 convergence */
|
||||||
get_cpuid(7, 1, &eax, &ebx, &ecx, &edx);
|
get_cpuid(7, 1, &eax, &ebx, &ecx, &edx);
|
||||||
const int has_bf16 = (eax & (1 << 5)) != 0; /* AVX512_BF16 */
|
const int has_bf16 = (eax & (1 << 5)) != 0; /* AVX512_BF16 */
|
||||||
const int has_fp16 = (eax & (1 << 23)) != 0; /* AVX512_FP16 */
|
const int has_fp16 = (eax & (1 << 23)) != 0; /* AVX512_FP16 */
|
||||||
const int has_avx10 = (edx & (1 << 19)) != 0; /* AVX10 convergence */
|
const int has_avx10 = (edx & (1 << 19)) != 0; /* AVX10 convergence */
|
||||||
|
|
||||||
if (has_avx2) {
|
if (has_avx2) {
|
||||||
avx_bits |= SIMDID_AVX2;
|
avx_bits |= SIMDID_AVX2;
|
||||||
if (has_fma)
|
if (has_fma)
|
||||||
avx_bits |= SIMDID_AVX_FMA;
|
avx_bits |= SIMDID_AVX_FMA;
|
||||||
if (has_f16c)
|
if (has_f16c)
|
||||||
avx_bits |= SIMDID_AVX_F16C;
|
avx_bits |= SIMDID_AVX_F16C;
|
||||||
|
|
||||||
main_lvl = SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_AVX2);
|
main_lvl = SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_AVX2);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (has_avx512f && xgetbv_has_avx512_state()) {
|
if (has_avx512f && xgetbv_has_avx512_state()) {
|
||||||
/* Ensure base subset present */
|
/* Ensure base subset present */
|
||||||
if (has_avx512bw && has_avx512vl && has_avx512dq) {
|
if (has_avx512bw && has_avx512vl && has_avx512dq) {
|
||||||
avx512_bits |= SIMDID_AVX512_BASE;
|
avx512_bits |= SIMDID_AVX512_BASE;
|
||||||
}
|
}
|
||||||
if (has_avx512_vnni)
|
if (has_avx512_vnni)
|
||||||
avx512_bits |= SIMDID_AVX512_VNNI;
|
avx512_bits |= SIMDID_AVX512_VNNI;
|
||||||
if (has_bf16)
|
if (has_bf16)
|
||||||
avx512_bits |= SIMDID_AVX512_BF16;
|
avx512_bits |= SIMDID_AVX512_BF16;
|
||||||
if (has_fp16)
|
if (has_fp16)
|
||||||
avx512_bits |= SIMDID_AVX512_FP16;
|
avx512_bits |= SIMDID_AVX512_FP16;
|
||||||
if (has_vbmi)
|
if (has_vbmi)
|
||||||
avx512_bits |= SIMDID_AVX512_VBMI;
|
avx512_bits |= SIMDID_AVX512_VBMI;
|
||||||
if (has_vbmi2)
|
if (has_vbmi2)
|
||||||
avx512_bits |= SIMDID_AVX512_VBMI2;
|
avx512_bits |= SIMDID_AVX512_VBMI2;
|
||||||
|
|
||||||
/* impl classification */
|
/* impl classification */
|
||||||
const int glue = is_amd || is_intel_dual_avx512(family, model);
|
const int glue = is_amd || is_intel_dual_avx512(family, model);
|
||||||
const int native512 = is_intel_native_avx512(family, model);
|
const int native512 = is_intel_native_avx512(family, model);
|
||||||
if (glue) {
|
if (glue) {
|
||||||
avx512_bits |= SIMDID_AVX512_IMPL_DUAL;
|
avx512_bits |= SIMDID_AVX512_IMPL_DUAL;
|
||||||
} else if (native512) {
|
} else if (native512) {
|
||||||
avx512_bits |= SIMDID_AVX512_IMPL_NATIVE;
|
avx512_bits |= SIMDID_AVX512_IMPL_NATIVE;
|
||||||
} else {
|
} else {
|
||||||
avx512_bits |= SIMDID_AVX512_IMPL_UNKNOWN;
|
avx512_bits |= SIMDID_AVX512_IMPL_UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* main level for AVX512 */
|
/* main level for AVX512 */
|
||||||
main_lvl = SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_AVX512);
|
main_lvl = SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_AVX512);
|
||||||
|
|
||||||
/* AMX promotion if present */
|
/* AMX promotion if present */
|
||||||
if (has_amx_tile) {
|
if (has_amx_tile) {
|
||||||
amx_bits |= SIMDID_AMX_TILE;
|
amx_bits |= SIMDID_AMX_TILE;
|
||||||
if (has_amx_int8)
|
if (has_amx_int8)
|
||||||
amx_bits |= SIMDID_AMX_INT8;
|
amx_bits |= SIMDID_AMX_INT8;
|
||||||
if (has_amx_bf16)
|
if (has_amx_bf16)
|
||||||
amx_bits |= SIMDID_AMX_BF16;
|
amx_bits |= SIMDID_AMX_BF16;
|
||||||
main_lvl = SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_AMX);
|
main_lvl = SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_AMX);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* AVX10 detection — requires AVX10 convergence bit
|
/* AVX10 detection — requires AVX10 convergence bit
|
||||||
(leaf 7 subleaf 1 EDX[19]) and leaf 0x24 */
|
(leaf 7 subleaf 1 EDX[19]) and leaf 0x24 */
|
||||||
if (has_avx10) {
|
if (has_avx10) {
|
||||||
int a10_eax = 0, a10_ebx = 0, a10_ecx = 0, a10_edx = 0;
|
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);
|
get_cpuid(0x24, 0, &a10_eax, &a10_ebx, &a10_ecx, &a10_edx);
|
||||||
const int avx10_ver = a10_ebx & 0xFF;
|
const int avx10_ver = a10_ebx & 0xFF;
|
||||||
if (avx10_ver >= 1) {
|
if (avx10_ver >= 1) {
|
||||||
avx10_bits |= SIMDID_AVX10 | SIMDID_AVX10_V1;
|
avx10_bits |= SIMDID_AVX10 | SIMDID_AVX10_V1;
|
||||||
if (avx10_ver >= 2)
|
if (avx10_ver >= 2)
|
||||||
avx10_bits |= SIMDID_AVX10_V2;
|
avx10_bits |= SIMDID_AVX10_V2;
|
||||||
if (a10_ebx & (1 << 17))
|
if (a10_ebx & (1 << 17))
|
||||||
avx10_bits |= SIMDID_AVX10_256W;
|
avx10_bits |= SIMDID_AVX10_256W;
|
||||||
if (a10_ebx & (1 << 18))
|
if (a10_ebx & (1 << 18))
|
||||||
avx10_bits |= SIMDID_AVX10_512W;
|
avx10_bits |= SIMDID_AVX10_512W;
|
||||||
main_lvl = SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_AVX10);
|
main_lvl = SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_AVX10);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_simdid.mask = root | manu | main_lvl | SIMDID_PACK_AVX512(avx512_bits) |
|
_simdid.mask = root | manu | main_lvl | SIMDID_PACK_AVX512(avx512_bits) |
|
||||||
SIMDID_PACK_AMX(amx_bits) | SIMDID_PACK_AVX(avx_bits) |
|
SIMDID_PACK_AMX(amx_bits) | SIMDID_PACK_AVX(avx_bits) |
|
||||||
SIMDID_PACK_AVX10(avx10_bits);
|
SIMDID_PACK_AVX10(avx10_bits);
|
||||||
return _simdid;
|
return _simdid;
|
||||||
}
|
}
|
||||||
|
|
||||||
SN_API SIMDID SN_DECL sn_get_simd_level()
|
SN_API SIMDID SN_DECL sn_get_simd_level_global()
|
||||||
{
|
{
|
||||||
if (global_simdid.mask) {
|
if (global_simdid.mask) {
|
||||||
return global_simdid;
|
return global_simdid;
|
||||||
}
|
}
|
||||||
global_simdid = sn_get_simd_level_local();
|
global_simdid = sn_get_simd_level_local();
|
||||||
return global_simdid;
|
return global_simdid;
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
SIMDID sn_get_simd_level()
|
SIMDID sn_get_simd_level()
|
||||||
{
|
{
|
||||||
SIMDID simd_id;
|
SIMDID simd_id;
|
||||||
simd_id.mask = 0;
|
simd_id.mask = 0;
|
||||||
return simd_id; /* non-x86: no SIMD level probing */
|
return simd_id; /* non-x86: no SIMD level probing */
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -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
|
||||||
@@ -5,27 +5,51 @@
|
|||||||
#define _GNU_SOURCE
|
#define _GNU_SOURCE
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <sched.h>
|
#include <sched.h>
|
||||||
|
#include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Pin the calling thread to a single logical processor.
|
/* Pin the calling thread to a single logical processor.
|
||||||
* core_id: zero-based logical processor index.
|
* core_id >= 0 : bind to that logical processor (global index, across groups).
|
||||||
* Silently ignored if core_id is out of range or the OS call fails. */
|
* core_id < 0 : clear affinity constraint within the current group. */
|
||||||
SN_API void SN_DECL sn_set_thread_affinity(int core_id)
|
SN_API void SN_DECL sn_set_thread_affinity(int core_id)
|
||||||
{
|
{
|
||||||
if (core_id < 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
#if SN_IS_OS(SN_OS_WINDOWS)
|
#if SN_IS_OS(SN_OS_WINDOWS)
|
||||||
/* windows.h already included via sn_internal.h */
|
if (core_id < 0) {
|
||||||
if (core_id >= 64)
|
/* 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;
|
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)
|
#elif SN_IS_OS(SN_OS_LINUX)
|
||||||
cpu_set_t set;
|
cpu_set_t set;
|
||||||
CPU_ZERO(&set);
|
if (core_id < 0) {
|
||||||
CPU_SET(core_id, &set);
|
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);
|
pthread_setaffinity_np(pthread_self(), sizeof(set), &set);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user