C# 并行排序算法

C# 并行排序算法,c#,.net,sorting,parallel-processing,parallel-extensions,C#,.net,Sorting,Parallel Processing,Parallel Extensions,我正在寻找一个C#中并行(多线程)排序算法的简单实现,该算法可以在列表或数组上运行,并可能使用并行扩展,但这部分并不是严格必需的 编辑:Frank Krueger提供了一个很好的答案,但我希望将该示例转换为不使用LINQ的示例。还要注意,Parallel.Do()似乎已被Parallel.Invoke()取代 谢谢。在他的文章《黑暗面》中,我们提供了快速排序的并行扩展版本: (编辑:由于该链接现已失效,感兴趣的读者可在以下网址找到该链接的存档) private void QuicksortSeq

我正在寻找一个C#中并行(多线程)排序算法的简单实现,该算法可以在
列表
或数组上运行,并可能使用并行扩展,但这部分并不是严格必需的

编辑:Frank Krueger提供了一个很好的答案,但我希望将该示例转换为不使用LINQ的示例。还要注意,
Parallel.Do()
似乎已被
Parallel.Invoke()
取代

谢谢。

在他的文章《黑暗面》中,我们提供了快速排序的并行扩展版本:

(编辑:由于该链接现已失效,感兴趣的读者可在以下网址找到该链接的存档)

private void QuicksortSequential(T[]arr,int left,int right)
其中T:i可比较
{
如果(右>左)
{
int pivot=分区(arr、左、右);
QuicksortSequential(arr,左,枢轴-1);
QuicksortSequential(arr,轴+1,右);
}
}
私有void QuickSortParalleloped(T[]arr,int left,int right)
其中T:i可比较
{
const int SEQUENTIAL_THRESHOLD=2048;
如果(右>左)
{
if(右-左<顺序_阈值)
{
快速排序顺序(arr、左、右);
}
其他的
{
int pivot=分区(arr、左、右);
平行,做吧(
()=>QuickPort并行优化(arr,左侧,枢轴-1),
()=>QuickSortParalleloped(arr,pivot+1,右));
}
}
}

请注意,一旦项目数少于2048,他将恢复为顺序排序。

根据处理器的数量,将需要排序的列表划分为大小相等的子列表,然后每当完成两个部分时,将它们合并到一个新部分,直到只剩下一个,所有线程都已完成。非常简单,您应该能够自己实现它,并且可以使用任何现有的排序算法(如库中的排序算法)对每个线程中的列表进行排序。

这里是一个没有lamda表达式的版本,它将在C#2和.Net 2+并行扩展中编译。这也应该适用于Mono,它自己实现了并行扩展(来自2008年谷歌代码夏):

//
///并行快速排序算法。
/// 
公共类并行排序
{
#区域公共静态方法
/// 
///顺序快速排序。
/// 
/// 
/// 
公共静态void QuicksortSequential(T[]arr),其中T:i可比较
{
快速排序顺序(arr,0,arr.Length-1);
}
/// 
///并行快速排序
/// 
/// 
/// 
公共静态void QuicksortParallel(T[]arr),其中T:i可比较
{
QuickPortParallel(arr,0,arr.Length-1);
}
#端区
#区域私有静态方法
私有静态void QuicksortSequential(T[]arr,int left,int right)
其中T:i可比较
{
如果(右>左)
{
int pivot=分区(arr、左、右);
QuicksortSequential(arr,左,枢轴-1);
QuicksortSequential(arr,轴+1,右);
}
}
私有静态void QuicksortParallel(T[]arr,int left,int right)
其中T:i可比较
{
const int SEQUENTIAL_THRESHOLD=2048;
如果(右>左)
{
if(右-左<顺序_阈值)
{
快速排序顺序(arr、左、右);
}
其他的
{
int pivot=分区(arr、左、右);
调用(新操作[]{delegate{QuicksortParallel(arr,左,pivot-1);},
委托{QuicksortParallel(arr,pivot+1,右);}
});
}
}
}
专用静态无效交换(T[]arr,int i,int j)
{
T tmp=arr[i];
arr[i]=arr[j];
arr[j]=tmp;
}
私有静态int分区(T[]arr,int低,int高)
其中T:i可比较
{
//简单分区实现
int-pivotPos=(高+低)/2;
T pivot=arr[pivotPos];
交换(arr、低位、pivotPos);
int左=低;

对于(inti=low+1;i更新我现在在双核机器上实现了比1.7x更好的加速

我想我会尝试编写一个在.NET2.0中工作的并行分类器(我想,请检查一下),它除了
线程池
之外不使用任何东西

以下是对2000000个元素数组进行排序的结果:

Time Parallel Time Sequential ------------- --------------- 2854 ms 5052 ms 2846 ms 4947 ms 2794 ms 4940 ms ... ... 2815 ms 4894 ms 2981 ms 4991 ms 2832 ms 5053 ms Avg: 2818 ms Avg: 4969 ms Std: 66 ms Std: 65 ms Spd: 1.76x 致:

(实际上,在代码中,我做了一些负载平衡,这似乎很有帮助。)

我发现,只有当一个数组中有25000个以上的项目时,运行这个并行版本才有回报(不过,至少50000个项目似乎让我的处理器呼吸更多)

我已经在我的小双核机器上做了很多改进。我很想在8路monster上尝试一些想法。此外,这项工作是在一台运行Mono的13英寸小MacBook上完成的。我很好奇其他人在正常的.NET 2.0安装上的表现如何


这里提供了所有丑陋荣耀的源代码:。如果有兴趣,我可以清理它。

一种基于处理器缓存大小的合并排序,在处理器之间划分块

合并阶段可以通过将合并拆分为n位来完成,每个处理器开始将正确偏移量的块合并到块中

我没有试过写这个


(由于CPU缓存和主ram的相对速度与发现合并排序时ram和磁带的相对速度相差不远,也许我们应该再次开始使用合并排序)

谢谢Richard-我想我必须回答一个VB问题或其他问题才能得到10000:-)用别人的代码打破障碍感觉也很蹩脚,但我接受了。我不确定
/// <summary>
/// Parallel quicksort algorithm.
/// </summary>
public class ParallelSort
{
    #region Public Static Methods

    /// <summary>
    /// Sequential quicksort.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="arr"></param>
    public static void QuicksortSequential<T>(T [] arr) where T : IComparable<T>
    {
        QuicksortSequential(arr, 0, arr.Length - 1);
    }

    /// <summary>
    /// Parallel quicksort
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="arr"></param>
    public static void QuicksortParallel<T>(T[] arr) where T : IComparable<T>
    {
        QuicksortParallel(arr, 0, arr.Length - 1);
    }

    #endregion

    #region Private Static Methods

    private static void QuicksortSequential<T>(T[] arr, int left, int right) 
        where T : IComparable<T>
    {
        if (right > left)
        {
            int pivot = Partition(arr, left, right);
            QuicksortSequential(arr, left, pivot - 1);
            QuicksortSequential(arr, pivot + 1, right);
        }
    }

    private static void QuicksortParallel<T>(T[] arr, int left, int right) 
        where T : IComparable<T>
    {
        const int SEQUENTIAL_THRESHOLD = 2048;
        if (right > left)
        {
            if (right - left < SEQUENTIAL_THRESHOLD)
            {
                QuicksortSequential(arr, left, right);
            }
            else
            {
                int pivot = Partition(arr, left, right);
                Parallel.Invoke(new Action[] { delegate {QuicksortParallel(arr, left, pivot - 1);},
                                               delegate {QuicksortParallel(arr, pivot + 1, right);}
                });
            }
        }
    }

    private static void Swap<T>(T[] arr, int i, int j)
    {
        T tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }

    private static int Partition<T>(T[] arr, int low, int high) 
        where T : IComparable<T>
    {
        // Simple partitioning implementation
        int pivotPos = (high + low) / 2;
        T pivot = arr[pivotPos];
        Swap(arr, low, pivotPos);

        int left = low;
        for (int i = low + 1; i <= high; i++)
        {
            if (arr[i].CompareTo(pivot) < 0)
            {
                left++;
                Swap(arr, i, left);
            }
        }

        Swap(arr, low, left);
        return left;
    }

    #endregion
}
Time Parallel Time Sequential ------------- --------------- 2854 ms 5052 ms 2846 ms 4947 ms 2794 ms 4940 ms ... ... 2815 ms 4894 ms 2981 ms 4991 ms 2832 ms 5053 ms Avg: 2818 ms Avg: 4969 ms Std: 66 ms Std: 65 ms Spd: 1.76x
QuickSortSequential:
    QuickSortSequential (beg, l - 1);
    QuickSortSequential (l + 1, end);
QuickSortParallel:
    ManualResetEvent fin2 = new ManualResetEvent (false);
    ThreadPool.QueueUserWorkItem (delegate {
        QuickSortParallel (l + 1, end);
        fin2.Set ();
    });
    QuickSortParallel (beg, l - 1);
    fin2.WaitOne (1000000);
    fin2.Close ();