【问题标题】:"Shear" sort list of jobs for multiple threads efficiently高效地“剪切”多个线程的作业列表
【发布时间】:2018-04-26 09:57:56
【问题描述】:

我有一个List,其中包含几个Mesh-对象。这是这样一个网格类的样子:

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

mesh 对象内的文件List 包含File-Objects,主要由一个带有文件系统路径的字符串和一个二维数组组成,该数组将在文件被解析后保存文件的内容.

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

多线程和解析工作正常。我决定启动与我的 CPU 具有单核一样多的线程。就我而言:4。

在 Linq 的帮助下,我首先将所有文件对象集中在一个自己的列表中:

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

之后,每个线程从该列表中获取 1/4 的对象并开始解析文件。

这是我的问题:相同大小的文件位于同一个网格内(GridWidth* GridHeight = 解析的矩阵单元数)。在这一点上,可能会偶然发生一个线程只获取大尺寸的文件,而另一个线程只获取小尺寸的文件。在这种情况下,一个线程会比其他线程更早完成 - 我不希望这样,因为那样效率低。

所以我想到了首先根据网格的大小对网格列表进行排序,然后将它们的文件按方向添加到 Shear Sort Method(或 Snake Sort ) 到每个线程的新列表。以下算法有效。但我认为它们可能还有一些改进的空间。

这些是我的问题:这个算法是否已经足够高效或者是否存在更好的方法来为每个线程提供文件列表?如果没有更好的方法,我会对“更智能”的编码方式感兴趣(for 循环的所有 if/else 和模运算似乎有点复杂)。

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();
}

【问题讨论】:

    标签: c# multithreading list linq sorting


    【解决方案1】:

    我的建议

    /// <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。

    【讨论】:

    • 谢谢!尽管此代码可以工作,但它不能解决我的子列表问题,这些子列表可能包含与其大小相关的不平衡对象。如果您的函数的输入按降序排序,我认为第一个子列表中对象大小的总和大于第二个或第三个子列表的总和。看看我最后一个清单中的大评论,以了解我的意思。您的代码将输入列表的每四个位置添加到相应的块中。使用剪切排序(或蛇排序),一个块包含来自不同位置的元素。
    • 但如果我通过先降序对列表进行排序,我的怀疑可能无关紧要。那么你的代码可能是一个更好的解决方案。
    • 没错,你可以先通过你喜欢的排序的列表。
    • 谢谢。我有一个改进的想法,现在它似乎是完美的:而不是传递chunkSize 它更适合传递您希望启动的线程数(在本例中为 4)并执行以下操作 [...]@ 987654323@[...]
    • 哦好主意!最后它就是你想要的,线程 :) 干得好
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-13
    • 2011-12-11
    • 1970-01-01
    相关资源
    最近更新 更多