Merge safe fiber allocation reduction
This commit is contained in:
commit
a1f5e6ebef
|
|
@ -49,6 +49,7 @@ public static class FiberExports
|
|||
private const int FiberMagicEndOffset = 104;
|
||||
|
||||
private static int _contextSizeCheck;
|
||||
private static readonly bool TraceEnabled = Environment.GetEnvironmentVariable("SHARPEMU_LOG_FIBER") == "1";
|
||||
|
||||
private static readonly object _fiberGate = new();
|
||||
private static readonly ConcurrentDictionary<ulong, FiberContinuation> _continuations = new();
|
||||
|
|
@ -287,9 +288,10 @@ public static class FiberExports
|
|||
|
||||
_ = GuestThreadExecution.EnterFiber(0);
|
||||
GuestThreadExecution.RequestCurrentContextTransfer(transferTarget);
|
||||
TraceFiber(
|
||||
$"return-to-thread fiber=0x{fiberAddress:X16} " +
|
||||
$"resume=0x{transferTarget.Rip:X16} rsp=0x{transferTarget.Rsp:X16} arg=0x{returnArgument:X16}");
|
||||
if (TraceEnabled)
|
||||
TraceFiber(
|
||||
$"return-to-thread fiber=0x{fiberAddress:X16} " +
|
||||
$"resume=0x{transferTarget.Rip:X16} rsp=0x{transferTarget.Rsp:X16} arg=0x{returnArgument:X16}");
|
||||
return SetReturn(ctx, 0);
|
||||
}
|
||||
|
||||
|
|
@ -335,7 +337,7 @@ public static class FiberExports
|
|||
return SetReturn(ctx, FiberErrorInvalid);
|
||||
}
|
||||
|
||||
if (!TryReadFiberFields(ctx, fiber, out var fields))
|
||||
if (!TryReadFiberFields(ctx, fiber, out var fields, readName: true))
|
||||
{
|
||||
return SetReturn(ctx, FiberErrorInvalid);
|
||||
}
|
||||
|
|
@ -521,9 +523,10 @@ public static class FiberExports
|
|||
_stackRanges[fiber] = new FiberStackRange(contextAddress, contextSize);
|
||||
}
|
||||
|
||||
TraceFiber(
|
||||
$"init fiber=0x{fiber:X16} entry=0x{entry:X16} ctx=0x{contextAddress:X16} " +
|
||||
$"size=0x{contextSize:X} flags=0x{flags:X} build=0x{buildVersion:X8} name='{name}'");
|
||||
if (TraceEnabled)
|
||||
TraceFiber(
|
||||
$"init fiber=0x{fiber:X16} entry=0x{entry:X16} ctx=0x{contextAddress:X16} " +
|
||||
$"size=0x{contextSize:X} flags=0x{flags:X} build=0x{buildVersion:X8} name='{name}'");
|
||||
return SetReturn(ctx, 0);
|
||||
}
|
||||
|
||||
|
|
@ -583,7 +586,8 @@ public static class FiberExports
|
|||
}
|
||||
if (fields.State != FiberStateIdle)
|
||||
{
|
||||
TraceFiber($"run-state-error reason={reason} fiber=0x{fiber:X16} state=0x{fields.State:X8}");
|
||||
if (TraceEnabled)
|
||||
TraceFiber($"run-state-error reason={reason} fiber=0x{fiber:X16} state=0x{fields.State:X8}");
|
||||
return SetReturn(ctx, FiberErrorState);
|
||||
}
|
||||
|
||||
|
|
@ -670,9 +674,10 @@ public static class FiberExports
|
|||
|
||||
_ = GuestThreadExecution.EnterFiber(fiber);
|
||||
GuestThreadExecution.RequestCurrentContextTransfer(transferTarget);
|
||||
TraceFiber(
|
||||
$"transfer reason={reason} from=0x{previousFiber:X16} to=0x{fiber:X16} resume={resumed} " +
|
||||
$"rip=0x{transferTarget.Rip:X16} rsp=0x{transferTarget.Rsp:X16} arg=0x{argOnRun:X16}");
|
||||
if (TraceEnabled)
|
||||
TraceFiber(
|
||||
$"transfer reason={reason} from=0x{previousFiber:X16} to=0x{fiber:X16} resume={resumed} " +
|
||||
$"rip=0x{transferTarget.Rip:X16} rsp=0x{transferTarget.Rsp:X16} arg=0x{argOnRun:X16}");
|
||||
return SetReturn(ctx, 0);
|
||||
}
|
||||
|
||||
|
|
@ -971,7 +976,7 @@ public static class FiberExports
|
|||
return true;
|
||||
}
|
||||
|
||||
private static bool TryReadFiberFields(CpuContext ctx, ulong fiber, out FiberFields fields)
|
||||
private static bool TryReadFiberFields(CpuContext ctx, ulong fiber, out FiberFields fields, bool readName = false)
|
||||
{
|
||||
fields = default;
|
||||
if (!TryReadUInt32(ctx, fiber + FiberStateOffset, out var state) ||
|
||||
|
|
@ -980,7 +985,7 @@ public static class FiberExports
|
|||
!TryReadUInt64(ctx, fiber + FiberContextAddressOffset, out var contextAddress) ||
|
||||
!TryReadUInt64(ctx, fiber + FiberContextSizeOffset, out var contextSize) ||
|
||||
!TryReadUInt32(ctx, fiber + FiberFlagsOffset, out var flags) ||
|
||||
!TryReadInlineName(ctx, fiber + FiberNameOffset, out var name))
|
||||
!TryReadInlineName(ctx, fiber + FiberNameOffset, out var name, decode: readName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
@ -1065,7 +1070,7 @@ public static class FiberExports
|
|||
return ctx.Memory.TryWrite(address, buffer);
|
||||
}
|
||||
|
||||
private static bool TryReadInlineName(CpuContext ctx, ulong address, out string value)
|
||||
private static bool TryReadInlineName(CpuContext ctx, ulong address, out string value, bool decode)
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[MaxNameLength + 1];
|
||||
if (!ctx.Memory.TryRead(address, buffer))
|
||||
|
|
@ -1080,7 +1085,9 @@ public static class FiberExports
|
|||
length = buffer.Length;
|
||||
}
|
||||
|
||||
value = Encoding.UTF8.GetString(buffer[..length]);
|
||||
// A switch validates the same bytes but does not need a managed name.
|
||||
// Only sceFiberGetInfo exposes the decoded name to its caller.
|
||||
value = decode ? Encoding.UTF8.GetString(buffer[..length]) : string.Empty;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -1117,10 +1124,7 @@ public static class FiberExports
|
|||
|
||||
private static void TraceFiber(string message)
|
||||
{
|
||||
if (string.Equals(Environment.GetEnvironmentVariable("SHARPEMU_LOG_FIBER"), "1", StringComparison.Ordinal))
|
||||
{
|
||||
Console.Error.WriteLine($"[LOADER][TRACE] fiber.{message}");
|
||||
}
|
||||
Console.Error.WriteLine($"[LOADER][TRACE] fiber.{message}");
|
||||
}
|
||||
|
||||
private readonly record struct FiberFields(
|
||||
|
|
|
|||
|
|
@ -81,6 +81,49 @@ public sealed class FiberSwitchLoopTests : IDisposable
|
|||
FiberExports.ResetRuntimeState();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WarmSwitchLoop_BoundsAllocationAndPreservesResumeArguments()
|
||||
{
|
||||
var memory = new FakeCpuMemory(Base, RegionSize);
|
||||
var context = new CpuContext(memory, Generation.Gen5);
|
||||
InitializeFiber(memory, context, FiberA, NameA, EntryA, ArgOnInitializeA, ContextA);
|
||||
InitializeFiber(memory, context, FiberB, NameB, EntryB, ArgOnInitializeB, ContextB);
|
||||
GuestThreadExecution.EnterImportCallFrame(RootReturnRip, RootResumeRsp, RootReturnSlot);
|
||||
context[CpuRegister.Rdi] = FiberA;
|
||||
context[CpuRegister.Rdx] = ArgSlotRoot;
|
||||
Assert.Equal(0, FiberExports.FiberRun(context));
|
||||
Assert.True(GuestThreadExecution.TryConsumeCurrentContextTransfer(out _));
|
||||
|
||||
void SwitchOnce(int index)
|
||||
{
|
||||
var toB = (index & 1) == 0;
|
||||
GuestThreadExecution.EnterImportCallFrame(toB ? ResumeRipA : ResumeRipB,
|
||||
toB ? ResumeRspA : ResumeRspB, toB ? ReturnSlotA : ReturnSlotB);
|
||||
context[CpuRegister.Rdi] = toB ? FiberB : FiberA;
|
||||
context[CpuRegister.Rsi] = (ulong)index;
|
||||
context[CpuRegister.Rdx] = toB ? ArgSlotA : ArgSlotB;
|
||||
if (FiberExports.FiberSwitch(context) != 0 ||
|
||||
!GuestThreadExecution.TryConsumeCurrentContextTransfer(out _))
|
||||
throw new InvalidOperationException("Fiber switch failed.");
|
||||
}
|
||||
|
||||
for (var i = 0; i < 2000; i++) SwitchOnce(i);
|
||||
const int count = 20000;
|
||||
var allocated = GC.GetAllocatedBytesForCurrentThread();
|
||||
var start = System.Diagnostics.Stopwatch.GetTimestamp();
|
||||
for (var i = 0; i < count; i++) SwitchOnce(i);
|
||||
var elapsed = System.Diagnostics.Stopwatch.GetElapsedTime(start);
|
||||
allocated = GC.GetAllocatedBytesForCurrentThread() - allocated;
|
||||
Console.WriteLine($"Fiber switches: bytes/call={allocated / (double)count:F1}, us/call={elapsed.TotalMicroseconds / count:F3}");
|
||||
// Previously 912 bytes per switch with tracing disabled. Keep room for
|
||||
// continuation storage, but do not format names or discarded traces.
|
||||
if (Environment.GetEnvironmentVariable("SHARPEMU_LOG_FIBER") != "1")
|
||||
Assert.True(allocated < count * 600L, $"Allocated {allocated} bytes for {count} switches.");
|
||||
Assert.Equal((ulong)(count - 1), ReadUInt64(memory, ArgSlotA));
|
||||
Assert.Equal((ulong)(count - 2), ReadUInt64(memory, ArgSlotB));
|
||||
Assert.Equal(FiberA, GuestThreadExecution.CurrentFiberAddress);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RepeatedSwitch_RestoresEachFiberOwnResumePoint()
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue