Files
stgsharp/src/Nitload.Common/HighPerformance/Memory/L4.Predict.cs
T

112 lines
4.3 KiB
C#

// -----------------------------------------------------------------------------
// file="L4.Predict"
// Project: StgSharp
// Copyright (c) Nitload.
// SPDX-License-Identifier: MIT
// -----------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Nitload.Common.HighPerformance.Memory
{
public unsafe partial class L4
{
private int _active_line_count;
private int _last_evict_pos;
internal partial int Predict()
{
if (!TryGetCurrentPredictor(out IL4Predictor? p)) {
return 0;
}
int total_count = 0, count;
Span<L4.AddressSource> as_sp = stackalloc L4.AddressSource[4];
SwissEnumerator e = _addressMap.GetWriteBufferEnumerator();
Predict:
while ((count = p.PredictMission(in as_sp)) > 0)
{
for (int i = 0; i < count; i++)
{
if (_addressMap.TryGetWrite(as_sp[i], out ulong map_handle_adr))
{
L4.CacheLineMetadata* map_handle = (L4.CacheLineMetadata*)map_handle_adr;
map_handle->IncreasePredictCount();
map_handle->CheckCount();
} else
{
uint size = as_sp[i].Size;
int handle_idx = _bufferAllocator.Alloc(size);
L4.CacheLineMetadata* handle = _bufferAllocator.MetadataAt(handle_idx);
p.Prefetch(as_sp[i], handle->Position, handle->SizeMask);
if (!_addressMap.TryAddOrSet(as_sp[i], (ulong)handle)) {
throw new InvalidOperationException();
}
_active_line_count++;
if (_active_line_count >= MaxActiveCacheLineCount)
{
int retry = 0;
const int max_retry = 3;
for (; retry < max_retry && EvictOnce(ref e); retry++) {
e = _addressMap.GetWriteBufferEnumerator(_last_evict_pos);
}
if (retry >= max_retry) {
throw new OverflowException("Evict failed, maybe all cache lines are in use.");
}
_active_line_count--;
}
}
}
total_count += count;
if (total_count >= 2048)
{
_ = Interlocked.Add(ref _predict_count, total_count);
_last_evict_pos = e.CurrentOffset;
return total_count;
}
}
if (MoveNextPredictor())
{
_ = TryGetCurrentPredictor(out p);
goto Predict;
}
_ = Interlocked.Add(ref _predict_count, total_count);
_last_evict_pos = e.CurrentOffset;
return total_count;
}
private bool EvictOnce(
ref SwissEnumerator e
)
{
while (e.MoveNext())
{
(AddressSource source, ulong cache_head_ptr) = e.Current;
L4.CacheLineMetadata* handle = (L4.CacheLineMetadata*)cache_head_ptr;
if (Volatile.Read(ref handle->ConsumeCount) == Volatile.Read(ref handle->ConsumeCount))
{
// evict
_ = _addressMap.TryRemove(source, out _);
_bufferAllocator.Free(handle);
return true;
}
}
return false;
}
}
}
// Eviction follow-up:
// 1. Compare ConsumeCount with PredictCount; reset both on TLSF slot reuse and count new predictions.
// 2. Fix retry/result handling; decrement _active_line_count only after a successful eviction.
// 3. Correct TLSF reverse-slab pointer validation before freeing metadata pointers.
// 4. Defer TLSF Free until retired published buffers and outstanding handles can no longer reference the line.
// 5. Add Swiss TryRemoveAt(offset) to tombstone the enumerated slot without hashing and probing again.