https://gitorious.org/parmap/parmap.git
原始文件
提示修订: 0064fbd0ad69de205ea6ec6999f3d3895e9442c2 作者罗贝多·迪·科斯莫2012年1月10日20:40:45 UTC
添加了OCaml 3.11的Makefile
提示修订: 0064英尺/天
parmap.ml(零件图.ml)
(******************************************************************)(*ParMap:在多核上执行Map计算的简单库*)(*                                                                        *)(*作者:Marco Danalpto,Roberto Di Cosmo*)(*                                                                        *)(*此库是免费软件:您可以重新分发和/或修改*)(*根据GNU Lesser General Public License作为*的条款)(*由自由软件基金会发布,第2版*)(*许可证,或(根据您的选择)任何更高版本。特殊链接*)(*GNU Lesser General Public License的例外情况适用于此*)(*库,有关详细信息,请参阅许可证文件。*)(**************************************************************************)打开ExtLib(*OS相关常数*)(*映射包含甚至巨大结果数据的文件的合理大小*)让huge_size=如果Sys.word_size=64,则1 lsl 32,否则1 lsl 26;;(*序列类型、包含列表和数组*)键入“a sequence=L of”列表|a of”数组;;让debug_enabled=ref false;;let log_dir=ref(Printf.sprintf“/tmp/.parmap.%d”(Unix.getpid()))让调试fmt=打印.kprintf(如果!debug_enabled然后开始(趣味->Format.eprintf“[Parmap]:%s@.”s)end-else忽略)柔性制造技术let信息fmt=Printf.kprintf(趣味->格式.eprintf“[Parmap]:%s@.”s)fmt(*utils*)(*应该是[?a|a<-startv--endv],使用Batteries的列表理解*)让ext_intv启动结束=设s,e=(最小启动电压endv),(最大启动电压endv)in设rec aux acc=函数n->如果n=s,则n::acc else aux(n::cc)(n-1)在辅助[]e中;;(*freopen仿真,来自Xavier对OCaml邮件列表的建议*)让重新打开outchan名称=同花顺;如果不是(Sys.file_exists!log_dir),则为Unix.mkdir!log_dir 0o777;let filename=filename.concat!中的log_dir fname设fd1=Unix.descr_of_out_channel输出let fd2=Unix.openfile文件名[Unix.O_WRONLY;Unix.O_CREAT;Unix.O_TRUNC]0o666 inUnix.dup2 fd2 fd1;Unix.关闭fd2(*从被视为大数组的mmap中解组*)让解组fd=设a=大数组。中的Array1.map_file fd Bigarray.char Bigaray.c_layout true(-1)让res=Byterray.unmarshal a 0 inUnix.close fd;物件(*封送到视为大数组的mmap*)(*系统相关注释:-在Linux内核上,我们可能会分配一个巨大的映射内存区域,并直接将其封送到其中让ba=大数组。Array1.map_file fd Bigarray.char Bigaray.c_layout中的true huge_size忽略(Byterray.marshal_to_buffer ba 0 v[marshal.Closures]);Unix.关闭fd-与其他系统兼容,尤其是Mac OS X,后者坚持分配*all*即使对于稀疏文件,声明的内存区域也必须选择效率较低的方法:*将值v编组为字符串s,并计算其大小*分配一个大小相同的mmap,*将字符串复制到该mmap这将分配两倍的内存,并产生v值的额外副本*)让封送fd v=让s=Marshal.to_string v[Marshal.Closures]in忽略(字节数组.mmap_of_string fd)(*创建影子文件描述符*)让tempfd()=let name=文件名.temp_file“mmap”“TMP”在尝试let fd=Unix.open文件名[Unix.O_RDWR;Unix.O_CREAT]0o600 inUnix.unlink名称;fd公司使用e->Unix.unlink名称;升高e(*一个简单的映射器函数,在一次迭代中计算n个核中每个核上的1/n个数据*)让simplemapper ncores计算opid al集合=(*冲洗所有物品*)flush_all();(*init任务参数*)设ln=阵列长度al in让chunksize=ln/ncores进入(*为mmap创建描述符*)让fdarr=Array.init ncores(fun_->tempfd())进入(*在分叉之前调用GC*)Gc.紧凑型();(*生孩子*)对于i=0到ncores-1 do将Unix.fork()与匹配0 -> 开始让lo=i*块大小设hi=如果i=ncores-1,则ln-1其他(i+1)*块大小-1 in让exchandler e j=(*处理索引j处的异常*)info“(%d,%d)中索引j=%d处出错,chunksize=%d(共%d个),核心%d\n%!上出现异常%s!”j lo hi chunksize(hi-lo+1)(Printexc.to_string e)i;出口1英寸让v=在中计算al-lo-hi-opid exc_handler费达尔元帅。(i) v;退出0结束|-1->info“fork错误:pid%d;i=%d”(Unix.getpid())i;|pid->()完成;(*等待所有孩子*)对于i=0到ncores-1 do尝试忽略(Unix.wait())使用Unix。Unix_error(Unix.ECHILD,_,_)->()完成;(*读取所有数据*)设res=ref[]in(*以相反的顺序迭代,以正确的顺序累加*)对于i=0到ncores-1 dores:=((解组fdarr.((ncores-1)-i)):'d)::!物件;完成;(*收集所有结果*)收集!物件;;(*更复杂的映射器功能,具有自动负载平衡*)(*主机和工作机之间交换的消息类型*)type msg_to_master=int就绪| int出错*string;;type msg_to_worker=已完成|任务int;;让setup_children_chans oc管道向下fdarr i=Setcore.Setcore i;(*将stdout和stderr发送到一个文件,以避免混合不同内核的输出*)repen_out stdout(Printf.sprintf“stdout.%d”i);reopen_out stderr(Printf.sprintf“stderr.%d”i);(*关闭管道的另一端,并将两端转换为ic/oc*)Unix.close(snd-pipedown.(i));让pid=中的Unix.getpid()让ic=Unix.in_channel_of_descr(fst-pipedown.(i))inlet receive()=中的Marshal.from_channel ic让信号v=Marshal.to_channel oc v[];冲洗oc让返回v=let d=中的Unix.gettimeofday()让_=封送fdarr。(i) v英寸调试“工作线程在封送过程中消耗了%f”(Unix.gettimeofday()-。d) 英寸让完成()=(调试“正在关闭(pid=%d)\n%!”pid;尝试close_in ic;close_out oc与_->());退出0 in接收、信号、返回、完成、pid;;(*捕获并行结构的参数映射器基元*)让mapper ncores~chunksize计算opid al集合=设ln=阵列长度al in将chunksize与匹配无->simplemapper ncores计算opid al collect(*无需负载平衡*)|当ncores=ln/v->simplemapper ncores计算opid al collect时的一些v(*不需要负载平衡*)|一些v->(*init任务参数*)让chunksize=v和ntasks=ln/v进入(*冲洗所有物品*)flush_all();(*为mmap创建描述符*)让fdarr=Array.init ncores(fun_->tempfd())进入(*与工人建立沟通渠道*)let pipedown=Array.init ncores(fun_->Unix.pipe())in让pipeup_rd,pipeup-wr=Unix.pipe()进入让oc_up=Unix.out_channel_of_descr pipeup_wr输入(*在分叉之前调用GC*)Gc.紧凑型();(*生孩子*)对于i=0到ncores-1 do将Unix.fork()与匹配0 -> 开始let d=中的Unix.gettimeofday()(*通信原语*)Unix.close pipeup_rd;让receive,signal,return,finish,pid=setup_children_chans oc_up向下管道fdarr i让reschunk=ref-opid输入让computetask n=(*计算块数n*)设lo=n*块大小设hi=如果n=ntasks-1,则ln-1 else(n+1)*chunksize-1 in让exchandler e j=(*处理索引j处的异常*)开始let errmsg=打印exc.to_string ein info“(%d,%d)中索引j=%d处出现错误,chunksize=%d(共%d个),核心%d\n%!”j lo hi chunksize(hi-lo+1)错误消息i;信号(错误(i,errmsg));完成()结束reschunk:=计算al-lo-hi!重新筛选exc_handler;info“核心%d(pid=%d)、数据段(%d,%d)(长度%d,chunksize=%d)上的worker在%f秒内完成”i pid lo hi-ln chunksize(Unix.gettimeofday()-。d)在里面虽然是真的(*要求工作,直到我们结束*)信号(就绪i);将receive()与匹配|已完成->返回(!reschunk:d);完成()|任务n->计算任务n完成;结束|-1->info“fork错误:pid%d;i=%d”(Unix.getpid())i;|pid->()完成;(*关闭未使用的管道端*)Array.iter(fun(rfd,_)->Unix.close rfd)流水线;Unix.close pipeup_wr;(*获取ic/oc/wfdl*)let ocs=Array.init ncores(fun n->Unix.out_channel_of_descr(snd-pipedown.(n)))in让ic=Unix.in_channel_of_descr pipeup_rd in(*给工人喂食,直到所有任务完成*)对于i=0到ntasks-1 do将Marshal.from_channel ic与匹配准备就绪w->(调试“向工作线程%d发送任务%d”i w;设oc=ocs。(w) 英寸(元帅.to_channel oc(任务i)[]);冲洗oc)|错误(核心,消息)->(信息“由于核心%d上的异常而中止:%s”核心消息;退出1)完成;(*向所有子级发送终止令牌*)数组iter(fun oc->Marshal.to_channel oc已完成[];冲洗oc;关闭oc)接触网;(*等待所有子项终止*)对于i=0到ncores-1 do尝试忽略(Unix.wait())使用Unix。Unix_error(Unix.ECHILD,_,_)->()完成;(*读取所有数据*)设res=ref[]in(*以相反的顺序迭代,以正确的顺序累加*)对于i=0到ncores-1 dores:=((解组fdarr.((ncores-1)-i)):'d)::!物件;完成;(*收集所有结果*)收集!物件;;(*并行mapfold函数*)让parmapfold?(ncores=1)?(块大小)(f:'a->'b)(s:'a序列)(op:'b->'c->'c)(opid:'c)=(*强制使用数组以加快对列表元素的访问*)让al=s与中的A al->al|L L->Array.of_list L匹配让计算al-lo hi上一个exchandler=(*以相反的顺序迭代,以正确的顺序累加*)让r=中的ref对于j=0到(hi-lo)do尝试r:=op(f(数组unsafe_get al(hi-j)))!r;带e->exc_handler e j完成!第页在里面mapper ncores ~ chunksize compute opid al(fun r->List.fold_right concat r opid);;(*平行映射函数*)让parmap?(ncores=1)?chunksize(f:'a->'b)(s:'a序列):'b列表=(*强制使用数组以加快对列表元素的访问*)让al=s与中的A al->al|L L->Array.of_list L匹配让计算al-lo hi上一个exchandler=(*以相反的顺序迭代,以正确的顺序累加,并添加到acc*)让f’j=使用e->exc_handler e j尝试f(Array.unsafe_get al(lo+j))设rec-ax-acc=功能0->(f'0)::acc|n->辅助((f'n)::acc)(n-1)辅助上一个(hi-lo)在里面mapper ncores~chunksize compute[]al(fun r->ExtLib.List.concat r);;(*平行折叠功能*)让折叠?(ncores=1)?分块大小(op:'a->'b->'b)(s:'a序列)(opid:'b)=parmapfold~ncores?chunksize(有趣的x->x)s操作opid concat;;(*数组上的并行映射函数*)让map_intv lo-hi f a=设l=hi-lo in如果l<0,则[||]否则开始设r=数组创建(l+1)(f(Array.unsafe_geta-lo))对于i=1到l do数组unsafe_set r i(f(数组unsafe _获取a(lo+i)))完成;第页结束让array_parmap?(ncores=1)?chunksize(f:'a->'b)(al:'a数组):'b数组=让我们计算一个lo-hi-previous exchandler=尝试Array.concat[(map_intv lo hi f a);上一个]使用e->exchandler e lo在里面mapper ncores~chunksize compute[||]al(fun r->Array.concat r);;(*此代码针对浮点数组的操作进行了高度优化:-提前知道结果的大小可以将其作为Bigarray预先分配到共享内存空间中;-使用不安全的函数,我们将OCaml编译器欺骗为使用Bigarray内存作为数组,如下所示数组unsafe_get(对象逻辑arr_out)1这是因为OCaml编译对浮点数组的访问作为未装箱数据,无需进一步的完整性检查;-最终复制到真正的OCaml数组是通过C中的memcpy完成的。这种方法的性能提高了2到3倍w.r.t.array_parmap,使用Obj.magic和关于数组和bigarrays的内部表示的知识。*)错误数组大小异常类型buf=(float,Bigarray.float64_elt,Bigarary.c_layout)Bigarray。数组1.t*int;;(*某天应该是长整数*)设init_shared_buffer a=let size=阵列长度a英寸设fd=tempfd()let arr=大数组。Array1.map_file fd Bigarray.float64 Bigarry.c_layout实际大小(*mmap()函数应为关联的文件添加一个额外的引用文件描述符fildes不会被后续的close()删除在该文件描述符上。http://pubs.opengroup.org/onlinepubs/009695399/functions/mmap.html*)Unix.close fd;(arr,大小);;让array_float_parmap?(ncores=1)?块状大小?结果如何?sharedbuffer(f:'a->float)(al:'a数组):float数组=let size=数组长度al in让barr_out=将sharedbuffer与匹配一些(arr,s)->如果s<size,则(信息“共享缓冲区太小,无法容纳array_float_parmap中的输入”;引发错误的ArraySize)其他arr|无->fst(init_shared_buffer al)在里面(*诱使编译器以浮点数组的形式访问Bigarray内存区域:Bigarray中的数据放置在偏移量1 w.r.t.处,这是一个普通数组,因此我们在arroutasarray中获取指向该区域的指针,并将其类型化为float数组*)let barr_out_as_array=数组unsafe_get(Obj.magic barr_out)1 in让计算lohiexchandler=尝试因为i=lo到hi-do数组不安全设置barr_out_as_as_Array i(f(数组不安全获取al i))完成使用e->exchandler e lo在里面mapper ncores ~ chunksize compute()al(fun r->());设res=将结果与匹配无->字节数组.to_floatarray barr_out大小|一些a->如果Array.length a<size,则(信息“结果数组太小,无法将结果保存在array_float_parmap中”;引发WrongArraySize)其他的Byterray.to_this_floatarray barr_out大小单位:res;;
返回顶部