Использование задач для повторных действий - PullRequest
0 голосов
/ 09 июня 2018

У меня 50 агентов машинного обучения.Каждый кадр, они получают некоторые входы и вычисляют нейронную сеть.Поскольку каждый агент независим, я бы хотел, чтобы каждый агент вычислял сеть как отдельную задачу.

Если бы мне нужно было создать задачу для каждого агента, каждого кадра, это замедлило бы мою программу.Я пытался сгруппировать своих агентов в 2 задачи (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 голосов
/ 09 июня 2018

Для этого вы должны использовать 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 "System.Reactive "и добавьте 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();
}
0 голосов
/ 09 июня 2018

Это хорошая статья.

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

Ваше решение - PLINQ.Избегайте создания новых задач

...