L4 completely add support for multi predictor, working on ver 5 mat para

This commit is contained in:
Youmu Liu
2026-04-10 18:05:40 +08:00
parent b5bee5c442
commit 8a66c1697a
16 changed files with 602 additions and 191 deletions
@@ -1,6 +1,6 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="ConcurrentList" // file="ConcurrentFlexibaleArray"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload // AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved. // Copyright (c) Nitload. All rights reserved.
@@ -96,5 +96,23 @@ namespace StgSharp.Common.Collections
} }
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal Span<T> AsSpan()
{
return _array.AsSpan();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void ReleaseSpanOperation()
{
_rwLock.ExitReadLock();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void RequestSpanOperation()
{
_rwLock.EnterReadLock();
}
} }
} }
@@ -45,10 +45,10 @@ namespace StgSharp.HighPerformance.Memory
{ {
internal int _id; internal int _id;
internal int _predictor;
internal int _refCount; internal int _refCount;
internal nuint _address; internal nuint _address;
internal nuint _origin; internal nuint _origin;
internal uint _predictor;
internal uint _profile; internal uint _profile;
} }
@@ -62,6 +62,10 @@ namespace StgSharp.HighPerformance.Memory
int remain = count; int remain = count;
Vector128<byte> three = Vector128.Create((byte)0x03); Vector128<byte> three = Vector128.Create((byte)0x03);
byte* p = (byte*)_eviction; byte* p = (byte*)_eviction;
_predictors.RequestSpanOperation();
Span<IL4Predict> pSpan = _predictors.AsSpan();
try
{
for (int i = 0; (i < CacheLineCount) && (remain > 0); i += 16) for (int i = 0; (i < CacheLineCount) && (remain > 0); i += 16)
{ {
int evictMask = _intrinsic.ScanEvict(p + i, three); int evictMask = _intrinsic.ScanEvict(p + i, three);
@@ -75,7 +79,7 @@ namespace StgSharp.HighPerformance.Memory
// EvictLine(i + index); // EvictLine(i + index);
_ = _map.TryRemove(head->_origin, out _); _ = _map.TryRemove(head->_origin, out _);
int id = (int)head->_predictor; int id = (int)head->_predictor;
IL4Predict predict = _predictions[id] ?? IL4Predict predict = pSpan[id] ??
throw new L4PredictUnregisteredException(id); throw new L4PredictUnregisteredException(id);
predict.WriteBack(head->_origin, head->_profile, new ReadOnlySpan<byte>((byte*)head->_address, Unsafe.SizeOf<CacheLineData>())); predict.WriteBack(head->_origin, head->_profile, new ReadOnlySpan<byte>((byte*)head->_address, Unsafe.SizeOf<CacheLineData>()));
RecycleLine(index); RecycleLine(index);
@@ -86,6 +90,11 @@ namespace StgSharp.HighPerformance.Memory
} }
return remain; return remain;
} }
finally
{
_predictors.ReleaseSpanOperation();
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void RecycleLine( private void RecycleLine(
@@ -28,6 +28,7 @@
using StgSharp.Common.Collections; using StgSharp.Common.Collections;
using StgSharp.Mathematics.Memory; using StgSharp.Mathematics.Memory;
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
@@ -40,14 +41,16 @@ namespace StgSharp.HighPerformance.Memory
private ConcurrentBufferStack<int> _idReuse = new(32); private ConcurrentBufferStack<int> _idReuse = new(32);
private ConcurrentFlexibleArray<IL4Predict> _predictions = new(32); private ConcurrentFlexibleArray<IL4Predict> _predictors = new(32);
private int _largestId = 0; private ConcurrentQueue<int> _predictionQueue = new();
private Queue<int> _predictionQueue = []; private int _largestId ;
private IL4Predict CurrentPredict { get; set; } private IL4Predict CurrentPredict { get; set; }
private int CurrentPredictIndex { get; set; }
public int RegisterPredict( public int RegisterPredict(
IL4Predict predict IL4Predict predict
) )
@@ -58,7 +61,7 @@ namespace StgSharp.HighPerformance.Memory
if (!_idReuse.TryPop(out int id)) { if (!_idReuse.TryPop(out int id)) {
id = _largestId++; id = _largestId++;
} }
_predictions[id] = predict; _predictors[id] = predict;
_predictionQueue.Enqueue(id); _predictionQueue.Enqueue(id);
return id; return id;
} }
@@ -70,29 +73,30 @@ namespace StgSharp.HighPerformance.Memory
if (id < 0) { if (id < 0) {
return null; return null;
} }
IL4Predict p = _predictions[id]; IL4Predict p = _predictors[id];
if (p == null) { if (p == null) {
return p; return p;
} }
_predictions[id] = null!; _predictors[id] = null!;
// TODO: scan and write back
_idReuse.Push(id); _idReuse.Push(id);
return p; return p;
} }
private void UseNewPredict() private bool TryGetNextPredict(
)
{ {
if (_predictionQueue.Count == 0) { if (_predictionQueue.IsEmpty) {
return; return false;
} }
if (_predictionQueue.TryDequeue(out int id)) if (_predictionQueue.TryDequeue(out int id))
{ {
IL4Predict predict = _predictions[id] ?? IL4Predict predict = _predictors[id] ??
throw new L4PredictUnregisteredException(id); throw new L4PredictUnregisteredException(id);
CurrentPredict = predict; CurrentPredict = predict;
CurrentPredictIndex = id;
return true;
} }
return false;
} }
} }
@@ -45,6 +45,11 @@ namespace StgSharp.HighPerformance.Memory
public interface IL4Predict public interface IL4Predict
{ {
bool IsSamePolicy(
uint policy,
IL4Predict predict
);
PredictResult Predict( PredictResult Predict(
Span<CacheLinePrediction> node, Span<CacheLinePrediction> node,
out int count out int count
@@ -65,7 +70,7 @@ namespace StgSharp.HighPerformance.Memory
/// </param> /// </param>
void Prefetch( void Prefetch(
nuint origin, nuint origin,
ulong policy, uint policy,
Span<byte> cache Span<byte> cache
); );
@@ -42,9 +42,13 @@ namespace StgSharp.HighPerformance.Memory
if (_activeCount > MaxActivateCacheLineCount) { if (_activeCount > MaxActivateCacheLineCount) {
throw new OverflowException("Too many activated cache line in external reference."); throw new OverflowException("Too many activated cache line in external reference.");
} }
IL4Predict predict = CurrentPredict;
Span<CacheLinePrediction> span = new(_prediction, PredictionCount); Span<CacheLinePrediction> span = new(_prediction, PredictionCount);
PredictResult result = PrefetchPredict.Predict(span, out int actualCount); PredictResult result = predict.Predict(span, out int actualCount);
if ((result == PredictResult.Exhausted) && (actualCount <= 0)) if ((result & PredictResult.Exhausted) != 0) {
_exhausted = !TryGetNextPredict();
}
if (actualCount <= 0)
{ {
_exhausted = true; _exhausted = true;
return; return;
@@ -62,18 +66,43 @@ namespace StgSharp.HighPerformance.Memory
missIndex++; missIndex++;
} }
} }
_predictors.RequestSpanOperation();
Span<IL4Predict> predictorSpan = _predictors.AsSpan();
try
{
// now prefetch the misses // now prefetch the misses
for (int i = 0; i < missIndex; i++) for (int i = 0; i < missIndex; i++)
{ {
ref CacheLinePrediction handle = ref span[i]; CacheLineHead* head;
nuint address = handle.Address; ref CacheLinePrediction pHandle = ref span[i];
if (TestHit(address)) nuint address = pHandle.Address;
int index;
if (_map.TryGet(address, out nuint handle))
{ {
head = (CacheLineHead*)handle;
index = head->_id;
(*_eviction)[index] = 0; // reset eviction counter
#region update predictor belonging
int predictId = head->_predictor;
IL4Predict belong = predictorSpan[predictId];
if (predict.IsSamePolicy(head->_profile, belong))
{
head->_predictor = predictId; // wrong line
} else
{
belong.WriteBack(head->_origin, head->_profile, new ReadOnlySpan<byte>((byte*)head->_address, Unsafe.SizeOf<CacheLineData>()));
head->_profile = pHandle.MapPolicy;
predict.Prefetch(head->_origin, head->_profile, new Span<byte>((byte*)head->_address, Unsafe.SizeOf<CacheLineData>()));
}
#endregion
continue; continue;
} }
int retryCount = 0, index, remain = missIndex - i; int retryCount = 0, remain = missIndex - i;
const int maxRetry = 1024; const int maxRetry = 1024;
while (!TryGetEmptyLine(out index)) while (!TryGetEmptyLine(out index))
{ {
@@ -82,33 +111,27 @@ namespace StgSharp.HighPerformance.Memory
if (retryCount > maxRetry) { if (retryCount > maxRetry) {
throw new OverflowException($"Failed to evict cache line after {maxRetry} retries."); throw new OverflowException($"Failed to evict cache line after {maxRetry} retries.");
} }
if (retryCount > maxRetry / 2) {
Thread.Sleep(1);
} }
CacheLineHead* head = &_head[index]; }
head = &_head[index];
head->_id = index; head->_id = index;
head->_origin = address; head->_origin = address;
head->_profile = handle.MapPolicy; head->_profile = pHandle.MapPolicy;
head->_refCount = 0; head->_refCount = 0;
head->_address = (nuint)(_data + index); head->_address = (nuint)(_data + index);
// pre fetch them here // pre fetch them here
PrefetchPredict.Prefetch(head->_origin, head->_profile, new Span<byte>((byte*)head->_address, Unsafe.SizeOf<CacheLineData>())); predict.Prefetch(head->_origin, head->_profile, new Span<byte>((byte*)head->_address, Unsafe.SizeOf<CacheLineData>()));
_ = _map.TryAddOrSet(address, (nuint)head, out _); _ = _map.TryAddOrSet(address, (nuint)head, out _);
} }
_ = Interlocked.Add(ref _aheadCount, actualCount); _ = Interlocked.Add(ref _aheadCount, actualCount);
} }
finally
private partial bool TestHit(
nuint origin
)
{ {
if (_map.TryGet(origin, out nuint handle)) _predictors.ReleaseSpanOperation();
{
CacheLineHead* head = (CacheLineHead*)handle;
int index = head->_id;
(*_eviction)[index] = 0; // reset eviction counter
return true;
} }
return false;
} }
} }
+20 -7
View File
@@ -26,6 +26,7 @@
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using StgSharp.Collections; using StgSharp.Collections;
using StgSharp.Common.HighPerformance.ProcessorAbstraction;
using System.Data; using System.Data;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@@ -49,14 +50,20 @@ namespace StgSharp.HighPerformance.Memory
private int _activeCount; private int _activeCount;
private int _aheadCount; private int _aheadCount;
private readonly int _bindedNuma;
private int _maxId; private int _maxId;
private volatile int _prefetching; private volatile int _prefetching;
private readonly nuint _baseAddress; private readonly nuint _baseAddress;
private readonly SwissTable _map; private readonly SwissTable _map;
private readonly UnmanagedStack<int> _idMux; private readonly UnmanagedStack<int> _idMux;
public L4() public L4(
int bindedNuma
)
{ {
_bindedNuma = bindedNuma;
// init order is fallow by align requirement of each component. // init order is fallow by align requirement of each component.
nuint cacheSize = ((nuint)Unsafe.SizeOf<CacheLineData>()) * CacheLineCount; // SIMDID, no less than 16 byte bit nuint cacheSize = ((nuint)Unsafe.SizeOf<CacheLineData>()) * CacheLineCount; // SIMDID, no less than 16 byte bit
nuint entrySize = ((nuint)Unsafe.SizeOf<CacheLineHead>()) * CacheLineCount; // 16 byte align nuint entrySize = ((nuint)Unsafe.SizeOf<CacheLineHead>()) * CacheLineCount; // 16 byte align
@@ -67,7 +74,7 @@ namespace StgSharp.HighPerformance.Memory
// malloc all memory at once, and free at once in Dispose. // malloc all memory at once, and free at once in Dispose.
// This is to avoid fragmentation and improve performance. // This is to avoid fragmentation and improve performance.
_baseAddress = (nuint)NativeMemory.AlignedAlloc(cacheSize + entrySize + stackSize + mapSize + predictionSize + rrpvSize, 16); _baseAddress = (nuint)Numa.Alloc(bindedNuma, cacheSize + entrySize + stackSize + mapSize + predictionSize + rrpvSize);
nuint ptr = _baseAddress; nuint ptr = _baseAddress;
// init all components with the buffer, and move the buffer pointer forward. // init all components with the buffer, and move the buffer pointer forward.
@@ -86,6 +93,8 @@ namespace StgSharp.HighPerformance.Memory
ptr += stackSize; ptr += stackSize;
} }
public static nuint AllocSize { get; }
// public IL4Predict PrefetchPredict { get; set; } // public IL4Predict PrefetchPredict { get; set; }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -114,7 +123,15 @@ namespace StgSharp.HighPerformance.Memory
_disposed = true; _disposed = true;
_map.Dispose(); _map.Dispose();
_idMux.Dispose(); _idMux.Dispose();
NativeMemory.AlignedFree((void*)_baseAddress);
nuint cacheSize = ((nuint)Unsafe.SizeOf<CacheLineData>()) * CacheLineCount; // SIMDID, no less than 16 byte bit
nuint entrySize = ((nuint)Unsafe.SizeOf<CacheLineHead>()) * CacheLineCount; // 16 byte align
nuint predictionSize = ((nuint)Unsafe.SizeOf<CacheLinePrediction>()) * PredictionCount; //16 byte align
nuint mapSize = (nuint)SwissTable.RequestedNativeMemorySize; // 16 byte align
nuint rrpvSize = (nuint)Unsafe.SizeOf<EvictionArray>(); // 16 byte align
nuint stackSize = ((nuint)sizeof(int)) * CacheLineCount; // int align
Numa.Free((void*)_baseAddress, _bindedNuma, cacheSize + entrySize + stackSize + mapSize + predictionSize + rrpvSize);
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }
@@ -164,10 +181,6 @@ namespace StgSharp.HighPerformance.Memory
private partial void Prefetch(); private partial void Prefetch();
private partial bool TestHit(
nuint origin
);
private struct CacheLineData private struct CacheLineData
{ {
@@ -25,8 +25,10 @@
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using StgSharp.HighPerformance.Memory;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
@@ -43,6 +45,35 @@ namespace StgSharp.Common.HighPerformance.ProcessorAbstraction
public static ReadOnlySpan<NumaNode> AllNumaNode => _numaNodes; public static ReadOnlySpan<NumaNode> AllNumaNode => _numaNodes;
public static unsafe void* Alloc(
int numaId,
nuint size
)
{
if (_numaNodes.Length == 0) {
throw new InvalidOperationException("Numa info has not been loaded.");
}
if ((numaId < 0) || (numaId >= _numaNodes.Length)) {
throw new ArgumentOutOfRangeException(nameof(numaId), "Invalid NUMA node ID.");
}
return NumaAllocNative(size, numaId);
}
public static unsafe void Free(
void* address,
int numaId,
nuint size
)
{
if (_numaNodes.Length == 0) {
throw new InvalidOperationException("Numa info has not been loaded.");
}
if ((numaId < 0) || (numaId >= _numaNodes.Length)) {
throw new ArgumentOutOfRangeException(nameof(numaId), "Invalid NUMA node ID.");
}
NumaFreeNative(address, size, numaId);
}
[LibraryImport(Native.LibName, EntryPoint = "sn_get_numa_each")] [LibraryImport(Native.LibName, EntryPoint = "sn_get_numa_each")]
[UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
[DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory)] [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory)]
@@ -60,6 +91,20 @@ namespace StgSharp.Common.HighPerformance.ProcessorAbstraction
Span<int> coreCountInNuma Span<int> coreCountInNuma
); );
public static L4[] RequestBufferForEachNuma()
{
if (_numaNodes.Length == 0) {
throw new InvalidOperationException("Numa info has not been loaded.");
}
L4[] buffers = new L4[NumaNodeCount];
for (int i = 0; i < NumaNodeCount; i++)
{
L4 buffer = new L4(i);
buffers[i] = buffer;
}
return buffers;
}
internal static void Initialize() internal static void Initialize()
{ {
Span<int> coreCountInNuma = stackalloc int[(Environment.ProcessorCount / 32) + 1]; Span<int> coreCountInNuma = stackalloc int[(Environment.ProcessorCount / 32) + 1];
@@ -73,6 +118,13 @@ namespace StgSharp.Common.HighPerformance.ProcessorAbstraction
} }
} }
[LibraryImport(Native.LibName, EntryPoint = "sn_set_thread_affinity")]
[UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
[DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory)]
internal static partial void SetThreadAffinity(
int coreId
);
[MethodImpl(MethodImplOptions.NoInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
private static NumaNode InitNumaNode( private static NumaNode InitNumaNode(
uint numaId, uint numaId,
@@ -84,5 +136,22 @@ namespace StgSharp.Common.HighPerformance.ProcessorAbstraction
return new(numaId, count, globalIndex); return new(numaId, count, globalIndex);
} }
[LibraryImport(Native.LibName, EntryPoint = "sn_numa_alloc")]
[UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
[DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory)]
private static unsafe partial void* NumaAllocNative(
nuint size,
int numaId
);
[LibraryImport(Native.LibName, EntryPoint = "sn_numa_free")]
[UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
[DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory)]
private static unsafe partial void NumaFreeNative(
void* ptr,
nuint size,
int numa_id
);
} }
} }
@@ -0,0 +1,48 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="MatrixParallel.Numa"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StgSharp.Mathematics.Numeric
{
public static unsafe partial class MatrixParallel
{
private static int[] NumaPayload;
private static MatrixParallelThread[,] NumaThreads;
private static void UpdateThreadPayload()
{
for (int i = 0; i < NumaPayload.Length; i++) { }
}
}
}
@@ -69,12 +69,8 @@ namespace StgSharp.Mathematics.Numeric
public ThreadPriority AfterWork { get; set; } public ThreadPriority AfterWork { get; set; }
internal ref readonly IntrinsicContext Intrinsic => ref (*_context);
internal bool IsSingle { get; set; } internal bool IsSingle { get; set; }
internal MatrixParallelWrap BindedWrap { get; set; }
internal MatrixThreadState State { get; private set; } internal MatrixThreadState State { get; private set; }
internal SIMDID Simd { get; private set; } internal SIMDID Simd { get; private set; }
@@ -163,7 +159,6 @@ namespace StgSharp.Mathematics.Numeric
} }
State = MatrixThreadState.Idle; State = MatrixThreadState.Idle;
State = MatrixThreadState.Running; State = MatrixThreadState.Running;
AfterWork = BindedWrap.AfterWork;
_managedThread.Priority = ThreadPriority.Highest; _managedThread.Priority = ThreadPriority.Highest;
if (IsSingle) if (IsSingle)
{ {
@@ -173,17 +168,6 @@ namespace StgSharp.Mathematics.Numeric
IsSingle = false; IsSingle = false;
} else } else
{ {
PredictPolicy policy = BindedWrap.Policy;
switch (policy)
{
case PredictPolicy.Transparent:
break;
case PredictPolicy.BufferSequential:
break;
default:
break;
}
// MatrixParallelQueue taskQueue = BindedWrap.TaskQueue; // MatrixParallelQueue taskQueue = BindedWrap.TaskQueue;
/* /*
if (0 > (int)p ->ComputeHandle) { if (0 > (int)p ->ComputeHandle) {
@@ -198,7 +182,6 @@ namespace StgSharp.Mathematics.Numeric
break; break;
} }
/**/ /**/
BindedWrap.ReportThreadAccomplished();
} }
_managedThread.Priority = AfterWork; _managedThread.Priority = AfterWork;
State = MatrixThreadState.Idle; State = MatrixThreadState.Idle;
@@ -208,19 +191,19 @@ namespace StgSharp.Mathematics.Numeric
#pragma warning disable CA5393 #pragma warning disable CA5393
[LibraryImport(Native.LibName, EntryPoint = "sn_get_simd_level_local")] [LibraryImport(Native.LibName, EntryPoint = "sn_get_simd_level_local")]
[UnmanagedCallConv(CallConvs = [ typeof(CallConvCdecl) ])] [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
[DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory)] [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory)]
internal static partial ulong GetSimdId(); internal static partial ulong GetSimdId();
[LibraryImport(Native.LibName, EntryPoint = "sn_set_thread_affinity")] [LibraryImport(Native.LibName, EntryPoint = "sn_set_thread_affinity")]
[UnmanagedCallConv(CallConvs = [ typeof(CallConvCdecl) ])] [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
[DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory)] [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory)]
internal static partial void SetThreadAffinity( internal static partial void SetThreadAffinity(
int coreId int coreId
); );
[LibraryImport(Native.LibName, EntryPoint = "load_intrinsic_function")] [LibraryImport(Native.LibName, EntryPoint = "load_intrinsic_function")]
[UnmanagedCallConv(CallConvs = [ typeof(CallConvCdecl) ])] [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
[DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory)] [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory)]
private static unsafe partial void LoadIntrinsicFunction( private static unsafe partial void LoadIntrinsicFunction(
IntrinsicContext* context, IntrinsicContext* context,
-73
View File
@@ -1,73 +0,0 @@
#ifdef _MSC_VER
#pragma once
#endif
#ifndef SSC
#define SSC
#include "ssc_internal.h"
#define STBIDEF SSCAPI
#include "stb_image.h"
#include "gl.h"
#include "wgl.h"
#include "glfw3.h"
#include <immintrin.h>
#include <assert.h>
#ifndef SSCAPI_DEFINE
#ifdef _WIN32
#define SSCAPI __declspec(dllexport)
#elif defined _LINUX_
#define SSCAPI __attribute__((dllexport))
#elif defined _APPLE_
#define SSCAPI __attribute__((visibility("default")))
#else
#define SSCAPI
#endif
#endif // ! SSCAPI_DEFINE
#if __cplusplus
extern "C" {
#endif
#define byte char
#define cs_char uint16_t
#define ssc_null_assert(ptr) assert(ptr != NULL)
#define ALIGN(simdVec) _mm_loadu_ps(&simdVec)
#define ALIGN256(simdVec) _mm256_loadu_ps(&simdVec)
#define ALIGN512(simdVec) _mm512_loadu_ps(&simdVec)
typedef struct Image {
int width;
int height;
int channel;
char *pixelPtr;
} Image;
typedef char *(*imageLoader)(char const *filename, int *x, int *y, int *channels_in_file,
int desired_channels);
SSCAPI char infolog[512];
SSCAPI int SSCDECL glCheckShaderStat(GladGLContext *context, uint64_t shaderHandle, int key,
char **logRef);
SSCAPI void SSCDECL initGL(int majorVersion, int minorVersion);
SSCAPI unsigned int SSCDECL linkShaderProgram(GladGLContext *context, GLuint shadertype);
SSCAPI void SSCDECL loadImageData(char *location, Image *out, imageLoader loader);
SSCAPI GLFWglproc SSCDECL loadGlfuncDefault(char *procName);
SSCAPI void SSCDECL unloadImageData(Image *out);
SSCAPI char *SSCDECL readLog(void);
#pragma endregion
SSCAPI int SSCDECL load_intrinsic_function(void *intrinsic_context);
#ifdef __cplusplus
}
#endif
#endif
+2 -1
View File
@@ -55,7 +55,8 @@ 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 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 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);
SN_API void* SN_DECL sn_numa_alloc(size_t size, int numa_id);
SN_API void SN_DECL sn_numa_free(void *ptr, size_t size, int numa_id);
SN_API int SN_DECL sn_get_numa_each(int numa_id, uint64_t *internal, uint32_t *global); SN_API int SN_DECL sn_get_numa_each(int numa_id, uint64_t *internal, uint32_t *global);
SN_API int SN_DECL sn_get_numa_overall(int *out_numa_count, int *count_of_each_numa); SN_API int SN_DECL sn_get_numa_overall(int *out_numa_count, int *count_of_each_numa);
#endif #endif
+296 -12
View File
@@ -1,25 +1,236 @@
#define _GNU_SOURCE
#include "sn_target.h" #include "sn_target.h"
#include "StgSharpNative.h"
#if SN_IS_OS(SN_OS_LINUX) #if SN_IS_OS(SN_OS_LINUX)
#define _GNU_SOURCE #include <errno.h>
#include <inttypes.h>
#include <limits.h>
#include <linux/mempolicy.h>
#include <pthread.h> #include <pthread.h>
#include <sched.h> #include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <unistd.h> #include <unistd.h>
typedef int (*sn_linux_id_visitor)(int value, void *context);
typedef struct sn_linux_counter_context {
int count;
} sn_linux_counter_context;
typedef struct sn_linux_max_context {
int max_value;
} sn_linux_max_context;
typedef struct sn_linux_fill_cpu_context {
uint64_t *internal;
uint32_t *global;
int index;
} sn_linux_fill_cpu_context;
typedef struct sn_linux_fill_node_count_context {
int *count_of_each_numa;
} sn_linux_fill_node_count_context;
static int sn_linux_read_first_line(char const *path, char *buffer, size_t buffer_size)
{
FILE *file = fopen(path, "r");
if (file == NULL)
return 0;
char *line = fgets(buffer, (int)buffer_size, file);
fclose(file);
return line != NULL;
}
static int sn_linux_parse_nonnegative_int(char const **cursor, int *value)
{
char *end = NULL;
uintmax_t parsed_value = 0;
if (**cursor == '-')
return 0;
errno = 0;
parsed_value = strtoumax(*cursor, &end, 10);
if (end == *cursor || errno == ERANGE || parsed_value > (uintmax_t)INT_MAX)
return 0;
*cursor = end;
*value = (int)parsed_value;
return 1;
}
static int sn_linux_visit_id_list(char const *text, sn_linux_id_visitor visitor, void *context)
{
char const *cursor = text;
while (*cursor != '\0') {
while (*cursor == ' ' || *cursor == '\t' || *cursor == '\n' || *cursor == ',')
cursor++;
if (*cursor == '\0')
break;
int begin = 0;
if (!sn_linux_parse_nonnegative_int(&cursor, &begin))
return 0;
int finish = begin;
if (*cursor == '-') {
cursor++;
if (!sn_linux_parse_nonnegative_int(&cursor, &finish))
return 0;
}
if (finish < begin)
return 0;
for (int value = begin;; value++) {
if (!visitor(value, context))
return 0;
if (value == finish)
break;
}
}
return 1;
}
static int sn_linux_count_id(int value, void *context)
{
(void)value;
((sn_linux_counter_context *)context)->count++;
return 1;
}
static int sn_linux_track_max_id(int value, void *context)
{
sn_linux_max_context *max_context = (sn_linux_max_context *)context;
if (value > max_context->max_value)
max_context->max_value = value;
return 1;
}
static int sn_linux_fill_cpu_id(int value, void *context)
{
sn_linux_fill_cpu_context *fill_context = (sn_linux_fill_cpu_context *)context;
if (fill_context->internal != NULL)
fill_context->internal[fill_context->index] = (uint64_t)fill_context->index;
if (fill_context->global != NULL)
fill_context->global[fill_context->index] = (uint32_t)value;
fill_context->index++;
return 1;
}
static int sn_linux_count_cpu_list(char const *path)
{
char buffer[256];
if (!sn_linux_read_first_line(path, buffer, sizeof(buffer)))
return 0;
sn_linux_counter_context counter = {0};
if (!sn_linux_visit_id_list(buffer, sn_linux_count_id, &counter))
return 0;
return counter.count;
}
static int sn_linux_fill_node_cpu_count(int value, void *context)
{
char path[128];
snprintf(path, sizeof(path), "/sys/devices/system/node/node%d/cpulist", value);
((sn_linux_fill_node_count_context *)context)->count_of_each_numa[value] =
sn_linux_count_cpu_list(path);
return 1;
}
static int sn_linux_node_exists(int numa_id)
{
char path[128];
snprintf(path, sizeof(path), "/sys/devices/system/node/node%d", numa_id);
return access(path, F_OK) == 0;
}
static size_t sn_linux_round_up_to_page_size(size_t size)
{
int64_t page_size_value = (int64_t)sysconf(_SC_PAGESIZE);
size_t effective_page_size = (page_size_value > 0) ? (size_t)page_size_value : 4096u;
size_t rounded = size + effective_page_size - 1;
if (rounded < size)
return 0;
return rounded - (rounded % effective_page_size);
}
static int sn_linux_bind_memory_to_node(void *address, size_t size, int numa_id,
uint32_t flags)
{
#if defined(SYS_mbind)
size_t bits_per_word = sizeof(unsigned long) * 8u;
size_t word_count = ((size_t)numa_id / bits_per_word) + 1u;
unsigned long *node_mask = (unsigned long *)calloc(word_count, sizeof(unsigned long));
if (node_mask == NULL)
return 0;
node_mask[(size_t)numa_id / bits_per_word] = 1ul << ((size_t)numa_id % bits_per_word);
int syscall_result = (int)syscall(SYS_mbind, address, size,
MPOL_BIND | MPOL_F_STATIC_NODES, node_mask,
(unsigned long)numa_id + 1ul, flags);
free(node_mask);
return syscall_result == 0;
#else
(void)address;
(void)size;
(void)numa_id;
(void)flags;
return 0;
#endif
}
static int sn_linux_populate_writable_mapping(void *address, size_t size)
{
#if defined(MADV_POPULATE_WRITE)
if (madvise(address, size, MADV_POPULATE_WRITE) == 0)
return 1;
return errno == EINVAL;
#else
(void)address;
(void)size;
return 1;
#endif
}
/* Pin the calling thread to a single logical processor. /* Pin the calling thread to a single logical processor.
* core_id >= 0 : bind to that logical processor (global index, across groups). * core_id >= 0 : bind to that logical processor (global index, across groups).
* core_id < 0 : clear affinity constraint within the current group. */ * 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)
{ {
long ncpus = sysconf(_SC_NPROCESSORS_ONLN); int cpu_count = (int)sysconf(_SC_NPROCESSORS_ONLN);
if (cpu_count <= 0)
return;
cpu_set_t *set = CPU_ALLOC(cpu_count);
size_t size = CPU_ALLOC_SIZE(cpu_count);
if (set == NULL)
return;
cpu_set_t *set = CPU_ALLOC(ncpus);
size_t size = CPU_ALLOC_SIZE(ncpus);
CPU_ZERO_S(size, set); CPU_ZERO_S(size, set);
if (core_id < 0) { if (core_id < 0) {
for (long i = 0; i < ncpus; i++) for (int i = 0; i < cpu_count; i++)
CPU_SET_S((int)i, size, set); CPU_SET_S((int)i, size, set);
} else { } else {
CPU_SET_S(core_id, size, set); CPU_SET_S(core_id, size, set);
@@ -31,16 +242,89 @@ SN_API void SN_DECL sn_set_thread_affinity(int core_id)
SN_API int SN_DECL sn_get_numa_overall(int *out_numa_count, int *count_of_each_numa) SN_API int SN_DECL sn_get_numa_overall(int *out_numa_count, int *count_of_each_numa)
{ {
(void)out_numa_count; char buffer[256];
(void)count_of_each_numa; if (!sn_linux_read_first_line("/sys/devices/system/node/online", buffer, sizeof(buffer)))
return 0; return 0;
sn_linux_max_context max_context = {-1};
if (!sn_linux_visit_id_list(buffer, sn_linux_track_max_id, &max_context) ||
max_context.max_value < 0)
return 0;
if (out_numa_count != NULL)
*out_numa_count = max_context.max_value + 1;
if (count_of_each_numa != NULL) {
for (int index = 0; index <= max_context.max_value; index++)
count_of_each_numa[index] = 0;
sn_linux_fill_node_count_context fill_context = {count_of_each_numa};
if (!sn_linux_visit_id_list(buffer, sn_linux_fill_node_cpu_count, &fill_context))
return 0;
}
return 1;
} }
SN_API int SN_DECL sn_get_numa_each(int numa_id, uint64_t *in_numa_cpuid, uint64_t *global_cpuid) SN_API int SN_DECL sn_get_numa_each(int numa_id, uint64_t *internal, uint32_t *global)
{
if (numa_id < 0)
return 0;
char path[128];
char buffer[256];
snprintf(path, sizeof(path), "/sys/devices/system/node/node%d/cpulist", numa_id);
if (!sn_linux_read_first_line(path, buffer, sizeof(buffer)))
return 0;
sn_linux_fill_cpu_context fill_context = {internal, global, 0};
return sn_linux_visit_id_list(buffer, sn_linux_fill_cpu_id, &fill_context);
}
SN_API void *SN_DECL sn_numa_alloc(size_t size, int numa_id)
{
size_t allocation_size = sn_linux_round_up_to_page_size(size);
if (allocation_size == 0)
return NULL;
int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS;
#if defined(MAP_POPULATE)
if (numa_id >= 0)
mmap_flags |= MAP_POPULATE;
#endif
void *memory = mmap(NULL, allocation_size, PROT_READ | PROT_WRITE,
mmap_flags, -1, 0);
if (memory == MAP_FAILED)
return NULL;
if (numa_id < 0)
return memory;
uint32_t bind_flags = 0u;
#if defined(MAP_POPULATE)
bind_flags = MPOL_MF_MOVE | MPOL_MF_STRICT;
#endif
if (!sn_linux_node_exists(numa_id) ||
!sn_linux_bind_memory_to_node(memory, allocation_size, numa_id, bind_flags) ||
!sn_linux_populate_writable_mapping(memory, allocation_size)) {
munmap(memory, allocation_size);
return NULL;
}
return memory;
}
SN_API void SN_DECL sn_numa_free(void *ptr, size_t size, int numa_id)
{ {
(void)numa_id; (void)numa_id;
(void)in_numa_cpuid;
(void)global_cpuid; if (ptr == NULL || size == 0)
return 0; return;
size_t allocation_size = sn_linux_round_up_to_page_size(size);
if (allocation_size != 0)
munmap(ptr, allocation_size);
} }
#endif #endif
+30 -3
View File
@@ -56,14 +56,41 @@ SN_API int SN_DECL sn_get_numa_each(int numa_id, uint64_t *internal, uint32_t *g
int intra = 0; int intra = 0;
while (mask) { while (mask) {
uint32_t idx; DWORD idx;
_BitScanForward64(&idx, mask); _BitScanForward64(&idx, mask);
int _ = (internal == NULL) ? intra : (internal[intra] = intra); if (internal != NULL)
global[intra] = (int)idx; internal[intra] = (uint64_t)intra;
global[intra] = (uint32_t)idx;
intra++; intra++;
mask &= mask - 1; mask &= mask - 1;
} }
return 1; return 1;
} }
SN_API void *SN_DECL sn_numa_alloc(size_t size, int numa_id)
{
if (size == 0)
return NULL;
if (numa_id < 0)
return VirtualAlloc(NULL, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
ULONG highest = 0;
if (!GetNumaHighestNodeNumber(&highest) || (ULONG)numa_id > highest)
return NULL;
return VirtualAllocExNuma(GetCurrentProcess(), NULL, size, MEM_RESERVE | MEM_COMMIT,
PAGE_READWRITE, (DWORD)numa_id);
}
SN_API void SN_DECL sn_numa_free(void *ptr, size_t size, int numa_id)
{
(void)size;
(void)numa_id;
if (ptr != NULL)
VirtualFree(ptr, 0, MEM_RELEASE);
}
#endif #endif