Skip to content

Commit

Permalink
feat: make yielder arguments comparable
Browse files Browse the repository at this point in the history
* Improve perfomance by reducing boxing.
  • Loading branch information
teneko committed Sep 29, 2024
1 parent 3366b9a commit 5228103
Show file tree
Hide file tree
Showing 40 changed files with 703 additions and 377 deletions.
28 changes: 26 additions & 2 deletions perf/Tenekon.Coroutines.Benchmark/AsyncIteratorBenchmark.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;

namespace Tenekon.Coroutines.Benchmark
{
[MemoryDiagnoser]
[MediumRunJob]
[ShortRunJob]
public class AsyncIteratorBenchmark
{
private const int RunLess = 9;
Expand All @@ -23,7 +24,30 @@ public async Task AsyncIterator(int runs)
var results = new List<int>();

while (await generator.MoveNextAsync()) {
results.Add(((YieldReturnArgument<int>)generator.Current).Value);
results.Add(Unsafe.As<YieldReturnArgument<int>>(generator.Current).Value);
}

static async Coroutine Generator(int runs)
{
var run = runs;
while (run-- > 0) {
await YieldReturn(run);
await Task.Yield();
}
}
}

[Benchmark]
[Arguments(RunLess)]
[Arguments(RunMore)]
[Arguments(RunMost)]
public async Task CloeableAsyncIterator(int runs)
{
var generator = Generator(runs).GetAsyncIterator(isCloneable: true);
var results = new List<int>();

while (await generator.MoveNextAsync()) {
results.Add(Unsafe.As<YieldReturnArgument<int>>(generator.Current).Value);
}

static async Coroutine Generator(int runs)
Expand Down
2 changes: 0 additions & 2 deletions perf/Tenekon.Coroutines.Benchmark/Imports.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,2 @@
global using static Tenekon.Coroutines.Yielders;
global using static Tenekon.Coroutines.Yielders.Arguments;
global using static Tenekon.Coroutines.Iterators.Yielders;
global using static Tenekon.Coroutines.Iterators.Yielders.Arguments;
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public void UnsafeOnCompleted(Action continuation)
IAsyncIteratorStateMachineHolder theirStateMachineHolder,
in SuspensionPoint ourSuspensionPoint,
ref SuspensionPoint theirSuspensionPoint) =>
CoroutineAwaiterCore.RenewStateMachineCoroutineAwaiter<TStateMachine, ConfiguredCoroutineAwaiter, ConfiguredValueTaskAwaitable.ConfiguredValueTaskAwaiter, ValueTaskAccessor, Nothing>(
CoroutineAwaiterCore.RenewStateMachineCoroutineAwaiter<TStateMachine, ConfiguredCoroutineAwaiter, ConfiguredValueTaskAwaitable.ConfiguredValueTaskAwaiter, ValueTaskAccessor, VoidCoroutineResult>(
theirStateMachineHolder,
in ourSuspensionPoint,
ref theirSuspensionPoint,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public void UnsafeOnCompleted(Action continuation)
IAsyncIteratorStateMachineHolder theirStateMachineHolder,
in SuspensionPoint ourSuspensionPoint,
ref SuspensionPoint theirSuspensionPoint) =>
CoroutineAwaiterCore.RenewStateMachineCoroutineAwaiter<TStateMachine, CoroutineAwaiter, ValueTaskAwaiter, ValueTaskAccessor, Nothing>(
CoroutineAwaiterCore.RenewStateMachineCoroutineAwaiter<TStateMachine, CoroutineAwaiter, ValueTaskAwaiter, ValueTaskAccessor, VoidCoroutineResult>(
theirStateMachineHolder,
in ourSuspensionPoint,
ref theirSuspensionPoint,
Expand Down
2 changes: 1 addition & 1 deletion src/Tenekon.Coroutines/Coroutines/Coroutine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public readonly ConfiguredCoroutineAwaitable ConfigureAwait(bool continueOnCaptu
public static implicit operator Coroutine(ValueTask task) => new(task);

public readonly IAsyncIterator GetAsyncIterator(in CoroutineContext additiveContext = default, bool isCloneable = false) =>
new AsyncIteratorImpl<Nothing>(this, in additiveContext, isCloneable: isCloneable);
new AsyncIteratorImpl<VoidCoroutineResult>(this, in additiveContext, isCloneable: isCloneable);

public readonly bool Equals(in Coroutine other) => CoroutineEqualityComparer.Equals(in this, in other);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
internal static class CoroutineAwaiterExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void DelegateCompletion(this Task task, IValueTaskCompletionSource<Nothing> completionSource)
internal static void DelegateCompletion(this Task task, IValueTaskCompletionSource<VoidCoroutineResult> completionSource)
{
var taskAwaiter = task.ConfigureAwait(false).GetAwaiter();

Expand All @@ -14,7 +14,7 @@ internal static void DelegateCompletion(this Task task, IValueTaskCompletionSour
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
static void CompleteSource(in ConfiguredTaskAwaitable.ConfiguredTaskAwaiter taskAwaiter, IValueTaskCompletionSource<Nothing> completionSource)
static void CompleteSource(in ConfiguredTaskAwaitable.ConfiguredTaskAwaiter taskAwaiter, IValueTaskCompletionSource<VoidCoroutineResult> completionSource)
{
try {
taskAwaiter.GetResult();
Expand Down Expand Up @@ -51,7 +51,7 @@ static void CompleteSource(in ConfiguredValueTaskAwaitable<TResult>.ConfiguredVa
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void DelegateCoroutineCompletion<TAwaiter>(this ref TAwaiter coroutineAwaiter, IValueTaskCompletionSource<Nothing> completionSource)
internal static void DelegateCoroutineCompletion<TAwaiter>(this ref TAwaiter coroutineAwaiter, IValueTaskCompletionSource<VoidCoroutineResult> completionSource)
where TAwaiter : struct, ICriticalNotifyCompletion, ICoroutineAwaiter
{
if (coroutineAwaiter.IsCompleted) {
Expand All @@ -62,7 +62,7 @@ internal static void DelegateCoroutineCompletion<TAwaiter>(this ref TAwaiter cor
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
static void CompleteSource(in TAwaiter coroutineAwaiter, IValueTaskCompletionSource<Nothing> completionSource)
static void CompleteSource(in TAwaiter coroutineAwaiter, IValueTaskCompletionSource<VoidCoroutineResult> completionSource)
{
try {
coroutineAwaiter.GetResult();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ internal interface ICoroutineAwaiterMethodBuilder<TResult>

internal readonly struct CoroutineAwaiterMethodBuilder<TCoroutineAwaiter>(
in TCoroutineAwaiter awaiter,
CoroutineStateMachineHolder<Nothing, CoroutineAwaiterStateMachine<CoroutineAwaiterMethodBuilder<TCoroutineAwaiter>>> stateMachineHolder) : ICoroutineAwaiterMethodBuilder
CoroutineStateMachineHolder<VoidCoroutineResult, CoroutineAwaiterStateMachine<CoroutineAwaiterMethodBuilder<TCoroutineAwaiter>>> stateMachineHolder) : ICoroutineAwaiterMethodBuilder
where TCoroutineAwaiter : struct, ICriticalNotifyCompletion, ICoroutineAwaiter
{
public readonly bool IsCompleted => _awaiter.IsCompleted;

public readonly TCoroutineAwaiter _awaiter = awaiter;
public readonly CoroutineStateMachineHolder<Nothing,CoroutineAwaiterStateMachine<CoroutineAwaiterMethodBuilder<TCoroutineAwaiter>>> _stateMachineHolder = stateMachineHolder;
public readonly CoroutineStateMachineHolder<VoidCoroutineResult,CoroutineAwaiterStateMachine<CoroutineAwaiterMethodBuilder<TCoroutineAwaiter>>> _stateMachineHolder = stateMachineHolder;

public readonly void AwaitUnsafeOnCompleted() => _awaiter.UnsafeOnCompleted(_stateMachineHolder.MoveNextAction);

Expand Down
3 changes: 2 additions & 1 deletion src/Tenekon.Coroutines/Coroutines/CoroutineContext.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.ComponentModel;
using Tenekon.Coroutines.Sources;

namespace Tenekon.Coroutines;

Expand Down Expand Up @@ -53,7 +54,7 @@ internal static CoroutineContext CreateInternal(
internal int _identifier;
#endif

internal ICoroutineResultStateMachineHolder ResultStateMachine => _resultStateMachine ??= CoroutineStateMachineHolder<Nothing>.s_synchronousSuccessSentinel;
internal ICoroutineResultStateMachineHolder ResultStateMachine => _resultStateMachine ??= CoroutineStateMachineHolder<VoidCoroutineResult>.s_synchronousSuccessSentinel;

[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
Expand Down
14 changes: 7 additions & 7 deletions src/Tenekon.Coroutines/Coroutines/CoroutineMethodBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ public static CoroutineMethodBuilder Create()

public Coroutine Task {
get {
var stateMachineHolder = _stateMachineHolder ??= CoroutineMethodBuilder<Nothing>.CreateWeaklyTyedStateMachineBox();
var stateMachineHolder = _stateMachineHolder ??= CoroutineMethodBuilder<VoidCoroutineResult>.CreateWeaklyTyedStateMachineBox();
return new Coroutine(new ValueTask(stateMachineHolder, stateMachineHolder.Version), stateMachineHolder);
}
}

private CoroutineStateMachineHolder<Nothing> _stateMachineHolder;
private CoroutineStateMachineHolder<VoidCoroutineResult> _stateMachineHolder;

public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine =>
_ = CoroutineMethodBuilder<Nothing>.GetStateMachineHolder(ref stateMachine, ref _stateMachineHolder);
_ = CoroutineMethodBuilder<VoidCoroutineResult>.GetStateMachineHolder(ref stateMachine, ref _stateMachineHolder);

public readonly void SetException(Exception e) => _stateMachineHolder.SetException(e);

Expand All @@ -29,17 +29,17 @@ public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMac
public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
where TAwaiter : INotifyCompletion
where TStateMachine : IAsyncStateMachine =>
CoroutineMethodBuilder<Nothing>.AwaitOnCompleted(ref awaiter, ref stateMachine, ref _stateMachineHolder);
CoroutineMethodBuilder<VoidCoroutineResult>.AwaitOnCompleted(ref awaiter, ref stateMachine, ref _stateMachineHolder);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
where TAwaiter : ICriticalNotifyCompletion
where TStateMachine : IAsyncStateMachine =>
CoroutineMethodBuilder<Nothing>.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine, ref _stateMachineHolder);
CoroutineMethodBuilder<VoidCoroutineResult>.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine, ref _stateMachineHolder);

internal IAsyncIteratorStateMachineHolder<Nothing> ReplaceCoroutineUnderlyingStateMachine<TStateMachine>(ref TStateMachine stateMachine)
internal IAsyncIteratorStateMachineHolder<VoidCoroutineResult> ReplaceCoroutineUnderlyingStateMachine<TStateMachine>(ref TStateMachine stateMachine)
where TStateMachine : IAsyncStateMachine =>
CoroutineMethodBuilder<Nothing>.RenewCoroutineStateMachineHolder(ref stateMachine, ref _stateMachineHolder);
CoroutineMethodBuilder<VoidCoroutineResult>.RenewCoroutineStateMachineHolder(ref stateMachine, ref _stateMachineHolder);

public void SetStateMachine(IAsyncStateMachine stateMachine) => throw new NotImplementedException();
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ internal static Coroutine MakeChildCoroutine<TCoroutineAwaiter>(ref TCoroutineAw
where TCoroutineAwaiter : struct, ICriticalNotifyCompletion, ICoroutineAwaiter
{
Debug.Assert(contextToBequest.BequesterOrigin == CoroutineContextBequesterOrigin.ChildCoroutine);
var stateMachineHolder = CoroutineStateMachineHolder<Nothing, CoroutineAwaiterStateMachine<CoroutineAwaiterMethodBuilder<TCoroutineAwaiter>>>.RentFromCache();
var stateMachineHolder = CoroutineStateMachineHolder<VoidCoroutineResult, CoroutineAwaiterStateMachine<CoroutineAwaiterMethodBuilder<TCoroutineAwaiter>>>.RentFromCache();
var coroutineBuilder = new CoroutineAwaiterMethodBuilder<TCoroutineAwaiter>(in coroutineAwaiter, stateMachineHolder);
var stateMachine = new CoroutineAwaiterStateMachine<CoroutineAwaiterMethodBuilder<TCoroutineAwaiter>>(coroutineBuilder) {
_state = -1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ void IAsyncIteratorStateMachineHolder<TResult>.SetAsyncIteratorCompletionSource(

void IAsyncIteratorStateMachineHolder<TResult>.SetException(Exception e) => _valueTaskSource._valueTaskSource.SetException(e);

IAsyncIteratorStateMachineHolder<Nothing> IAsyncIteratorStateMachineHolder.CreateNewByCloningUnderlyingStateMachine(
IAsyncIteratorStateMachineHolder<VoidCoroutineResult> IAsyncIteratorStateMachineHolder.CreateNewByCloningUnderlyingStateMachine(
in SuspensionPoint ourSuspensionPoint,
ref SuspensionPoint theirSuspensionPoint)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Threading.Tasks.Sources;
using Tenekon.Coroutines.Sources;

namespace Tenekon.Coroutines;

Expand Down
4 changes: 2 additions & 2 deletions src/Tenekon.Coroutines/Coroutines/Iterators/AsyncIterator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ public static class AsyncIterator

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IAsyncIterator Create(Func<Coroutine> provider, in CoroutineContext additiveContext = default, bool isCloneable = false) =>
new AsyncIteratorImpl<Nothing>(provider, in additiveContext, isCloneable: isCloneable);
new AsyncIteratorImpl<VoidCoroutineResult>(provider, in additiveContext, isCloneable: isCloneable);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IAsyncIterator Create(Coroutine coroutine, in CoroutineContext additiveContext = default, bool isCloneable = false) =>
new AsyncIteratorImpl<Nothing>(coroutine, in additiveContext, isCloneable: isCloneable);
new AsyncIteratorImpl<VoidCoroutineResult>(coroutine, in additiveContext, isCloneable: isCloneable);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IAsyncIterator<TResult> Create<TResult>(Func<Coroutine<TResult>> provider, in CoroutineContext additiveContext = default, bool isCloneable = false) =>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Diagnostics;
using Tenekon.Coroutines.CompilerServices;
using Tenekon.Coroutines.Sources;

namespace Tenekon.Coroutines.Iterators;

Expand Down Expand Up @@ -219,7 +220,7 @@ public void YieldReturn()
if ((_nextSuspensionPoint._state & SuspensionPointState.ArgumentSupplied) != 0) {
iteratorContext._iteratorContextService._currentSuspensionPoint.RequireAwaiterCompletionNotifier();
Debug.Assert(_nextSuspensionPoint._argumentCompletionSource is not null);
_nextSuspensionPoint._argumentCompletionSource.SetResult(default(Nothing)); // ISSUE: hard assumption
_nextSuspensionPoint._argumentCompletionSource.SetResult(default(VoidCoroutineResult)); // ISSUE: hard assumption
iteratorContext._coroutineStateMachineHolder?.MoveNext();
}
} finally {
Expand Down Expand Up @@ -351,9 +352,9 @@ IAsyncIterator IAsyncIterator.Clone()
var theirIteratorContextService = new AsyncIteratorContextService(in SuspensionPoint.AwaiterCompletionNotifierRequired, isAsyncIteratorCloneable: true);
var theirStateMachineHolder = ourStateMachineHolder.CreateNewByCloningUnderlyingStateMachine(in _nextSuspensionPoint, ref theirIteratorContextService._currentSuspensionPoint);
theirStateMachineHolder.CoroutineContext = ourIteratorContext._iteratorAgnosticCoroutineContext;
var theirIterator = new AsyncIteratorImpl<Nothing>(new Coroutine(theirStateMachineHolder, theirStateMachineHolder.Version), _additiveContext, isCloneable: true);
var theirIterator = new AsyncIteratorImpl<VoidCoroutineResult>(new Coroutine(theirStateMachineHolder, theirStateMachineHolder.Version), _additiveContext, isCloneable: true);
{ // Initialize their iterator
var theirIteratorContext = new AsyncIteratorImpl<Nothing>.AsyncIteratorContext(theirIteratorContextService) {
var theirIteratorContext = new AsyncIteratorImpl<VoidCoroutineResult>.AsyncIteratorContext(theirIteratorContextService) {
_iteratorAgnosticCoroutineContext = ourIteratorContext._iteratorAgnosticCoroutineContext,
_coroutineStateMachineHolder = theirStateMachineHolder,
_coroutineAwaiter = theirIterator._coroutineHolder.Coroutine.GetAwaiter()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ internal interface IAsyncIteratorStateMachineHolder : ICoroutineStateMachineHold
{
short Version { get; }

IAsyncIteratorStateMachineHolder<Nothing> CreateNewByCloningUnderlyingStateMachine(in SuspensionPoint ourSuspensionPoint, ref SuspensionPoint theirSuspensionPoint);
IAsyncIteratorStateMachineHolder<VoidCoroutineResult> CreateNewByCloningUnderlyingStateMachine(in SuspensionPoint ourSuspensionPoint, ref SuspensionPoint theirSuspensionPoint);
}

internal interface IAsyncIteratorStateMachineHolder<TResult> : IAsyncIteratorStateMachineHolder, ICoroutineStateMachineHolder<TResult>, IValueTaskSource<TResult>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace Tenekon.Coroutines.Iterators;
using Tenekon.Coroutines.Sources;

namespace Tenekon.Coroutines.Iterators;

internal struct SuspensionPoint
{
Expand Down Expand Up @@ -30,11 +32,11 @@ internal void SupplyArgument(Key argumentKey, ICallableArgument argument, ICorou
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void BeginSupplyingAwaiterCompletionNotifier(out ManualResetCoroutineCompletionSource<Nothing> externTaskCompletionNotifierSource)
private void BeginSupplyingAwaiterCompletionNotifier(out ManualResetCoroutineCompletionSource<VoidCoroutineResult> externTaskCompletionNotifierSource)
{
ThrowIfNotRequiringAwaiterCompletionNotifier(in this);
_state = SuspensionPointState.AwaiterCompletionNotifierSupplied | (_state & ~SuspensionPointState.AwaiterCompletionNotifierRequired);
externTaskCompletionNotifierSource = ManualResetCoroutineCompletionSource<Nothing>.RentFromCache();
externTaskCompletionNotifierSource = ManualResetCoroutineCompletionSource<VoidCoroutineResult>.RentFromCache();
_awaiterCompletionNotifier = externTaskCompletionNotifierSource.CreateValueTask();
}

Expand Down
36 changes: 0 additions & 36 deletions src/Tenekon.Coroutines/Coroutines/Iterators/Yielders.Exchange.cs

This file was deleted.

13 changes: 0 additions & 13 deletions src/Tenekon.Coroutines/Coroutines/Iterators/Yielders.cs

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

Loading

0 comments on commit 5228103

Please sign in to comment.