蛙蛙推荐:F#实现并行排序算法

摘要:F#是微软推出的一套函数式编程语言,能在CLR中运行,且和.NET其它语言能很好的交互,又因为它对并发编程的特殊支持,比如不变对象,异步表达式,新的并行基元等,所以很值得入门学习一下。现在我们综合应用这些技术写一个并行排序算法,并对其进行性能测试。

思路:并行算法中其中有一种比较常见的方法就是先把要处理的数据分成若干份,然后让不同的线程(CPU)去处理,然后所有的线程处理完成后,把结果汇聚在一起,在一个独立的线程里完成结果合并,从而形成最终结果。在做分割的时候尽量让每个线程只访问自己独立的数据,而不访问全局数据和其它线程的数据(这里说的数据是非只读数据),在合并结果的时候要有一种高效的算法来合并。

排序算法里有归并排序算法,我们先写一个多路归并排序算法,然后把要排序的数组分成CPU的个数份,让每个CPU去对每一份进行排序,所有线程排序完成后汇聚在一起,在一个独立的线程里进行归并排序。

大概再解释一下代码,可能有些人对f#还不熟悉。
1、归并算法的思路就是把多个已经排序的数组合并成一个大的排序数组,先从每个分数组的最小下标开始,谁都最小就放到大数组里,然后这个数组的下标加一,然后再比较,再把最小的放到大数组里,重复,直到所有的小数组的下标已经指向到末尾。其中会用到一个临时变量min,所以用mutable关键字修饰。
2、F#的数组的长度用Array.length方法得出,变量和数组的赋值符号是<-,而不是=,=相当于c#里的==,f#里没有continue和break等关键字
3、async关键字是一个新的并行基元,用它扩住的代码由f#自动的异步在线程池里执行,如果里面要返回结果的话,要用let!和return!关键字,我们的排序只是对数组进行操作,并不返回,所以这里比较简单。
4、(fun a b -> a - b)是一个lamda表达式,它可以自动转换成Comparer<T>,起到排序依据的作用
5、Array.map表示把一个数组里的每个元素应用一个方法,它这时候不执行,会通过管道传递给Async.Parallel方法,Async.Parallel方法返回一个异步执行数组Async<'a array>,最后用Async.Run来真正执行Async.Parallel返回的结果。
6、|>表示管道的意思,大致就是把前一个函数的结果让后一个函数来用,这样一条语句可以表达很连贯的逻辑。

整体代码如下:

 1  # light
 2 
 3  open  System
 4  open  System.Diagnostics
 5  open  Microsoft.FSharp.Control.CommonExtensions
 6 
 7  let  merge_sort destArray source cmp =
 8      let  N = Array.length source
 9      let  L = Array.length destArray -  1
10      let  posArr = Array.create N  0
11      for  i =  0   to  L  do
12         let   mutable  min = - 1
13         for  j =  0   to  N -  1   do
14            if  posArr.[j] >= Array.length source.[j]  then  ()
15            else
16               if  min = - 1   then  min  <-  j
17               else
18                  if  (cmp source.[j].[posArr.[j]] source.[min].[posArr.[min]]) <  0   then  min  <-  j
19         if  min = - 1   then  ()
20            else
21              destArray.[i]  <-  source.[min].[posArr.[min]]                             
22              posArr.[min]  <-  posArr.[min] +  1
23 
24  let  parallel_sort cmp arr =
25       let  processorCount = Environment.ProcessorCount;
26       let  partArray = Array.create processorCount [ || ]
27       let   mutable  remain = Array.length arr
28       let  partLen = Array.length arr / processorCount
29 
30       for  i =  0   to  processorCount -  1   do
31           if  i = processorCount -  1   then
32               let  temp_arr = Array.create remain  0
33              Array.Copy(arr, i*partLen, temp_arr,  0 , remain)
34              partArray.[i]  <-  temp_arr
35           else
36               let  temp_arr = Array.create partLen  0
37              Array.Copy(arr, i*partLen, temp_arr,  0 , partLen)
38              remain  <-  remain - partLen
39              partArray.[i]  <-  temp_arr
40 
41       let  a_sort_one arr =
42          async {
43              Array.sort cmp arr
44          }
45          
46       let  a_sort_all =
47          partArray
48           | > Array.map ( fun  f  ->  a_sort_one f)
49           | > Async.Parallel
50           | > Async.Run
51          
52      a_sort_all
53       let  ret = Array.create (Array.length arr)  0
54      merge_sort ret partArray ( fun  a b  ->  a - b)
55      ret
56 
57  let  arr = Array.create  1000000   0
58  let  rnd =  new  Random()
59  for  i =  0   to  Array.length arr -  1   do
60      arr.[i]  <-  rnd.Next()
61 
62  let  stop = Stopwatch.StartNew()
63  stop.Start
64  let  sorted_arr = parallel_sort ( fun  a b  ->  a-b) arr
65  stop.Stop
66  printfn  " 并行排序结果/r/n=%A/r/n用时%d毫秒 "  sorted_arr stop.ElapsedMilliseconds          
67 
68  let  stop2 = Stopwatch.StartNew()
69  Array.sort ( fun  a b  ->  a-b) arr
70  stop.Stop
71  printfn  " 串行排序结果/r/n=%A/r/n用时%d毫秒 "  arr stop2.ElapsedMilliseconds   
72 
73  Console.ReadKey( true )   


我本机,IBM X200测试串行排序大约在1200多秒,并行排序在900秒左右。

相关链接:
从简单的 F# 表达式构建并发应用程序
http://msdn.microsoft.com/zh-cn/magazine/cc967279.aspx
从C# 3.0到F#
http://www.cnblogs.com/allenlooplee/archive/2008/07/25/1251631.html
F#系列随笔索引
http://www.cnblogs.com/anderslly/archive/2008/10/08/fs-posts-indices.html
Concurrency with MPI in .NET
http://weblogs.asp.net/podwysocki/archive/2008/05/15/concurrency-with-mpi-in-net.aspx
使用 .NET Framework 中的函数式编程技术
http://msdn.microsoft.com/zh-cn/magazine/cc164244.aspx
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值