高效的信令任务的第三方物流落成于频繁reoccuring事件高效、频繁、第三方物流、信令

2023-09-03 17:10:52 作者:你的眼里是星辰

我正在模拟系统,除其他外,允许以离散模拟时间步任务的执行。执行所有发生在仿真线程的上下文中,但是,从使用该系统的操作者的角度出发,他们希望表现异步。值得庆幸的是第三方物流,以方便异步/计谋的关键字,使这相当简单。我对仿真这样一个原始的方法:

I'm working on a simulation system that, among other things, allows for the execution of tasks in discrete simulated time steps. Execution all occurs in the context of the simulation thread, but, from the perspective of an 'operator' using the system, they wish to behave asynchronously. Thankfully the TPL, with the handy 'async/await' keywords, makes this fairly straightforward. I have a primitive method on the Simulation like this:

    public Task CycleExecutedEvent()
    {
        lock (_cycleExecutedBroker)
        {
            if (!IsRunning) throw new TaskCanceledException("Simulation has been stopped");
            return _cycleExecutedBroker.RegisterForCompletion(CycleExecutedEventName);
        }
    }

这基本上是创建一个新的TaskCompletionSource,然后返回任务。该任务的目的是当上了模拟新的ExecuteCycle时执行的延续。

This is basically creating a new TaskCompletionSource and then returning a Task. The purpose of this Task is to execute its continuation when the new 'ExecuteCycle' on the simulation occurs.

然后我有一些像这样的扩展方法:

I then have some extension methods like this:

    public static async Task WaitForDuration(this ISimulation simulation, double duration)
    {
        double startTime = simulation.CurrentSimulatedTime;
        do
        {
            await simulation.CycleExecutedEvent();
        } while ((simulation.CurrentSimulatedTime - startTime) < duration);
    }

    public static async Task WaitForCondition(this ISimulation simulation, Func<bool> condition)
    {
        do
        {
            await simulation.CycleExecutedEvent();
        } while (!condition());
    }

这些都是非常方便的,那么,对于从运营商的角度构建序列,同时根据条件动作和等待的模拟时间段。我遇到的问题是,CycleExecuted发生非常频繁(大约每隔几毫秒,如果我以充分加速的速度运行)。由于这些等待的辅助方法注册一个新的在每个周期等待,这将导致大量成交TaskCompletionSource实例。

These are very handy, then, for building sequences from an 'operator' perspective, taking actions based on conditions and waiting for periods of simulated time. The issue I'm running into is that CycleExecuted occurs very frequently (roughly every few milliseconds if I'm running at fully accelerated speed). Because these 'wait' helper methods register a new 'await' on each cycle, this causes a large turnover in TaskCompletionSource instances.

我已经异形我的code和我发现,我的总的CPU时间约为5.5%,在这些落成,其中只有一个微不足道的百分比花费在活动code中度过的。有效地所有的时间都花在注册新落成在等待触发条件是有效的。

I've profiled my code and I've found that roughly 5.5% of my total CPU time is spent within these completions, of which only a negligible percentage is spent in the 'active' code. Effectively all of the time is spent registering new completions while waiting for the triggering conditions to be valid.

我的问题:我怎么能提高性能在这里,同时仍保留的异步/计谋模式的便利性写作运营商的行为?我想我需要的东西就像一个轻量级的和/或可重复使用的TaskCompletionSource,因为触发事件频繁发生。

My question: how can I improve performance here while still retaining the convenience of the async/await pattern for writing 'operator behaviors'? I'm thinking I need something like a lighter-weight and/or reusable TaskCompletionSource, given that the triggering event occurs so frequently.

我已经做了一点研究,这听起来像一个很好的选择是创建一个自定义实现的Awaitable模式,它可以直接扎入的情况下,省去了一堆TaskCompletionSource和任务实例。它也许会有帮助的原因是有很多不同的延续等待CycleExecutedEvent,他们需要频繁地等待着它。因此,理想的我在看的方式,只是排队延续回调,那么回调一切都在排队,每当事件发生时。我会继续挖掘,但我欢迎任何的帮助,如果人们知道一个干净的方式来做到这一点。

I've been doing a bit more research and it sounds like a good option would be to create a custom implementation of the Awaitable pattern, which could tie directly into the event, eliminating the need for a bunch of TaskCompletionSource and Task instances. The reason it could be useful here is that there are a lot of different continuations awaiting the CycleExecutedEvent and they need to await it frequently. So ideally I'm looking at a way to just queue up continuation callbacks, then call back everything in the queue whenever the event occurs. I'll keep digging, but I welcome any help if folks know a clean way to do this.

有关任何人浏览,将来这个问题,这里是自定义awaiter我放在一起:

For anybody browsing this question in the future, here is the custom awaiter I put together:

public sealed class CycleExecutedAwaiter : INotifyCompletion
{
    private readonly List<Action> _continuations = new List<Action>();

    public bool IsCompleted
    {
        get { return false; }
    }

    public void GetResult()
    {
    }

    public void OnCompleted(Action continuation)
    {
        _continuations.Add(continuation);
    }

    public void RunContinuations()
    {
        var continuations = _continuations.ToArray();
        _continuations.Clear();
        foreach (var continuation in continuations)
            continuation();
    }

    public CycleExecutedAwaiter GetAwaiter()
    {
        return this;
    }
}

而在模拟器:

And in the Simulator:

    private readonly CycleExecutedAwaiter _cycleExecutedAwaiter = new CycleExecutedAwaiter();

    public CycleExecutedAwaiter CycleExecutedEvent()
    {
        if (!IsRunning) throw new TaskCanceledException("Simulation has been stopped");
        return _cycleExecutedAwaiter;
    }

这是一个有点滑稽,因为awaiter永远不会报告完成,但大火持续呼叫完成,因为他们登记;尽管如此,它很适合这种应用。这减少了CPU的开销由5.5%至2.1%。它可能会仍然需要一些调整,但它是一个不错的改进,原来的。

It's a bit funny, as the awaiter never reports Complete, but fires continues to call completions as they are registered; still, it works well for this application. This reduces the CPU overhead from 5.5% to 2.1%. It will likely still require some tweaking, but it's a nice improvement over the original.

推荐答案

计谋关键字不只是在任务工作 S,它适用于任何遵循awaitable模式。有关详细信息,请参见斯蒂芬Toub的文章的等待着什么; 的。

The await keyword doesn't work just on Tasks, it works on anything that follows the awaitable pattern. For details, see Stephen Toub's article await anything;.

短的版本是,该类型必须有一个方法 GetAwaiter()返回实现 INotifyCompletion ,也有 IsCompleted 属性和调用getResult ()办法(无效 -returning,如果计谋 EX pression不该'T有一个值)。举一个例子,请参阅 TaskAwaiter

The short version is that the type has to have a method GetAwaiter() that returns a type that implements INotifyCompletion and also has IsCompleted property and GetResult() method (void-returning, if the await expression shouldn't have a value). For an example, see TaskAwaiter.

如果您创建自己的awaitable,你可以每次都返回相同的对象,避免分配很多 TaskCompletionSource S的开销。

If you create your own awaitable, you could return the same object every time, avoiding the overhead of allocating many TaskCompletionSources.