112 lines
4.3 KiB
C#
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.
|