"Сдвиг" сортировать список заданий для нескольких потоков эффективно - PullRequest
0 голосов
/ 26 апреля 2018

У меня есть List, который содержит несколько Mesh -объектов.Вот как выглядит такой класс сетки:

public class Mesh
{
    public int GridWidth { get; }
    public int GridHeight { get; }
    public List<File> Files { get; }
    /* ... */
}

List файлов внутри объекта сетки содержит File -объектов, которые в основном состоят из строки с указанием пути файловой системы к файлу.и двумерный массив, который будет содержать содержимое файла после его анализа.

public class File
{
    public string Path { get; }
    public double[][] Matrix { get; set; }
    /* ... */
}

Многопоточность и синтаксический анализ работают нормально.Я решил запустить столько потоков, сколько у моего процессора одноядерных.В моем случае: 4.

С помощью Linq я сначала концентрирую все файловые объекты в своем собственном Списке:

List<File> allFiles = meshes.SelectMany(mesh => mesh.Files).ToList();

После этого каждый Поток получает 1/4 Объектов.из этого списка и начинает анализ файлов.

И это моя проблема : файлы одинакового размера находятся внутри одной сетки (GridWidth* GridHeight = количество проанализированныхматрица-клетки).В этот момент может случиться так, что один поток получит только файлы большого размера, тогда как другой поток получит только файлы небольшого размера.В этом случае один поток закончил бы раньше, чем другой поток (и) - и я не хочу этого, потому что это было бы неэффективно.

Так что у меня была идея отсортировать список сеток по их размерусначала и после этого добавьте свои файлы в ориентации к методу сортировки сдвига (или сортировка по змеи ) в новый список для каждого потока.Следующий алгоритм работает.Но я думаю, что они могли бы стать чем-то вроде улучшения.

И это мои вопросы : Этот алгоритм уже достаточно эффективен или существует лучший способ предоставления списков файлов для каждого потока?Если нет лучшего способа, я бы заинтересовался более «умным» способом кодирования (цикл for кажется немного сложным со всеми его операциями if / else и modulo).

int cores = 4;
List<File>[] filesOfThreads = new List<Slice>[cores];

List<File> allFilesDesc = meshes.OrderByDescending(mesh => mesh.GridWidth * mesh.GridHeight).SelectMany(mesh => mesh.Files).ToList();

int threadIndex = 0;
/*
 * Inside this for-loop the threadIndex changes
 * with each cycle in this way (in case of 4 cores):
 * 0->1->2->3->3->2->1->0->0->1->2->3->3->2 ...
 * With each cycle a file of the current position of 
 * allFilesDesc[i] is added to the list of
 * filesOfThreads[threadIndex]. In this "shear" sort
 * way every thread should get approximately the same
 * number of big and small files.
 */
for (int i = 0; i < allFilesDesc.Count; i++)
{
    if (i < cores)
    {
        filesOfThreads[threadIndex] = new List<File>();
    }
    filesOfThreads[threadIndex].Add(allFilesDesc[i]);
    if (i < cores - 1)
    {
        threadIndex++;
    }
    else if ((i + 1) % cores != 0)
    {
        threadIndex += ((i + 1) / cores) % 2 == 0 ? 1 : -1;
    }
}

foreach (var files in filesOfThreads)
{
    Thread thread = new Thread(() => ComputeFiles(files));
    thread.Start();
}

1 Ответ

0 голосов
/ 26 апреля 2018

Мое предложение

/// <summary>
/// Helper methods for the lists.
/// </summary>
public static class ListExtensions
{
public static List<List<T>> ChunkBy<T>(this List<T> source, int chunkSize) 
{
    return source
        .Select((x, i) => new { Index = i, Value = x })
        .GroupBy(x => x.Index / chunkSize)
        .Select(x => x.Select(v => v.Value).ToList())
        .ToList();
}
}

Например, если вы выберете список из 18 элементов по 5 элементов на блок, вы получите список из 4 подсписков со следующими элементами: 5-5-5-3.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...