many versions of simdid
This commit is contained in:
@@ -2,8 +2,8 @@
|
|||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
// file="MatrixParallel"
|
// file="MatrixParallel"
|
||||||
// Project: StgSharp
|
// Project: StgSharp
|
||||||
// AuthorGroup: Nitload Space
|
// AuthorGroup: Nitload
|
||||||
// Copyright (c) Nitload Space. All rights reserved.
|
// Copyright (c) Nitload. All rights reserved.
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -57,7 +57,9 @@ namespace StgSharp.Mathematics.Numeric
|
|||||||
|
|
||||||
private static bool IsInitialized { get; set; } = false;
|
private static bool IsInitialized { get; set; } = false;
|
||||||
|
|
||||||
internal static unsafe IntrinsicContext* GetContext(SIMDID simd)
|
internal static unsafe IntrinsicContext* GetContext(
|
||||||
|
SIMDID simd
|
||||||
|
)
|
||||||
{
|
{
|
||||||
if (!IsParallelSupported) {
|
if (!IsParallelSupported) {
|
||||||
return null;
|
return null;
|
||||||
@@ -92,7 +94,10 @@ namespace StgSharp.Mathematics.Numeric
|
|||||||
{
|
{
|
||||||
MatrixParallelThread thread = new MatrixParallelThread(i);
|
MatrixParallelThread thread = new MatrixParallelThread(i);
|
||||||
_threads.Push(thread);
|
_threads.Push(thread);
|
||||||
|
SpinWait.SpinUntil(() => thread.State == MatrixThreadState.Ready);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO 构建全局的保底上下文
|
||||||
if (contextMap.Keys.Count == 1)
|
if (contextMap.Keys.Count == 1)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 4; i++)
|
for (int i = 0; i < 4; i++)
|
||||||
@@ -110,7 +115,17 @@ namespace StgSharp.Mathematics.Numeric
|
|||||||
|
|
||||||
[LibraryImport(Native.LibName, EntryPoint = "load_intrinsic_function")]
|
[LibraryImport(Native.LibName, EntryPoint = "load_intrinsic_function")]
|
||||||
[UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
|
[UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
|
||||||
private static unsafe partial void LoadIntrinsicFunction(IntrinsicContext* context, ulong id);
|
private static unsafe partial ulong CompareSIMDID(
|
||||||
|
IntrinsicContext* context,
|
||||||
|
ulong id
|
||||||
|
);
|
||||||
|
|
||||||
|
[LibraryImport(Native.LibName, EntryPoint = "load_intrinsic_function")]
|
||||||
|
[UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
|
||||||
|
private static unsafe partial void LoadIntrinsicFunction(
|
||||||
|
IntrinsicContext* context,
|
||||||
|
ulong id
|
||||||
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
// file="MatrixParallelThread"
|
// file="MatrixParallelThread"
|
||||||
// Project: StgSharp
|
// Project: StgSharp
|
||||||
// AuthorGroup: Nitload Space
|
// AuthorGroup: Nitload
|
||||||
// Copyright (c) Nitload Space. All rights reserved.
|
// Copyright (c) Nitload. All rights reserved.
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -35,9 +35,11 @@ namespace StgSharp.Mathematics.Numeric
|
|||||||
internal enum MatrixThreadState : int
|
internal enum MatrixThreadState : int
|
||||||
{
|
{
|
||||||
|
|
||||||
Ready = 0,
|
Born = 0,
|
||||||
Running = 1,
|
Ready = 1,
|
||||||
Idle = 2,
|
Running = 2,
|
||||||
|
Idle = 4,
|
||||||
|
Retired = 8,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,11 +47,13 @@ namespace StgSharp.Mathematics.Numeric
|
|||||||
{
|
{
|
||||||
|
|
||||||
private IntPtr* _context;
|
private IntPtr* _context;
|
||||||
private bool disposedValue;
|
private volatile bool _disposed, _isRetireRequested = false;
|
||||||
private readonly ManualResetEventSlim _resetEvent;
|
private readonly ManualResetEventSlim _resetEvent;
|
||||||
private readonly Thread _managedThread;
|
private readonly Thread _managedThread;
|
||||||
|
|
||||||
internal MatrixParallelThread(int poolId)
|
internal MatrixParallelThread(
|
||||||
|
int poolId
|
||||||
|
)
|
||||||
{
|
{
|
||||||
_resetEvent = new ManualResetEventSlim(false);
|
_resetEvent = new ManualResetEventSlim(false);
|
||||||
_managedThread = new Thread(MatrixParallelWorkLoad);
|
_managedThread = new Thread(MatrixParallelWorkLoad);
|
||||||
@@ -69,7 +73,7 @@ namespace StgSharp.Mathematics.Numeric
|
|||||||
|
|
||||||
internal MatrixParallelWrap BindedWrap { get; set; }
|
internal MatrixParallelWrap BindedWrap { get; set; }
|
||||||
|
|
||||||
internal MatrixThreadState state { get; set; }
|
internal MatrixThreadState State { get; set; }
|
||||||
|
|
||||||
internal SIMDID Simd { get; set; }
|
internal SIMDID Simd { get; set; }
|
||||||
|
|
||||||
@@ -80,7 +84,9 @@ namespace StgSharp.Mathematics.Numeric
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static unsafe void ExecuteTaskPackage(MatrixParallelQueue taskQueue)
|
public static unsafe void ExecuteTaskPackage(
|
||||||
|
MatrixParallelQueue taskQueue
|
||||||
|
)
|
||||||
{
|
{
|
||||||
MatrixParallelTaskPackage* p = taskQueue.Package;
|
MatrixParallelTaskPackage* p = taskQueue.Package;
|
||||||
switch (p->ComputeMode.OperationStyle)
|
switch (p->ComputeMode.OperationStyle)
|
||||||
@@ -121,44 +127,54 @@ namespace StgSharp.Mathematics.Numeric
|
|||||||
_resetEvent.Set();
|
_resetEvent.Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
[DllImport(
|
[LibraryImport(Native.LibName, EntryPoint = "sn_get_simd_level_local")]
|
||||||
dllName: Native.LibName,
|
[UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
|
||||||
EntryPoint = "sn_get_simd_level_local",
|
internal static partial ulong GetSimdId();
|
||||||
CallingConvention = CallingConvention.Cdecl)]
|
|
||||||
internal static extern ulong GetSimdId();
|
|
||||||
|
|
||||||
[DllImport(
|
[LibraryImport(Native.LibName, EntryPoint = "sn_set_thread_affinity")]
|
||||||
dllName:Native.LibName,
|
[UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
|
||||||
EntryPoint = "sn_set_thread_affinity",
|
internal static partial void SetThreadAffinity(
|
||||||
CallingConvention = CallingConvention.Cdecl)]
|
int coreId
|
||||||
internal static extern void SetThreadAffinity(int coreId);
|
);
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(
|
||||||
|
bool disposing
|
||||||
|
)
|
||||||
{
|
{
|
||||||
if (!disposedValue)
|
if (!_disposed)
|
||||||
{
|
{
|
||||||
if (disposing)
|
if (disposing)
|
||||||
{
|
{
|
||||||
_resetEvent.Dispose();
|
_isRetireRequested = true;
|
||||||
|
_resetEvent.Set();
|
||||||
_managedThread.Join();
|
_managedThread.Join();
|
||||||
|
_resetEvent.Dispose();
|
||||||
}
|
}
|
||||||
disposedValue = true;
|
_disposed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private unsafe void MatrixParallelWorkLoad()
|
private unsafe void MatrixParallelWorkLoad()
|
||||||
{
|
{
|
||||||
|
State = MatrixThreadState.Born;
|
||||||
|
|
||||||
// Bind OS-thread affinity once when the worker starts running
|
// Bind OS-thread affinity once when the worker starts running
|
||||||
SetThreadAffinity(PoolId);
|
SetThreadAffinity(PoolId);
|
||||||
Thread.Sleep(1);
|
Thread.Yield();
|
||||||
SIMDID feature = new();
|
SIMDID feature = new();
|
||||||
feature.Mask = GetSimdId();
|
feature.Mask = GetSimdId();
|
||||||
_context = (nint*)MatrixParallel.GetContext(feature);
|
_context = (nint*)MatrixParallel.GetContext(feature);
|
||||||
|
State = MatrixThreadState.Ready;
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
// Thread self = Thread.CurrentThread;
|
// Thread self = Thread.CurrentThread;
|
||||||
_resetEvent.Wait();
|
_resetEvent.Wait();
|
||||||
_resetEvent.Reset();
|
_resetEvent.Reset();
|
||||||
|
if (_isRetireRequested)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
State = MatrixThreadState.Running;
|
||||||
AfterWork = BindedWrap.AfterWork;
|
AfterWork = BindedWrap.AfterWork;
|
||||||
_managedThread.Priority = ThreadPriority.Highest;
|
_managedThread.Priority = ThreadPriority.Highest;
|
||||||
if (IsSingle)
|
if (IsSingle)
|
||||||
@@ -170,7 +186,9 @@ namespace StgSharp.Mathematics.Numeric
|
|||||||
BindedWrap.ReportThreadAccomplished();
|
BindedWrap.ReportThreadAccomplished();
|
||||||
}
|
}
|
||||||
_managedThread.Priority = AfterWork;
|
_managedThread.Priority = AfterWork;
|
||||||
|
State = MatrixThreadState.Idle;
|
||||||
}
|
}
|
||||||
|
State = MatrixThreadState.Retired;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -134,14 +134,15 @@ AVX Feature (bits 47..40)
|
|||||||
|
|
||||||
| bit | 47 | 46 | 45 | 44 | 43 | 42 | 41 | 40 |
|
| bit | 47 | 46 | 45 | 44 | 43 | 42 | 41 | 40 |
|
||||||
|-----|----|----|----|----|----|----|----|----|
|
|-----|----|----|----|----|----|----|----|----|
|
||||||
| use | R | R | R | R | F16C | FMA | AVX2 | AVX |
|
| use | R | R | R | DUAL | F16C | FMA | AVX2 | AVX |
|
||||||
|
|
||||||
Rules:
|
Rules:
|
||||||
- **AVX (bit 40)**: AVX present.
|
- **AVX (bit 40)**: AVX present.
|
||||||
- **AVX2 (bit 41)**: AVX2 present. For the AVX feature byte to be considered valid, both AVX and AVX2 must be set.
|
- **AVX2 (bit 41)**: AVX2 present. For the AVX feature byte to be considered valid, both AVX and AVX2 must be set.
|
||||||
- **FMA (bit 42)**: FMA present.
|
- **FMA (bit 42)**: FMA present.
|
||||||
- **F16C (bit 43)**: F16C present.
|
- **F16C (bit 43)**: F16C present.
|
||||||
- **[44..47]**: Reserved for other AVX-family extensions (e.g., BMI/BMI2, IFMA, XOP) as needed.
|
- **DUAL (bit 44)**: YMM execution is **glued/split-lane** (two fused 128-bit units rather than a native 256-bit path). Detection: AMD Zen1/Zen+ (Family 0x17, model < 0x30), Hygon (all, Zen1-based), VIA/Zhaoxin (all). When set, code using YMM operations may incur split-register overhead; prefer 128-bit SSE paths on these targets.
|
||||||
|
- **[45..47]**: Reserved for other AVX-family extensions (e.g., IFMA, XOP) as needed.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -207,17 +208,36 @@ When `MainLevel < AMX`, this byte can be all zeros.
|
|||||||
|
|
||||||
## 11) uArchHi ([63..56], 8 bits)
|
## 11) uArchHi ([63..56], 8 bits)
|
||||||
|
|
||||||
Purpose: optional high-byte strategy field to address issues like early Intel/AMD "glue" AVX512 that are not expressible purely via CPUID subsets. Defaults to 0.
|
Purpose: **bitmask** byte encoding core topology classification and SIMD execution-width policy for the detected core. All bits default to 0.
|
||||||
|
|
||||||
Suggested minimal use:
|
uArchHi bitmask layout:
|
||||||
- `0`: unused
|
|
||||||
- `1`: ForceDual (treat as AVX512-DUAL even if impl is unknown)
|
|
||||||
- `2`: ForceNative (treat as AVX512-NATIVE even if impl is unknown)
|
|
||||||
- `3..`: Implementation-maintained uArch enumeration (e.g., SKX/ICL/SPR/ZEN4/ZEN5…)
|
|
||||||
|
|
||||||
Practical strategy for a single maintainer:
|
| bit (relative to [63..56]) | Name | Note |
|
||||||
- AMD: detection side sets `AVX512_IMPL = DUAL` directly (no uArch needed)
|
|---:|------|------|
|
||||||
- Intel: enumerate only selected models of interest; otherwise Unknown follows default policy
|
| 0 | HYBRID_E | E-core (efficiency/small core) in a hybrid-topology processor (e.g., Intel Alder Lake+ E-core) |
|
||||||
|
| 1 | HYBRID_P | P-core (performance/big core) in a hybrid-topology processor (e.g., Intel Alder Lake+ P-core) |
|
||||||
|
| 2 | SIMD_DUAL | SIMD execution is glued/dual (not full-width). Summary flag: set when `AVX_DUAL=1` **or** `AVX512_IMPL=DUAL`. |
|
||||||
|
| 3..7 | Reserved | |
|
||||||
|
|
||||||
|
Core topology encoding (bits [1..0], numerically comparable — higher = stronger):
|
||||||
|
|
||||||
|
| bits [1..0] | Value | Meaning |
|
||||||
|
|:-----------:|:-----:|----------|
|
||||||
|
| 00 | 0 | Unknown — undetected hybrid type or pre-CPUID.1A hardware |
|
||||||
|
| 01 | 1 | **Hybrid-E** — efficiency/small core (a3) |
|
||||||
|
| 10 | 2 | **Hybrid-P** — performance/big core (a2) |
|
||||||
|
| 11 | 3 | **Unified** — confirmed non-hybrid; full-topology core (a1) |
|
||||||
|
|
||||||
|
The 2-bit field is directly comparable as an unsigned integer: `unified(3) > P-core(2) > E-core(1) > unknown(0)`.
|
||||||
|
|
||||||
|
Detection:
|
||||||
|
- **Non-hybrid (unified)**: if `CPUID.7.0 EDX[15]` (HYBRID) is **not** set, the CPU is confirmed non-hybrid → set both `HYBRID_P` and `HYBRID_E` (topology = 11).
|
||||||
|
- **Hybrid P-core**: HYBRID flag set and `CPUID.0x1A.0 EAX[31:24] == 0x40` → set only `HYBRID_P` (topology = 10).
|
||||||
|
- **Hybrid E-core**: HYBRID flag set and `CPUID.0x1A.0 EAX[31:24] == 0x20` → set only `HYBRID_E` (topology = 01).
|
||||||
|
- **Unknown hybrid** (flag set but core type unrecognised): leave topology at 00.
|
||||||
|
- **SIMD_DUAL**: set when any of the following: `AVX Feature DUAL=1` (glued YMM), or `AVX512_IMPL=DUAL`.
|
||||||
|
|
||||||
|
`sn_get_unite_simd` computes topology bits [1..0] with bitwise **AND** (conservative intersection — weaker topology wins) and SIMD_DUAL with bitwise **OR** (dual if either core is dual). `sn_compare_simd` compares topology value numerically as step 6 and SIMD_DUAL as the final (step 7) tie-break.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -232,7 +252,7 @@ Practical strategy for a single maintainer:
|
|||||||
- Intel: Decide DUAL/NATIVE based on family/model or policy table; if uncertain, use Unknown
|
- Intel: Decide DUAL/NATIVE based on family/model or policy table; if uncertain, use Unknown
|
||||||
- When VNNI/BF16/FP16/VBMI/VBMI2 are detected, set the corresponding bits.
|
- When VNNI/BF16/FP16/VBMI/VBMI2 are detected, set the corresponding bits.
|
||||||
4) If `MainLevel >= AMX`: fill the AMX byte.
|
4) If `MainLevel >= AMX`: fill the AMX byte.
|
||||||
5) When AVX/AVX2 are present, populate the AVX Feature byte: set AVX and AVX2; set FMA and F16C when detected; leave reserved bits for future extensions.
|
5) When AVX/AVX2 are present, populate the AVX Feature byte: set `AVX` and `AVX2`; set `FMA` and `F16C` when detected; set `DUAL` when the YMM execution path is split/glued (AMD Zen1/Zen+ Family 0x17 model < 0x30, Hygon all models, VIA/Zhaoxin all models).
|
||||||
6) **AVX10 detection** (requires `CPUID leaf 7 subleaf 1, EDX[19] == 1` as the AVX10 convergence bit):
|
6) **AVX10 detection** (requires `CPUID leaf 7 subleaf 1, EDX[19] == 1` as the AVX10 convergence bit):
|
||||||
- If AVX10 is indicated, query `CPUID leaf 0x24, subleaf 0`:
|
- If AVX10 is indicated, query `CPUID leaf 0x24, subleaf 0`:
|
||||||
- Read `EBX[7:0]` for version number; set `AVX10`, `V1`, and optionally `V2`.
|
- Read `EBX[7:0]` for version number; set `AVX10`, `V1`, and optionally `V2`.
|
||||||
@@ -240,10 +260,16 @@ Practical strategy for a single maintainer:
|
|||||||
- Read `EBX[18]` for 512-bit support → set `512W`.
|
- Read `EBX[18]` for 512-bit support → set `512W`.
|
||||||
- Promote `MainLevel` to `AVX10` (value 7).
|
- Promote `MainLevel` to `AVX10` (value 7).
|
||||||
- The `AVX512 Feature` field should still be filled for backward compatibility.
|
- The `AVX512 Feature` field should still be filled for backward compatibility.
|
||||||
7) Use uArchHi only when additional policy is needed; otherwise leave as 0.
|
7) Populate uArchHi:
|
||||||
|
- If `CPUID.7.0 EDX[15]` (HYBRID) **not** set: set `HYBRID_P | HYBRID_E` (unified = 11).
|
||||||
|
- If HYBRID is set, read `CPUID.0x1A.0 EAX[31:24]`:
|
||||||
|
- `0x40` (P-core) → set only `HYBRID_P` (topology = 10).
|
||||||
|
- `0x20` (E-core) → set only `HYBRID_E` (topology = 01).
|
||||||
|
- Otherwise leave topology at 00 (unknown hybrid).
|
||||||
|
- If `AVX Feature DUAL=1` or `AVX512_IMPL=DUAL`, set `SIMD_DUAL`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 13) Choice for High Bits Reservation
|
## 13) Choice for High Bits Reservation
|
||||||
|
|
||||||
`[31..24]` is reserved for AVX512 extensions/AI. `[47..44]` is reserved for AVX-family extensions beside the AVX byte. `[55..53]` is reserved for future AVX10 versions. uArchHi `[63..56]` remains available for minimal policy hints. If IFMA or other extensions are more important, repurpose the reserved bits while keeping `BASE`, `Impl`, and `AVX10` core bits unchanged.
|
`[31..24]` is reserved for AVX512 extensions/AI. `[47..45]` is reserved for AVX-family extensions (bit 44 is now `DUAL`). `[55..53]` is reserved for future AVX10 versions. uArchHi `[63..56]` bits [3..7] are reserved; bits [0..2] encode hybrid topology and SIMD-dual policy.
|
||||||
@@ -75,6 +75,7 @@
|
|||||||
#define SIMDID_AVX2 (1U << 1)
|
#define SIMDID_AVX2 (1U << 1)
|
||||||
#define SIMDID_AVX_FMA (1U << 2)
|
#define SIMDID_AVX_FMA (1U << 2)
|
||||||
#define SIMDID_AVX_F16C (1U << 3)
|
#define SIMDID_AVX_F16C (1U << 3)
|
||||||
|
#define SIMDID_AVX_DUAL (1U << 4) /* glued/split-lane YMM (two 128-bit units; Zen1, Hygon, VIA/Zhaoxin) */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef SIMDID_AMX_TILE
|
#ifndef SIMDID_AMX_TILE
|
||||||
@@ -91,6 +92,19 @@
|
|||||||
#define SIMDID_AVX10_512W (1U << 4)
|
#define SIMDID_AVX10_512W (1U << 4)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* uArchHi ([63..56]) bitmask — core topology and SIMD-width execution policy */
|
||||||
|
#ifndef SIMDID_UARCH_HYBRID_P
|
||||||
|
/* Topology 2-bit field (bits [1..0] of uArchHi byte):
|
||||||
|
* 00 = unknown (undetected hybrid type or pre-CPUID.1A hardware)
|
||||||
|
* 01 = E-core (efficiency/small core in a hybrid CPU)
|
||||||
|
* 10 = P-core (performance/big core in a hybrid CPU)
|
||||||
|
* 11 = unified (confirmed non-hybrid; numerically strongest)
|
||||||
|
* Higher numeric value == stronger / higher-capability topology. */
|
||||||
|
#define SIMDID_UARCH_HYBRID_E (1U << 0) /* E-core (efficiency/small) in a hybrid topology */
|
||||||
|
#define SIMDID_UARCH_HYBRID_P (1U << 1) /* P-core (performance/big) in a hybrid topology */
|
||||||
|
#define SIMDID_UARCH_SIMD_DUAL (1U << 2) /* SIMD is glued/dual (summary: AVX_DUAL or AVX512_IMPL=DUAL) */
|
||||||
|
#endif
|
||||||
|
|
||||||
// Pack helpers (define locally to avoid missing macros when included standalone)
|
// Pack helpers (define locally to avoid missing macros when included standalone)
|
||||||
#ifndef SIMDID_PACK_ROOT
|
#ifndef SIMDID_PACK_ROOT
|
||||||
#define SIMDID_PACK_ROOT(root) (((uint64_t)(root) & 0xFULL) << SIMDID_SHIFT_ROOT)
|
#define SIMDID_PACK_ROOT(root) (((uint64_t)(root) & 0xFULL) << SIMDID_SHIFT_ROOT)
|
||||||
|
|||||||
@@ -77,6 +77,25 @@ static int is_intel_native_avx512(int family, int model)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Returns 1 if this AMD/Hygon CPU uses split-lane YMM (two fused 128-bit execution units).
|
||||||
|
* - AMD Zen1 (family 0x17, model < 0x30) and Zen+ (0x18-0x2F) use a split/glued path.
|
||||||
|
* - Hygon Dhyana is architecturally Zen1-based; always split.
|
||||||
|
* - AMD Zen2+ (family 0x17 model >= 0x30, family 0x19+) have native 256-bit execution. */
|
||||||
|
static int is_amd_glue_ymm(const char *vendor, int cpuid1_eax)
|
||||||
|
{
|
||||||
|
if (strcmp(vendor, "HygonGenuine") == 0)
|
||||||
|
return 1; /* Hygon is Zen1-based; always split YMM */
|
||||||
|
if (strcmp(vendor, "AuthenticAMD") != 0)
|
||||||
|
return 0;
|
||||||
|
int base_fam = (cpuid1_eax >> 8) & 0xF;
|
||||||
|
int ext_fam = (cpuid1_eax >> 20) & 0xFF;
|
||||||
|
int disp_fam = (base_fam < 0xF) ? base_fam : (base_fam + ext_fam);
|
||||||
|
int base_mdl = (cpuid1_eax >> 4) & 0xF;
|
||||||
|
int ext_mdl = (cpuid1_eax >> 16) & 0xF;
|
||||||
|
int disp_mdl = (ext_mdl << 4) | base_mdl;
|
||||||
|
return (disp_fam == 0x17) && (disp_mdl < 0x30); /* Zen1/Zen+ */
|
||||||
|
}
|
||||||
|
|
||||||
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 };
|
||||||
@@ -93,9 +112,12 @@ SN_API SIMDID SN_DECL sn_get_simd_level_local()
|
|||||||
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);
|
||||||
|
const int is_via = (strcmp(vendor, "CentaurHauls") == 0) ||
|
||||||
|
(memcmp(vendor, " Shanghai ", 12) == 0); /* VIA / Zhaoxin */
|
||||||
|
|
||||||
/* CPUID.1 */
|
/* CPUID.1 */
|
||||||
get_cpuid(1, 0, &eax, &ebx, &ecx, &edx);
|
get_cpuid(1, 0, &eax, &ebx, &ecx, &edx);
|
||||||
|
int cpuid1_eax = eax; /* saved for Zen1/Zen+ glue-YMM detection */
|
||||||
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);
|
||||||
|
|
||||||
@@ -108,12 +130,14 @@ SN_API SIMDID SN_DECL sn_get_simd_level_local()
|
|||||||
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 :
|
||||||
|
is_via ? SIMDID_MANU_VIA :
|
||||||
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;
|
||||||
|
int has_hybrid = 0; /* filled from CPUID.7.0 EDX[15] when AVX present */
|
||||||
|
|
||||||
/* Baseline main level */
|
/* Baseline main level */
|
||||||
if (has_sse42)
|
if (has_sse42)
|
||||||
@@ -122,6 +146,10 @@ SN_API SIMDID SN_DECL sn_get_simd_level_local()
|
|||||||
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;
|
||||||
|
|
||||||
|
/* Glued/split-lane YMM: early AMD Zen1/Zen+, Hygon, and VIA/Zhaoxin */
|
||||||
|
if (is_amd_glue_ymm(vendor, cpuid1_eax) || is_via)
|
||||||
|
avx_bits |= SIMDID_AVX_DUAL;
|
||||||
|
|
||||||
/* 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;
|
||||||
@@ -140,6 +168,7 @@ SN_API SIMDID SN_DECL sn_get_simd_level_local()
|
|||||||
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;
|
||||||
|
has_hybrid = (edx & (1 << 15)) != 0; /* Intel hybrid core topology */
|
||||||
|
|
||||||
/* 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);
|
||||||
@@ -217,9 +246,28 @@ SN_API SIMDID SN_DECL sn_get_simd_level_local()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* uArchHi: hybrid core topology and SIMD-dual summary */
|
||||||
|
uint64_t uarch_bits = 0;
|
||||||
|
if (has_hybrid) {
|
||||||
|
int h_eax = 0, h_ebx = 0, h_ecx = 0, h_edx = 0;
|
||||||
|
get_cpuid(0x1A, 0, &h_eax, &h_ebx, &h_ecx, &h_edx);
|
||||||
|
unsigned core_type = (unsigned)(h_eax >> 24) & 0xFFU;
|
||||||
|
if (core_type == 0x40) /* Intel Core / P-core → 10 */
|
||||||
|
uarch_bits |= SIMDID_UARCH_HYBRID_P;
|
||||||
|
else if (core_type == 0x20) /* Intel Atom / E-core → 01 */
|
||||||
|
uarch_bits |= SIMDID_UARCH_HYBRID_E;
|
||||||
|
/* else unknown hybrid core type → topology stays 00 */
|
||||||
|
} else {
|
||||||
|
/* Confirmed non-hybrid (unified) → set both bits = 11 (numerically strongest) */
|
||||||
|
uarch_bits |= SIMDID_UARCH_HYBRID_P | SIMDID_UARCH_HYBRID_E;
|
||||||
|
}
|
||||||
|
if ((avx_bits & SIMDID_AVX_DUAL) ||
|
||||||
|
((avx512_bits & SIMDID_AVX512_IMPL_MASK) == SIMDID_AVX512_IMPL_DUAL))
|
||||||
|
uarch_bits |= SIMDID_UARCH_SIMD_DUAL;
|
||||||
|
|
||||||
_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) | SIMDID_PACK_UARCH(uarch_bits);
|
||||||
return _simdid;
|
return _simdid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ static int impl_strength(uint16_t avx512)
|
|||||||
* - AVX features : bitwise AND
|
* - AVX features : bitwise AND
|
||||||
* - AVX10 features : bitwise AND
|
* - AVX10 features : bitwise AND
|
||||||
* - Root / Manu : keep left (must be same arch in practice)
|
* - Root / Manu : keep left (must be same arch in practice)
|
||||||
* - uArchHi : min (most conservative policy)
|
* - uArchHi : bitwise OR (HYBRID_P/E union; SIMD_DUAL set if either core is dual)
|
||||||
* ----------------------------------------------------------------------- */
|
* ----------------------------------------------------------------------- */
|
||||||
SN_API SIMDID SN_DECL sn_get_unite_simd(SIMDID left, SIMDID right)
|
SN_API SIMDID SN_DECL sn_get_unite_simd(SIMDID left, SIMDID right)
|
||||||
{
|
{
|
||||||
@@ -102,10 +102,14 @@ SN_API SIMDID SN_DECL sn_get_unite_simd(SIMDID left, SIMDID right)
|
|||||||
/* AVX10 (byte 6): AND */
|
/* AVX10 (byte 6): AND */
|
||||||
out.make_byte[6] = left.make_byte[6] & right.make_byte[6];
|
out.make_byte[6] = left.make_byte[6] & right.make_byte[6];
|
||||||
|
|
||||||
/* uArchHi (byte 7): min */
|
/* uArchHi (byte 7):
|
||||||
|
* topology bits [1..0]: AND — conservative intersection (weaker topology wins)
|
||||||
|
* SIMD_DUAL bit [2]: OR — dual if either core is dual */
|
||||||
unsigned char lu = FIELD_UARCH(left);
|
unsigned char lu = FIELD_UARCH(left);
|
||||||
unsigned char ru = FIELD_UARCH(right);
|
unsigned char ru = FIELD_UARCH(right);
|
||||||
out.make_byte[7] = (char)(lu < ru ? lu : ru);
|
unsigned char topo = (lu & 0x3u) & (ru & 0x3u);
|
||||||
|
unsigned char dual = (lu | ru) & (unsigned char)SIMDID_UARCH_SIMD_DUAL;
|
||||||
|
out.make_byte[7] = (char)(topo | dual);
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
@@ -142,8 +146,12 @@ SN_API SIMDID SN_DECL sn_get_unite_simd(SIMDID left, SIMDID right)
|
|||||||
* DUAL(weakest) < UNKNOWN < NATIVE(strongest).
|
* DUAL(weakest) < UNKNOWN < NATIVE(strongest).
|
||||||
* This is not ISA capability but execution-width risk.
|
* This is not ISA capability but execution-width risk.
|
||||||
*
|
*
|
||||||
* 6. uArchHi (final tie-break)
|
* 6. uArchHi topology bits [1..0]
|
||||||
* Higher value = more specific / stronger policy hint.
|
* Numeric value: unified(11=3) > P-core(10=2) > E-core(01=1) > unknown(00=0).
|
||||||
|
* Higher value = stronger / higher-capability core form.
|
||||||
|
*
|
||||||
|
* 7. uArchHi SIMD_DUAL (final tie-break)
|
||||||
|
* Non-dual (SIMD_DUAL=0) > dual (SIMD_DUAL=1).
|
||||||
* ----------------------------------------------------------------------- */
|
* ----------------------------------------------------------------------- */
|
||||||
SN_API int SN_DECL sn_compare_simd(SIMDID left, SIMDID right)
|
SN_API int SN_DECL sn_compare_simd(SIMDID left, SIMDID right)
|
||||||
{
|
{
|
||||||
@@ -186,11 +194,19 @@ SN_API int SN_DECL sn_compare_simd(SIMDID left, SIMDID right)
|
|||||||
return ls > rs ? 1 : -1;
|
return ls > rs ? 1 : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 6. uArchHi (final tie-break) */
|
/* 6. uArchHi topology bits [1..0]: unified(3) > P(2) > E(1) > unknown(0) */
|
||||||
unsigned lu = FIELD_UARCH(left);
|
unsigned lu = FIELD_UARCH(left);
|
||||||
unsigned ru = FIELD_UARCH(right);
|
unsigned ru = FIELD_UARCH(right);
|
||||||
if (lu != ru)
|
unsigned lt = lu & 0x3u;
|
||||||
return lu > ru ? 1 : -1;
|
unsigned rt = ru & 0x3u;
|
||||||
|
if (lt != rt)
|
||||||
|
return lt > rt ? 1 : -1;
|
||||||
|
|
||||||
|
/* 7. uArchHi SIMD_DUAL: non-dual (SIMD_DUAL=0) > dual (SIMD_DUAL=1) */
|
||||||
|
int ld = (lu & SIMDID_UARCH_SIMD_DUAL) != 0;
|
||||||
|
int rd = (ru & SIMDID_UARCH_SIMD_DUAL) != 0;
|
||||||
|
if (ld != rd)
|
||||||
|
return ld ? -1 : 1; /* non-dual is stronger */
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user