使用任务执行重复操作

时间:2018-06-08 22:36:53

标签: c# multithreading asynchronous async-await

我有50个机器学习代理。每一帧,他们得到一些输入并计算神经网络。因为每个代理都是独立的,所以我想让每个代理将网络计算为一个单独的任务。

如果我要为每个代理创建一个任务,每个帧,它将使我的程序更慢。我试图将我的代理分为两个任务(25和25),但它仍然是一个开销。

我看到它的方式是在开始时为n组代理创建n个线程,并以某种方式查询每个帧的那些线程。线程将计算代理组的网络,然后等到下一个查询。

我已经阅读了一些关于这个主题的文章,我发现我无法重用任务。那么,哪种解决方法可以起作用?

基本上,我对50个代理程序进行了重复操作,每个框架运行一次,大约一分钟,并且不会并行化它们是浪费。

我仍然是多线程和任务的新手,所以我依靠你的帮助。

<小时/> 附注:我在Unity中使用遗传算法。 下面是我试图将代理分成n组并在n个任务中计算其网络的代码。

public async Task EvaluateAsync(int groupSize = 10)
{
    var groups = genomes.Select((g, i) => new { Value = g, Index = i })
                        .GroupBy(x => x.Index / groupSize)
                        .Select(x => x.Select(v => v.Value));

    var tasks = groups.Select(g =>
    {
        return Task.Run(() =>
        {
            foreach (var element in g)
                element.Fitness += ComputeFitness(element as NeuralGenome);
        });
    }).ToArray();

    for (var i = 0; i < tasks.Length; i++)
        await tasks[i];
}

在我调用的Update()函数中:

EvaluateAsync(25).Wait();

当网络非常大时,它会快一点,但只有10个神经元时速度要慢得多。

只有在网络非常庞大的情况下,使组变小,才能获得更好的性能。

这里我为每个代理创建一个任务:

public async Task EvaluateAsyncEach()
{
    var tasks = genomes.Select(x => Task.Run(() => x.Fitness += ComputeFitness(x as NeuralGenome)))
                       .ToArray();
    foreach (var task in tasks)
        await task;
}

对10帧进行以下测量。意思是,t / 10将是一个任务的时间。

正常运行的时间:

00:00:00.3791190
00:00:00.3758430
00:00:00.3697020
00:00:00.3743900
00:00:00.3764850

每个代理每个代理一个任务:

00:00:01.1288240
00:00:01.0761770
00:00:00.9311210
00:00:01.0122570
00:00:00.8938200

以25人为一组:

00:00:00.5401100
00:00:00.5629660
00:00:00.5640470
00:00:00.5932220
00:00:00.6053940
00:00:00.5828170

2 个答案:

答案 0 :(得分:0)

这是一篇很好的文章。

http://fintechexplained.blogspot.com/2018/05/top-ten-tips-for-implementing-multi.html?m=1

您的解决方案是PLINQ。避免创建新任务

答案 1 :(得分:0)

您应该使用Microsoft的Reactive Framework来实现此目的。它非常适合这种处理。

以下是代码:

var query =
    from genome in genomes.ToObservable()
    from fitness in Observable.Start(() => ComputeFitness(genome as NeuralGenome))
    select new { genome, fitness };

IDisposable subscription =
    query.Subscribe(x => x.genome.Fitness += x.fitness);

它完成了所有自己的线程/任务管理。它在计算时也会尽快产生结果。

如果您希望能够await结果,您可以这样做:

var query =
    from genome in genomes.ToObservable()
    from fitness in Observable.Start(() => ComputeFitness(genome as NeuralGenome))
    select new { genome, fitness };

var results = await query.ToArray();

foreach (var x in results)
{
    x.genome.Fitness += x.fitness;
}

Just NuGet&#34; System.Reactive&#34;并在查询中添加using System.Reactive.Linq;

根据评论中的代码,我认为你应该看一下这个:

private async Task ComputingNetworksAsync()
{
    var query =
        from a in agents.ToObservable()
        let i = a.GenerateNetworkInputs()
        from n in Observable.Start(() => a.ComputeNetwork(i))
        select n;

    await query.ToArray();
}

这相当于您的代码(.ToArray()除外)。

但是,您可以更进一步,并执行此操作:

private async Task ComputingNetworksAsync()
{
    var query =
        from a in agents.ToObservable()
        from i in Observable.Start(() => a.GenerateNetworkInputs())
        from n in Observable.Start(() => a.ComputeNetwork(i))
        select n;

    await query.ToArray();
}