Skip to content

Commit d1c9ffe

Browse files
committed
TT size limit is now 1TB. Reduced binary size
1 parent 5d9ec93 commit d1c9ffe

File tree

9 files changed

+94
-54
lines changed

9 files changed

+94
-54
lines changed

.github/workflows/release-pipeline.yaml

+7-7
Original file line numberDiff line numberDiff line change
@@ -25,40 +25,40 @@ jobs:
2525
working-directory: src/Sapling
2626
id: get_version
2727
run: |
28-
VERSION=1.2.5
28+
VERSION=1.2.6
2929
echo "Application version: $VERSION"
3030
echo "::set-output name=version::$VERSION"
3131
3232
# Build and Package AVX512 for all platforms
3333
- name: Build and Package AVX512 for Windows
3434
run: |
3535
dotnet restore
36-
dotnet publish -c Release -r win-x64 --self-contained /p:PublishSingleFile=true -p:DefineConstants="AVX512" -o ../output/win-x64-avx512
36+
dotnet publish -c Release -r win-x64 --self-contained /p:Release=true /p:PublishSingleFile=true -p:DefineConstants="AVX512" -o ../output/win-x64-avx512
3737
3838
- name: Build and Package Non-AVX512 for Windows
3939
run: |
4040
dotnet restore
41-
dotnet publish -c Release -r win-x64 --self-contained /p:PublishSingleFile=true -o ../output/win-x64
41+
dotnet publish -c Release -r win-x64 --self-contained /p:Release=true /p:PublishSingleFile=true -o ../output/win-x64
4242
4343
- name: Build and Package AVX512 for Linux
4444
run: |
4545
dotnet restore
46-
dotnet publish -c Release -r linux-x64 --self-contained /p:PublishSingleFile=true -p:DefineConstants="AVX512" -o ../output/linux-x64-avx512
46+
dotnet publish -c Release -r linux-x64 --self-contained /p:Release=true /p:PublishSingleFile=true -p:DefineConstants="AVX512" -o ../output/linux-x64-avx512
4747
4848
- name: Build and Package Non-AVX512 for Linux
4949
run: |
5050
dotnet restore
51-
dotnet publish -c Release -r linux-x64 --self-contained /p:PublishSingleFile=true -o ../output/linux-x64
51+
dotnet publish -c Release -r linux-x64 --self-contained /p:Release=true /p:PublishSingleFile=true -o ../output/linux-x64
5252
5353
- name: Build and Package AVX512 for OSX
5454
run: |
5555
dotnet restore
56-
dotnet publish -c Release -r osx-x64 --self-contained /p:PublishSingleFile=true -p:DefineConstants="AVX512" -o ../output/osx-x64-avx512
56+
dotnet publish -c Release -r osx-x64 --self-contained /p:Release=true /p:PublishSingleFile=true -p:DefineConstants="AVX512" -o ../output/osx-x64-avx512
5757
5858
- name: Build and Package Non-AVX512 for OSX
5959
run: |
6060
dotnet restore
61-
dotnet publish -c Release -r osx-x64 --self-contained /p:PublishSingleFile=true -o ../output/osx-x64
61+
dotnet publish -c Release -r osx-x64 --self-contained /p:Release=true /p:PublishSingleFile=true -o ../output/osx-x64
6262
6363
# Rename the output files for consistency
6464
- name: Rename output files

src/Sapling.Engine/MathHelpers.cs

+17-11
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,31 @@ namespace Sapling.Engine
44
{
55
public static unsafe class MemoryHelpers
66
{
7-
public static T* Allocate<T>(int count) where T : unmanaged
7+
public static unsafe T* Allocate<T>(long count) where T : unmanaged
88
{
9-
const nuint alignment = 64;
9+
if (count <= 0)
10+
throw new ArgumentOutOfRangeException(nameof(count), "Count must be positive.");
1011

11-
// Use ulong to avoid overflow when calculating size
12-
ulong totalSize = (ulong)sizeof(T) * (ulong)count;
12+
const ulong alignment = 64;
1313

14-
// Check if allocation would exceed nuint.MaxValue
15-
if (totalSize > nuint.MaxValue)
16-
throw new ArgumentOutOfRangeException(nameof(count), "Requested allocation is too large");
14+
// Use ulong to prevent overflow when calculating size
15+
ulong elementSize = (ulong)sizeof(T);
16+
ulong totalSize = elementSize * (ulong)count;
1717

18-
nuint size = (nuint)totalSize;
18+
// Basic safety check: 128 TB max (adjust based on your app domain)
19+
const ulong maxAllowedSize = 128UL * 1024 * 1024 * 1024 * 1024; // 128 TB
20+
if (totalSize > maxAllowedSize)
21+
throw new ArgumentOutOfRangeException(nameof(count), $"Requested size exceeds {maxAllowedSize / (1024 * 1024 * 1024)} GB.");
22+
23+
void* block = NativeMemory.AlignedAlloc((nuint)totalSize, (nuint)alignment);
1924

20-
void* block = NativeMemory.AlignedAlloc(size, alignment);
2125
if (block == null)
22-
throw new OutOfMemoryException($"Failed to allocate {size / (1024 * 1024)} MB for {typeof(T).Name}");
26+
throw new OutOfMemoryException($"Failed to allocate {(totalSize / (1024 * 1024))} MB for {typeof(T).Name}.");
27+
28+
NativeMemory.Clear(block, (nuint)totalSize);
2329

24-
NativeMemory.Clear(block, size);
2530
return (T*)block;
2631
}
32+
2733
}
2834
}

src/Sapling.Engine/Search/NegaMaxSearch.cs

-2
Original file line numberDiff line numberDiff line change
@@ -543,8 +543,6 @@ public unsafe int
543543
HistoryHeuristicExtensions.UpdateMovesHistory(History, moves, moveIndex, m, depth);
544544

545545
*(killers + (depthFromRoot << 1)) = m;
546-
547-
548546
if (counterMoveIndex != 0)
549547
{
550548
*(Counters + counterMoveIndex) = m;

src/Sapling.Engine/Search/ParallelSearcher.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ public unsafe class ParallelSearcher
88
{
99
public readonly List<Searcher> Searchers = new();
1010
public readonly Transposition* Transpositions;
11-
public readonly int TTSize;
11+
public readonly long TTSize;
1212

1313
// Used to prevent a previous searches timeout cancelling a new search
1414
private Guid _prevSearchId = Guid.NewGuid();
1515

16-
public ParallelSearcher(int ttSize)
16+
public ParallelSearcher(long ttSize)
1717
{
1818
TTSize = ttSize;
1919
Transpositions = MemoryHelpers.Allocate<Transposition>(ttSize);

src/Sapling.Engine/Search/Searcher.cs

+5-5
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public unsafe partial class Searcher
1414
private const nuint _pvTableBytes = _pvTableLength * sizeof(uint);
1515
private readonly uint* _pVTable;
1616
public readonly Transposition* Transpositions;
17-
public readonly uint TtMask;
17+
public readonly ulong TtMask;
1818
private long _lockedUntil;
1919

2020
private bool _searchCancelled;
@@ -43,12 +43,12 @@ public unsafe partial class Searcher
4343
public readonly int* WhiteMaterialCorrHist;
4444
public readonly int* BlackMaterialCorrHist;
4545

46-
public Searcher(Transposition* transpositions, int ttCount)
46+
public Searcher(Transposition* transpositions, long ttCount)
4747
{
4848
GC.SuppressFinalize(this);
4949
Transpositions = transpositions;
5050

51-
TtMask = (uint)ttCount - 1;
51+
TtMask = (ulong)(ttCount - 1);
5252
_pVTable = MemoryHelpers.Allocate<uint>(_pvTableLength);
5353

5454
WhiteAccumulators = AllocateSearchStack(Constants.MaxSearchDepth + 1);
@@ -229,7 +229,7 @@ public void Reset(GameState inputBoard)
229229
Unsafe.CopyBlock(HashHistory, inputBoard.HashHistory, sizeof(ulong) * (uint)inputBoard.Board.TurnCount);
230230
}
231231

232-
public (List<uint> pv, int depthSearched, int score, long nodes) Search(GameState inputBoard, Action cancellSearch, List<Searcher>? searchers = null, int nodeLimit = 0,
232+
public (List<uint> pv, int depthSearched, int score, long nodes) Search(GameState inputBoard, Action cancelSearch, List<Searcher>? searchers = null, int nodeLimit = 0,
233233
int depthLimit = 0, int threadId = -1, DateTime? timeLimit = null)
234234
{
235235
var depthSearched = 0;
@@ -327,7 +327,7 @@ public void Reset(GameState inputBoard)
327327
var timeRemaining = timeLimit - DateTime.Now;
328328
if (timeRemaining.HasValue && timeRemaining.Value.TotalMilliseconds < iterationDuration.TotalMilliseconds * 1.5)
329329
{
330-
cancellSearch();
330+
cancelSearch();
331331
break;
332332
}
333333
}

src/Sapling.Engine/SkipLocalsInit.cs

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
using System.Runtime.CompilerServices;
2+
3+
[module: SkipLocalsInit] // Applies to all methods in this module

src/Sapling.Engine/Transpositions/TranspositionTableExtensions.cs

+40-20
Original file line numberDiff line numberDiff line change
@@ -83,35 +83,55 @@ public static void Set(this ref Transposition entry, ulong hash, byte depth, int
8383
entry.Flag = nodeType;
8484
entry.Move = move != 0 ? move : entry.Move; //Don't clear TT move if no best move is provided: keep old one
8585
}
86-
public static unsafe int GetMaxTranspositionTableSizeInMb()
87-
{
88-
return (int)((long)int.MaxValue * sizeof(Transposition) / (1024 * 1024));
89-
}
90-
public static unsafe uint CalculateTranspositionTableSize(int sizeInMb)
86+
87+
public static unsafe long CalculateTranspositionTableSize(long sizeInMb)
9188
{
92-
int maxAllowedSizeInMb = (int)((long)int.MaxValue * sizeof(Transposition) / (1024 * 1024));
89+
if (sizeInMb <= 0)
90+
throw new ArgumentOutOfRangeException(nameof(sizeInMb), "Size must be positive.");
9391

94-
// Cap to the maximum if necessary
95-
if (sizeInMb > maxAllowedSizeInMb)
96-
{
97-
sizeInMb = maxAllowedSizeInMb;
98-
}
92+
const long EightGB = 8L * 1024;
93+
const long TwoGB = 2L * 1024;
94+
95+
ulong estimatedCount = (ulong)sizeInMb * 1024 * 1024 / (ulong)sizeof(Transposition);
96+
97+
if (estimatedCount == 0)
98+
throw new OverflowException("Requested size is too small to store even one transposition.");
99+
100+
// Find powers of two above and below the estimated count
101+
ulong lower = BitOperations.IsPow2(estimatedCount)
102+
? estimatedCount
103+
: BitOperations.RoundUpToPowerOf2(estimatedCount) >> 1;
99104

100-
ulong transpositionCount = (ulong)sizeInMb * 1024 * 1024 / (ulong)sizeof(Transposition);
105+
ulong upper = lower << 1;
101106

102-
// Round to nearest lower power of two (adjust if needed)
103-
if (!BitOperations.IsPow2(transpositionCount))
107+
// Calculate size in MB for the rounded counts
108+
long lowerMb = (long)(lower * (ulong)sizeof(Transposition) / (1024 * 1024));
109+
long upperMb = (long)(upper * (ulong)sizeof(Transposition) / (1024 * 1024));
110+
111+
long chosenMb;
112+
113+
if (sizeInMb <= EightGB)
104114
{
105-
transpositionCount = BitOperations.RoundUpToPowerOf2(transpositionCount) >> 1;
115+
// Always round up under 8GB
116+
chosenMb = upperMb;
106117
}
107-
108-
// If still too large, clamp to int.MaxValue
109-
if (transpositionCount > int.MaxValue)
118+
else if ((upperMb - sizeInMb) <= TwoGB)
110119
{
111-
transpositionCount = (uint)(1u << 31); // 2^31 (still fits in uint)
120+
// Round up if it's within 2GB
121+
chosenMb = upperMb;
112122
}
123+
else
124+
{
125+
// Otherwise round down
126+
chosenMb = lowerMb;
127+
}
128+
129+
ulong finalCount = (ulong)chosenMb * 1024 * 1024 / (ulong)sizeof(Transposition);
130+
131+
if (finalCount == 0 || finalCount > long.MaxValue)
132+
throw new OverflowException("Final transposition table size is too large.");
113133

114-
return (uint)transpositionCount;
134+
return (long)finalCount;
115135
}
116136

117137
public static unsafe int CalculateSizeInMb(uint transpositionCount)

src/Sapling/Sapling.csproj

+16-3
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,29 @@
77
<Nullable>enable</Nullable>
88
<ApplicationIcon>logo.ico</ApplicationIcon>
99
<Title>Sapling</Title>
10-
<AssemblyVersion>1.2.2.0</AssemblyVersion>
11-
<FileVersion>1.2.2.0</FileVersion>
12-
<Version>1.2.2.0</Version>
10+
<AssemblyVersion>1.2.6.0</AssemblyVersion>
11+
<FileVersion>1.2.6.0</FileVersion>
12+
<Version>1.2.6.0</Version>
1313
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
14+
<TieredPGO>true</TieredPGO>
15+
<TieredCompilation>true</TieredCompilation>
16+
<LangVersion>preview</LangVersion>
1417
</PropertyGroup>
1518

1619
<ItemGroup>
1720
<Content Include="logo.ico" />
1821
</ItemGroup>
1922

23+
<PropertyGroup Condition="'$(Release.ToLower())'=='true'">
24+
<Configuration>Release</Configuration>
25+
<SelfContained>true</SelfContained>
26+
<PublishSingleFile>true</PublishSingleFile>
27+
<PublishTrimmed>true</PublishTrimmed>
28+
<EnableCompressionInSingleFile>true</EnableCompressionInSingleFile>
29+
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
30+
<CETCompat>false</CETCompat>
31+
</PropertyGroup>
32+
2033
<ItemGroup>
2134
<ProjectReference Include="..\Sapling.Engine\Sapling.Engine.csproj" />
2235
</ItemGroup>

src/Sapling/UciEngine.cs

+4-4
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ namespace Sapling;
1111

1212
public class UciEngine
1313
{
14-
public int TranspositionSize = (int)TranspositionTableExtensions.CalculateTranspositionTableSize(256);
14+
public long TranspositionSize = (long)TranspositionTableExtensions.CalculateTranspositionTableSize(256);
1515

1616
private static readonly string[] PositionLabels = { "position", "fen", "moves" };
1717
private static readonly string[] GoLabels = { "go", "movetime", "wtime", "btime", "winc", "binc", "movestogo" };
@@ -28,7 +28,7 @@ public class UciEngine
2828
public UciEngine(StreamWriter logWriter)
2929
{
3030
var version = typeof(Program).Assembly.GetName().Version;
31-
_version = $"{version.Major}-{version.Minor}-{version.Build}";
31+
_version = $"{version.Major}.{version.Minor}.{version.Build}";
3232

3333
_logWriter = logWriter;
3434
_parallelSearcher = new ParallelSearcher(TranspositionSize);
@@ -71,7 +71,7 @@ private void SetOption(string[] tokens)
7171
{
7272
if (tokens[3] == "value" && int.TryParse(tokens[4], out var transpositionSize))
7373
{
74-
TranspositionSize = (int)TranspositionTableExtensions.CalculateTranspositionTableSize(transpositionSize);
74+
TranspositionSize = (long)TranspositionTableExtensions.CalculateTranspositionTableSize(transpositionSize);
7575
_parallelSearcher = new(TranspositionSize);
7676
_parallelSearcher.SetThreads(_threadCount);
7777
LogToFile($"[Debug] Set Transposition Size '{TranspositionSize}'");
@@ -96,7 +96,7 @@ public void ReceiveCommand(string message)
9696
Respond("id author Tim Jones");
9797
Respond($"option name Threads type spin default {_threadCount} min 1 max 1024");
9898
Respond($"option name Ponder type check default {_ponderEnabled.ToString().ToLower()}");
99-
Respond($"option name Hash type spin default {TranspositionTableExtensions.CalculateSizeInMb((uint)TranspositionSize)} min 32 max 22000");
99+
Respond($"option name Hash type spin default {TranspositionTableExtensions.CalculateSizeInMb((uint)TranspositionSize)} min 32 max 1000000");
100100
Respond($"option name UCI_Chess960 type check default false");
101101
#if OpenBench
102102
foreach (var spsaParameters in SpsaTuner.TuningParameters.Values)

0 commit comments

Comments
 (0)