l4 evict and swiss api improve
This commit is contained in:
@@ -16,6 +16,10 @@ 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)) {
|
||||
@@ -23,12 +27,13 @@ namespace Nitload.Common.HighPerformance.Memory
|
||||
}
|
||||
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.TryGet(as_sp[i], out ulong map_handle_adr))
|
||||
if (_addressMap.TryGetWrite(as_sp[i], out ulong map_handle_adr))
|
||||
{
|
||||
L4.CacheLineMetadata* map_handle = (L4.CacheLineMetadata*)map_handle_adr;
|
||||
map_handle->IncreasePredictCount();
|
||||
@@ -38,13 +43,31 @@ namespace Nitload.Common.HighPerformance.Memory
|
||||
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) {
|
||||
if (total_count >= 2048)
|
||||
{
|
||||
_ = Interlocked.Add(ref _predict_count, total_count);
|
||||
_last_evict_pos = e.CurrentOffset;
|
||||
return total_count;
|
||||
}
|
||||
}
|
||||
@@ -53,22 +76,36 @@ namespace Nitload.Common.HighPerformance.Memory
|
||||
_ = 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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
goto 本身没问题,但当前控制流有几个需要先确认的点:
|
||||
|
||||
高:换 predictor 的分支看起来反了。 MoveNextPredictor() 在 _predict 到达 _enter 时返回 true。因此 L4.Predict.cs:51 现在会在队列耗尽时再次取 predictor;TryGetCurrentPredictor 失败后仍 goto Predict,接着会用空的 p 调 PredictMission。反过来,有下一个 predictor 时它会提前返回,不再处理后续 predictor。
|
||||
|
||||
高:新分配的 metadata 没有增加预测计数。 现有映射分支会调用 IncreasePredictCount(),但新分配分支只分配并写入 Swiss。若该计数表示未消费预测数,新条目也需要相同的计数处理;还要确认 TLSF 重用 metadata 时会重置旧计数。
|
||||
|
||||
高:同一轮内重复的 AddressSource 可能重复分配。 SwissTable.TryGet 只查 published buffer,而新条目先写入 inactive buffer。后续再次预测相同 key 时会查不到,重新分配 metadata,再由 TryAddOrSet 覆盖先前 value,旧分配就失去映射。
|
||||
|
||||
中:需要校验 PredictMission 的返回值范围。 Span 容量是 4;count > 4 会让 as_sp[i] 越界,负值则被当作本轮结束。建议契约明确要求 0..4,并在 L4 边界验证。
|
||||
|
||||
本轮未改代码。L4.Predict.cs 的文件诊断通过,但尚未运行完整构建或运行时测试。
|
||||
*/
|
||||
// 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.
|
||||
|
||||
Reference in New Issue
Block a user