MPI并行编程系列二快速排序

MPI 并行编程系列二快速排序阅读:63 评论:0作者:飞得更高发表于2010-04-06 09 :00 原文链接在上一篇中对枚举排序的MPI并行算法进行了详细的描述和实现,算法相对简单,采用了并行编程模式中的单程序多数据流的并行编程模式。

在本篇中,将对快速排序进行并行化分析和实现。

本篇代码用到了上篇中的几个公用方法,在本篇中将不再做说明。

在本篇中,我们首先对快速排序算法进行描述和实现,并在此基础上分析此算法的并行性,确定并行编程模式,最后给出该算法的MPI实现。

一、快速排序算法说明快速排序时一种最基本的排序算法,效率相对较高。

其基本思想是:在当前无序数组R[1,n] 中选取一个记录作为比较的"基准" ,即作为排序中的"轴" 。

经过一趟排序后,当前无序数组R[1,n] 就会以这个轴为核心划分为两个无序的子区r1[1,i-1],r2[i,n] 。

其中左边的无序子区都会比"轴"小,右边的无序子区都会比" 轴" 大。

这样下一趟排序,我们就可以对这两个子区用同样的方法进行划分排序,知道所有的无序子区中的记录均排好为止。

根据算法的说明,快速排序时一个典型的递归算法,算法描述如下:无序数组R[1],R[2],.,R[n] quick_sort(R,start,end)if(start end)r=partion(R,start,end)quick_sort(R,start,r-1)quick_sort(R,r+1,end)endif end quick_sort 方法partion 的作用就是选取" 轴" ,并将数组分为两个无序子区,并将该" 轴" 的最终位置返回,在这里我们选择数组的第一个元素为"轴" ,其算法描述为:partion(R,start,end)r=R[start]while(start end)while((R[end]=r)&&(start end))end-end ehile R[start]=R[end]while((R[start]r)&&(start end))start++end wile R[end]=R[start]end while R[start]=r return start end partion 该排序算法的性能好坏主要取决于" 轴" 的选定,即无序数组的划分是否均衡。

最好的情况下,无序数组每次都会被划为两个均等的无序子区,这是算法的负责度为o(nlogn) ;最坏的情况,无序数组每次划分都是左边n-1 个元素,右边0 个元素,这时算法的复杂度为o(n A2)。

在通常的情况下,该算法的复杂度会依然保持在o(nlogn) ,上只不过具有更高的常数因子。

因此,选定一个有效地"轴",成为该算法的关键。

一般情况下,会选定无序数组的第一个,中间或者是最后一个元素作为算法的"轴",我们可以对着三个元素进行比较,取大小居中的那个元素作为该算法的" 轴" 。

、快速排序算法的串行实现确定在什么条件下终止递归操作。

主函数代码如下:1:void quick_sort_function(int*array,int start,int last){2快速排序很明显的是一个递归的程序。

编写递归程序一个很重的要点就是:3:int part_position ;4:5:if(start=last)6 :return ;7:8:part_position=part_array_head(array,start,last) ;9:quick_sort_function(array,start,part_position-1) ;10:quick_sort_function(array,part_position+1,last) ;11:}主函数代码很简单,一个终止递归的条件,一个递归方式。

在主函数中,核心函数为part_array_head, 其代码如下:1:int part_array_head(int*array,int start,int last){2 :3:int position_value=array[start] ;4:5:while(start last){6 :while(startlast&&array[last]=position_value)7 :last-- ;8:array[start]=array[last] ;9:10:while(startlast&&array[start]=position_value)11 :start++ ;12:array[last]=array[start] ;13:}14 :15:array[start]=position_value16:17:return start ;18:}从代码可以看出,快速排序的代码相对姜丹,总共不过三十行代码。

本人是非常喜欢递归操作的。

下面我们将对快速排序进行并行化分析。

三、快速排序并行化分析在并行编程策略中,有一种策略非常适合递归算法,自然也就成为我们快速排序并行化的首选策略。

这种策略为" 分治策略" ,这种策略的核心思想就是将一个大而复杂的问题分解成若干个子问题分而治之。

若分解后的子问题依然过大或者是过于复杂,则可反复利用分治策略,直到很容易的求解子问题未知。

有此看出,分治策略也符合递归的思想。

要实现分治策略,主要分为三步:1、将大问题分解成小问题;2、求解小问题;3、归并小问题的解,得到最终结果。

其中各个小问题的求解就是我们并行化的所在。

分治策略的说明图如下:在快速排序算法中,我们就是将原无序数组按照一定得规则拆分成一个个子数组,即上图中的" 分解"过程,每个子数组的排序可并行执行。

当各子数组排序完毕后,依此将结构传给其父数组,得到最终的结果,即上图中的" 归并" 过程。

基于以上的分析,我们给出快速排序算法MPI 的实现如下:四、快速排序的MPI并行实现因为分治策略的特殊性,我们进行快速排序算法的进程数目为2^m个。

我们依然用进程p0来读取原数组,最终的排序结果也会由进程p0打印出来。

在该算法中,我们如果知道进程数目为2A m个,就应该能够求出m因此我写了一个实现函数,求一个整数的以2为底的对数的算法。

该算法的具体实现为:1:int log_int(int root,int num){2 :3:int i,j ;4:5:i=1 ;6:j=root ;7:8:while(j num){9 :j*=root ;10:i++ ;11:}12 :13:if(j num)14:i-- ;15:16:return i ;17:} 该并行算法的主函数为:1:void quick_sort_mpi(int*argv,char*argc){2 :3:intprocess_id ;4:int process_size ;5:6:int*init_array ;7:intarray_length ;8:9:int log_num ;10:int k ;11:12:mpi_start(argv,argc,&process_size,&process_id,MPI_COMM_WORLD) ;13:14:if(process_size%2 !=0){15 :if( !process_id)16 :printf("the size of the process must is the multipe of 2") ;17:18:MPI_Abort(MPI_COMM_WORLD,PROCESS_SIZE_E;RR19O:R})20 :21:log_num=log_int(2,process_size) ;22:array_length=ARRAY_LENGT;H 23:24:if( !process_id){25 :init_array=(int*)my_mpi_malloc(0,sizeof(int)*array_length) ;26:array_builder(init_array,array_length) ;27:array_int_print(array_length,init_array) ;28:}29 :30:// 对数组进行快速排序31:quick_sort_mpi_function(init_array,array_length,log_num,process_id,0 ,MPI_COM M_WOR;LD3)2:33:if( !process_id)34 :array_int_print(array_length,init_array) ;35:36:MPI_Finalize() ;37:} 在程序中出现的子函数在这里就不一一介绍了,其中主要的函数在上一篇枚举排序的MPI算法中都有所介绍。

主函数的核心就是quic_sort_mpi_fuction 函数,该函数真正实现了快速排序,其代码如下:1:void quick_sort_mpi_function(2 :int*init_array,// 待排序数组3:int array_length,// 待排序数组长度4:int log_num,// 进程数取2 为底的对数5:int process_id,// 当前活动进程ID 6 :int part_process_id,// 对数组进行拆分的进程ID 7 :M P I_Comm comm):{8 9:int*local_array ;10:int local_array_length=0 ;11:12:int send_array_length ;13:14:int partion_position ;15:16:// 取得要接收数据的进程号的进程号17:int receive_process_id ;18:int j ;19:20:M P I _Status status ;21:22:if(log_num==0){23 :if(process_id==part_process_id&&array_length 1)24 :quick_sort_function(init_array,0,array_length-1) ;25:26:return ;27:}28 :29:receive_process_id=part_process_id+pow_int(2,log_num-1) ;30:31://当活动进程为数组拆分进程时,按照快速排序方法对数组分成两部分32:;35: if(process_id==part_process_id){33partion_position=part_array_head(init_array,0,array_length-1)send_array_length=array_length-partion_position-1local_array_length=partion_position MPI_Send((void*)&send_array_length,1,MPI_INT,38 :receive_process_id,LENGTH_MESSAGE,comm ;) 39: 40: if(send_array_length 0)41 :MPI_Send((void*)(init_array+partion_position+1),send_array_length,42: MPI_INT,receive_process_id,DATA_MESSAGE,comm ;) 43:}44 : 45:// 当活 动进程为待接收数据的进程时,接收从拆分进程发送过来的数据46: if(process_id==receive_process_id){47 : 48:MPI_Recv((void*)&local_array_length,1,MPI_INT,part_process_id,49: LENGTH_MESSAGE,comm,&stat ;us5) 0: 51: if(local_array_length 0)52 : {53: local_array=(int*)my_mpi_malloc(process_id,sizeof(int)*local_array_l ength) ; 54:MPI_Recv((void*)local_array,local_array_length,MPI_INT,part_process_ id,55 :DATA_MESSAGE,comm,&statu ;s)56: 57: }58 :}59 :60:j=local_array_length ;61:MPI_Bcast(&j,1,MPI_INT,part_process_id,comm) ; 62:if(j 1){64 :quick_sort_mpi_function(init_array,local_array_length,log_num-1,65 :process_id,part_process_id,comm) ; 66: }67 :68:j=local_array_length ;69:MPI_Bcast(&j,1,MPI_INT,receive_process_id,MPI_COMM_WORLD) ;70:if(j ;34:; 36:37:1)71:;35:quick_sort_mpi_function(local_array,local_array_length,log_num-1,72 :process_id,receive_process_id,comm) ;73:74:if(process_id==receive_process_id&&local_array_length 0)75 :MPI_Send((void*)local_array,local_array_length,MPI_INT,76 :part_process_id,DATA_MESSAGE_SORT,MPI_COMM_WO;R7L7D:) 78:if(process_id==part_process_id&&send_array_length 0)79 :MPI_Recv((void*)(init_array+partion_position+1),send_array_length,80MPI_INT,receive_process_id,DATA_MESSAGE_SORT,MPI_COMM_WORLD,&status) ;81:}该算法基本继承了并行编程策略的分治所发思想:分解--- 求解--- 归并。

合集下载

深入解析快速排序(QuickSort)

深入解析快速排序(QuickSort)

深入解析快速排序(QuickSort)本文将对快速排序进行深入的分析和介绍。

通过学习本文,您将•秒杀快速排序面试•掌握高效实现快排•加深范型编程意识八卦花絮快速排序是由图灵奖获得者、计算机语言设计大佬C. A. R. Hoare 在他26岁时提出的。

说起C. A. R. Hoare老爷爷,可能很多人的第一印象就是快速排序,但是快排仅仅是他人生中非常小的成就而已。

例如,他在1978年提出的Communicating Sequential Processes(CSP)理论,则深深的影响了并行程序设计,Go语言中的Goroutine就是这种典范。

基本思想快速排序的思想非常简单:对于一个数组S,我们选择一个元素,称为pivot。

将数组S中小于等于pivot的元素放在S的左边,大于等于pivot的元素放在S的右边。

左右两部分分别记为S1和S2,然后我们递归的按上述方式对S1、S2进行排序。

具体说来,我们维护两个指针,采用两边扫描。

从左到右扫描,当遇到一个元素大于等于pivot时,暂停。

从右到左扫描,当遇到一个小于等于pivot元素时,暂停。

然后交换这两个元素。

继续扫描,直到两个指针相遇或者交叉。

从直观上看,每次递归处理的两个子数组S1、S2的大小最好是相等或者接近的,这样所花费的时间最少。

实现细节说起来容易,做起来难了。

要想正确实现快速排序非常不容易,很容易犯错。

简单的修改就可能导致程序死循环或者结果错误。

如果你一度感到很难在几分钟内实现一个正确的快速排序,说明你是正常人。

那些五分钟内就能把快速排序写对的,几乎都是背代码。

我在实现以下代码时,就反复调试了十几分钟。

而且,我会告诉你曾经JDK的某个版本实现中都存在bug么?在给出完整代码之前,我们来考虑几个非常重要的问题。

如何选择pivot?至少有几种显而易见的方法:尝试1:选择数组中的第一个元素。

成本低,但是当输入数组已经有序时,将导致O($n^2$)的复杂度。

基于MIPS指令集的汇编程序(spim仿真)——快速排序和二分搜索

基于MIPS指令集的汇编程序(spim仿真)——快速排序和二分搜索

Report for MIPS Assembler ProgramsQuick Sort and Binary SearchHAO Cong1. IntroductionIn this program, I implemented two major functions: quick sort and binary search. First, a list of integers is given by user, and the integers are sorted in ascending order using quick sort method. Then the program search for the particular integer given by user, using binary search, and provides its position. Figure 1 shows the basic structure of the program.Figure 1: Basic Structure of Program2. Implementation2.1 Quick Sort2.1.1 Brief Introduction to Quick Sort①Quicksort sorts by employing a divide and conquer strategy to divide a list into two sub-lists.The steps are:1. Pick an element, called a pivot, from the list.2. Reorder the list so that all elements with values less than the pivot comebefore the pivot, while all elements with values greater than the pivot comeafter it (equal values can go either way). After this partitioning, the pivot is inits final position. This is called the partition operation.3. Recursively sort the sub-list of lesser elements and the sub-list of greaterelements.The base case of the recursion is list of size zero or one, which never need to be sorted.2.1.2 Pseudo code2.1.3 Implementation of Quick SortEven having written C code, it’s not easy to change into assembler code. The most difficult part is applying the recursive method. We have to take care of building Stack Frame and storing registers. Now I’d like to take several parts of the code to make detailedexplanation.Firstly,we claim 1024bytes in memory, in order to store the integers to be sorted. Use .data to put them in data segment. From now on, all operations on integers are based on memory.Here, we begin to call the function of quick sort. In MIPS, when calling other functions, parameters are stored in $a0 - $a3. So we put the begin index and end index in $a0 and $a1, then the parameters are transferred to sub-functions.In order to jump, we used instruction jal. Using this, the return address can be stored in $ra atomatically. So when caller ends, PC pointer can return to the next instruction of callee after jal to continue.This is the end condition of recursive. If the left list has only one element, the program should return and end recursive. In this case we don’t need to build a stack frame for callee. It can save execution time.At the beginning of a callee, it must:a) Create a stack frame, by subtracting the frame size from the stack pointer ($sp). In MIPS, the minimum stack frame size is 32 bytes, so even if the program doesn't need all of this space, stack frames still should be made this large.b) Save any callee-saved registers ($s0 - $s7, $fp, $ra) which are used by the callee. In this program, only $fp and $ra need to be saved.c) Set the frame pointer to the stack pointer, plus the frame size.Here we calculate the pivot value: the average of first and last element.Given middle value, we begin to compare from both beginning and ending of the list. From the beginning, we try to find the first integer that is lager than pivot value; from the end, we try to find the first integer that is smaller than pivot value. If the two pointers haven’t touch, do swap. Otherwise, do partition.The above figure shows the branch flow. In order to make it clear, I hided detailed code.Besides, I used instruction b/bgt/ble/blt/bge here, instead of jump instructions. Their functions are similar, but there’re also some differences between them. One is jump instructions will automatically store the return address in $ra, but branch instructions don’t. Here, we only need to implement the function of if-else, so branch instruction is OK.Here is recursive call. Since the function call will change the values in registers, the values must be pushed in stack before calling sub-functions and be popped after calling. So the sw instruction is used to preserve and lw is used to restore.In MIPS, there’s a common custom, that is, $t0-$t7 is called Temporary Register, and $s0-s7 is called Saved Register. In general, the caller should take charge of keep the value of $t0-$t7, if necessary, and the callee has the responsibility to save $s0-$s7. In other words, $t0-$t7 is “temporary” to caller, because callee may modifies it, so caller has to preserve them; but $s0-$s7 is “saved” to caller, because callee will save them and restore them before return, so caller doesn’t need to care. Keeping this custom does help to create good program, I think.At last, before function returns, it restore the $ra, $fp and other registers such as$s0-$s7, and jump to return address.So in this way, quick sort is implemented successfully. The sorted integers are kept in memory in order and can be output by using syscall. Input and output is quite easy, so I won’t introduce here.2.1 Binary Search2.2.1 Brief Introduction to Binary SearchBinary search is one of the searching algorithms, which is widely used in finding a given number in a list of sorted numbers. It’s time complexity is O(logn) and memory complexity is O(n).At the beginning, we pick up the number in the middle and compare it with the given one. If the given one is larger than the middle one, we go on searching the back half of the list, and if the given one is smaller than the middle one, we go on searching the front half of the list. Repeat this procedure until the number is found or cannot divide any more. In this way, we throw half of the numbers each comparison. So it’s called binary search.2.2.2 Implementation of Binary SearchCompared with quick sort, binary search is easier and doesn’t need recursive. However, I will also make a brief explanation on binary search.Here we begin binary search. The function needs two parameters: left index and right index.In the above segment, $t0 is the given number, and $t4 is the middle number. We can clearly see that if the given one is larger than the middle one, we go on searching the back half, and if the given one is smaller than the middle one, we go on searching the front half. If equal, target number is found, and program jump to print the result.If the target number is supposed in front half, we should move the right pointer to middle, otherwise, if it’s supposed in back half, we move the left pointer to middle.Of course, if at last there remains only two numbers and cannot be divided any more, compare the target number with both of them. If still not hit, just tell that the target number cannot be found.In this way, binary search is implemented successfully.3. Execution ResultsThe following figures show the execution results of this program. The first figure shows the whole picture while running. Figure 2 shows detailed result, including both sorting and searching. And figure 3 shows that the program can successfully deal with some complex situations, such as repeating numbers.Figure 1: Whole picture when runningFigure 2: More detailed resultFigure 3: Successfully deals with repeating numbers4. Code## HAO Cong -- 2010/12/15 - 2010/12/19## QuickSort_BinarySearch.asm -- A program for quick sort and binary search.dataarray: .space 1024num_of_integer: .word 0comment_msg: .asciiz "This is a program for quick sort and binary search.\n"input_msg: .asciiz "Please input the integers(-1 to quit):"search_msg: .asciiz "Please input a target integer:"not_found: .asciiz "Sorry, the integer does not exist."pos: .asciiz "Position: "thanks: .asciiz "Thanks for using!\n"newline: .asciiz "\n"whitespace: .asciiz " ".text.globl mainmain:la $a0, comment_msg # print the promoting message to screenli $v0, 4syscallmove $t7, $zero # t7 stores the number of integersla $t6, array # get base address of array and store in t6input:la $a0, input_msg # print the input message for userli $v0, 4syscallli $v0, 5 # read an integer from usersyscallli $t0, -1beq $v0, $t0, end_input # end input if integer is -1move $t0, $t7mul $t0, $t0, 4 # calculate offset addressaddu $t1, $t0, $t6 # calculate the actual address of the integer in memorysw $v0, ($t1) # store the integer to memoryaddu $t7, $t7, 1 # total number plus oneb input # go on inputend_input:sw $t7, num_of_integer # store the number of integers into memoryquick_sort_begin:li $a0, 0 # $a0: start indexsubu $a1, $t7, 1 # $a1: end indexjal quick_sort # call quick_sortoutput:lw $t1, num_of_integer # get the number of integers and keeps in $t1 li $t2, 0out_loop:beqz $t1, search # if $t1 decrease to zero, end the programsubu $t1, $t1, 1 # minus t1 by onemul $t3, $t2, 4 # calculate the integer's addressla $t4, arrayaddu $t4, $t3, $t4lw $a0, ($t4) # print one integerli $v0, 1syscallla $a0, whitespaceli $v0, 4syscalladdu $t2, $t2, 1 # $t2 plus oneb out_loopsearch:la $a0, newline # change a new lineli $v0, 4syscallla $a0, search_msg # print the input message for userli $v0, 4syscallli $v0, 5 # read an integer from usersyscallli $t0, -1beq $v0, $t0, end_program # end input if integer is -1b binary_searchend_program:la $a0, thanksli $v0, 4syscallli $v0, 10syscallquick_sort:bgt $a1, $a0, quick_sort_partition # if end index is larger than start index, do partition jr $ra # just return without doing nothingquick_sort_partition:subu $sp, $sp, 32 # frame size = 32sw $fp, 28($sp) # preserve the Frame Pointersw $ra, 24($sp) # preserve the Return Addressaddu $fp, $sp, 32 # move Frame Pointer to new basemove $t0, $a0 # put start index in $t0move $t1, $a1 # put end index in $t1la $t2, array # put array's base address in $t2mul $t3, $t0, 4addu $t3, $t3, $t2lw $t3, ($t3) # find the integer whose index is $t0mul $t4, $t1, 4addu $t4, $t4, $t2lw $t4, ($t4) # find the integer whose index is $t1li $t6, 2addu $t7, $t3, $t4divu $t5, $t7, $t6 # calculate the middle value: ( first + last ) / 2 , store in $t5move $s0, $t0move $s1, $t1 # backup $t0 and $t1sort_loop:left_half:bge $s0, $t1, right_half # if left pointer is larger than or equal to end index, jump outmul $s2, $s0, 4addu $s2, $s2, $t2 # the integer's address is in $s2lw $s3, ($s2) # get the integer whose index is $s0, put it in $s3bge $s3, $t5, right_half # if current integer is bigger than middle value, do right half addu $s0, $s0, 1 # $s0 plus oneb left_halfright_half:ble $s1, $t0, do_swap # if right pointer is smaller than or equal to start index, jump out of loopmul $s4, $s1, 4addu $s4, $s4, $t2 # the integer's address is in $s4lw $s5, ($s4) # get the integer whose index is $s1, put it in $s5ble $s5, $t5, do_swap # if the current integer is smaller than middle value, do swap subu $s1, $s1, 1 # $s1 minus oneb right_halfdo_swap:bgt $s0, $s1, partition # if left pointer is bigger than right pointer, do partitionmul $s2, $s0, 4addu $s2, $s2, $t2 # the integer's address is in $s2lw $s3, ($s2) # get the integer whose index is $s0, put it in $s3mul $s4, $s1, 4addu $s4, $s4, $t2 # the integer's address is in $s4lw $s5, ($s4) # get the integer whose index is $s1, put it in $s5sw $s3, ($s4)sw $s5, ($s2) # swap the two integers in memorybeq $s0, $t1, sort_loopaddu $s0, $s0, 1 # $s0 plus onebeq $s1, $t0, sort_loopsubu $s1, $s1, 1 # $s1 minus oneb sort_loop # go on sorting the integers partition:sw $s0, 20($sp) # preserve $s0 and $t1sw $t1, 16($sp)move $a0, $t0 # sort the left half of the arraymove $a1, $s1 # start from $t0 and end at $s1jal quick_sortlw $s0, 20($sp)lw $t1, 16($sp)move $a0, $s0 # sort the right half of the arraymove $a1, $t1 # start from $s0 and end at $t1jal quick_sortreturn:lw $ra, 24($sp) # restore Return Addresslw $fp, 28($sp) # restore Frame Pointeraddu $sp, $sp, 32 # restore Stack Pointerjr $ra # returnbinary_search:move $t0, $v0 # get the number from usermove $t1, $zero # $t1 is left indexlw $t2, num_of_integersubu $t2, $t2, 1 # $t2 is right indexla $t7, array # put array's base address in $t7 search_loop:subu $t3, $t2, $t1li $t4, 1beq $t3, $t4, judgement # if right - left = 1, compare the integer with both of themaddu $t3, $t1, $t2li $t4, 2div $t3, $t3, $t4 # middle index is ( left + right ) / 2, put in $t3move $t4, $t3mul $t4, $t4, 4addu $t4, $t4, $t7 # the integer's address is in $t4lw $t4, ($t4) # get the integer whose index is $t4, and still put it in $t4 blt $t0, $t4, front_half # if $t0 < $t4, find it in the front halfbgt $t0, $t4, back_half # if $t0 > $t4, find it in the back halfmove $a0, $t3 # if $t0 = $t4, the integer is foundjal get_resultjudgement:move $t5, $t1mul $t5, $t5, 4addu $t5, $t5, $t7lw $t6, ($t5) # find the integer with index of $t1, and put it in $t6move $a0, $t1beq $t0, $t6, jump_to_resultmove $t5, $t2mul $t5, $t5, 4addu $t5, $t5, $t7lw $t6, ($t5) # find the integer with index of $t2, and put it in $t6move $a0, $t2beq $t0, $t6, jump_to_resultla $a0, not_foundli $v0, 4syscallb searchjump_to_result:jal get_resultfront_half:move $t2, $t3 # move the right pointer to the middleb search_loopback_half:move $t1, $t3 # move the left pointer to the middleb search_loopget_result:move $t0, $a0addu $t0, $t0, 1la $a0, posli $v0, 4syscallmove $a0, $t0li $v0, 1syscallb searchReferences①Pick from /wiki/Quicksort, 2009。

Python快速排序法(转)

Python快速排序法(转)

Python快速排序法(转)⽅法解读:例:对初始序列:“6 1 2 7 9 3 4 5 10 8”采⽤快速排序法:⼀、分别从初始序列“6 1 2 7 9 3 4 5 10 8”两端开始“探测”。

先从右往左找⼀个⼩于6的数,再从左往右找⼀个⼤于6的数,然后交换他们。

这⾥可以⽤两个变量 i 和 j ,分别指向序列最左边和最右边。

我们为这两个变量起个好听的名字“哨兵i”和“哨兵j”。

刚开始的时候让哨兵i 指向序列的最左边(即i=1),指向数字6。

让哨兵j 指向序列的最右边(即j=10),指向数字8。

⼆、⾸先哨兵j 开始出动。

因为此处设置的基准数是最左边的数,所以需要让哨兵j先出动,这⼀点⾮常重要(请⾃⼰想⼀想为什么)。

哨兵j ⼀步⼀步地向左挪动(即 j--),直到找到⼀个⼩于6的数停下来。

接下来哨兵i 再⼀步⼀步向右挪动(即 i++),直到找到⼀个数⼤于6的数停下来。

最后哨兵j 停在了数字5⾯前,哨兵i 停在了数字7⾯前。

现在交换哨兵i 和哨兵j 所指向的元素的值。

到此,第⼀次交换结束。

三、接下来开始哨兵j继续向左挪动(再友情提醒,每次必须是哨兵j 先出发)。

他发现了4(⽐基准数6要⼩,满⾜要求)之后停了下来。

哨兵i 也继续向右挪动的,他发现了9(⽐基准数6要⼤,满⾜要求)之后停了下来。

此时再次进⾏交换。

四、第⼆次交换结束,“探测”继续。

哨兵j 继续向左挪动,他发现了3(⽐基准数6要⼩,满⾜要求)之后⼜停了下来。

哨兵i 继续向右移动,糟啦!此时哨兵i 和哨兵j 相遇了,哨兵i 和哨兵j 都⾛到3⾯前。

说明此时“探测”结束。

我们将基准数6和3进⾏交换。

交换之后的序列如下。

五、到此第⼀轮“探测”真正结束。

此时以基准数6为分界点,6左边的数都⼩于等于6,6右边的数都⼤于等于6。

回顾⼀下刚才的过程,其实哨兵j 的使命就是要找⼩于基准数的数,⽽哨兵i 的使命就是要找⼤于基准数的数,直到 i 和 j 碰头为⽌。

现在基准数6已经归位,它正好处在序列的第6位。

mpi冒泡排序并行化

mpi冒泡排序并行化

北京科技大学计算机与通信工程学院实验报告实验名称: 冒泡排序的并行化学生姓名:**专业:计算机科学与技术班级:计1203学号:********指导教师:***实验成绩:实验地点:机电楼301实验时间:2015年4月8日一、实验目的与实验要求1、实验目的(1)学会将串行程序改为并行程序。

(2)学会mpich2的使用。

(3)学会openmp的配置。

(4)mpi与openmp之间的比较。

2、实验要求(1)将串行冒泡程序局部并行化,以降低时间消耗。

(2) 理论上求出时间复杂度之比,根据结果得出时间消耗之比,进行比对分析。

二、实验设备(环境)及要求Vs2013,mpich2三、实验内容与步骤1、实验一mpi并行(1)实验内容1、写出一个冒泡排序程序,求出其时间复杂度,并运行得到相应的时间消耗。

2、将冒泡程序改为mpi并行程序:将全部需要排序的数分成4等份,分给四个进程一起冒泡,最后将所得的结果归到一个进程,进行归并排序,得到结果,得到时间消耗。

算出时间复杂度。

3、对得出的结果进行讨论与分析。

(2)主要步骤1、串行冒泡程序时间复杂度:取所要排序的数的个数为n个,时间复杂度为n*n/2。

代码实现:// maopao.cpp : 定义控制台应用程序的入口点。

//#include"stdafx.h"#include"stdlib.h"#include"time.h"const int ARRAY_SIZE = 120000;int main(int argc, char* argv[]){int zongshu[ARRAY_SIZE];srand(10086);time_t now_time, end_time;for (int i = 0; i < ARRAY_SIZE; i++){zongshu[i]=rand();}now_time = time(NULL);for (int i = 0; i < ARRAY_SIZE; i++){for (int j = ARRAY_SIZE - 1; j > i; j--){if (zongshu[j] <= zongshu[j - 1]){int z = zongshu[j - 1];zongshu[j - 1] = zongshu[j];zongshu[j] = z;}}}end_time = time(NULL);long shijian = end_time - now_time;for (int i = 0; i <ARRAY_SIZE; i++){printf("%d ", zongshu[i]);}printf("所用时间:%ld",shijian);while (true);}2、并行程序时间复杂度:取所要排序的数的个数为n个,进程数为m个。

(完整word版)并行计算实验快速排序的并行算法

(完整word版)并行计算实验快速排序的并行算法

3。

1实验目的与要求1、熟悉快速排序的串行算法2、熟悉快速排序的并行算法3、实现快速排序的并行算法3。

2 实验环境及软件单台或联网的多台PC机,Linux操作系统,MPI系统。

3.3实验内容1、快速排序的基本思想2、单处理机上快速排序算法3、快速排序算法的性能4、快速排序算法并行化5、描述了使用2m个处理器完成对n个输入数据排序的并行算法.6、在最优的情况下并行算法形成一个高度为log n的排序树7、完成快速排序的并行实现的流程图8、完成快速排序的并行算法的实现3.4实验步骤3.4。

1、快速排序(Quick Sort)是一种最基本的排序算法,它的基本思想是:在当前无序区R[1,n]中取一个记录作为比较的“基准”(一般取第一个、最后一个或中间位置的元素),用此基准将当前的无序区R[1,n]划分成左右两个无序的子区R[1,i-1]和R[i,n](1≤i≤n),且左边的无序子区中记录的所有关键字均小于等于基准的关键字,右边的无序子区中记录的所有关键字均大于等于基准的关键字;当R[1,i—1]和R[i,n]非空时,分别对它们重复上述的划分过程,直到所有的无序子区中的记录均排好序为止。

3.4.2、单处理机上快速排序算法输入:无序数组data[1,n]输出:有序数组data[1,n]Begincall procedure quicksort(data,1,n) Endprocedure quicksort(data,i,j)Begin(1) if (i<j) then(1。

1)r = partition(data,i,j)(1。

2)quicksort(data,i,r—1);(1.3)quicksort(data,r+1,j);end ifEndprocedure partition(data,k,l)Begin(1)pivo=data[l](2) i=k-1(3)for j=k to l—1 doif data[j]≤pivo theni=i+1exchange data[i] and data[j]end ifend for(4)exchange data[i+1] and data[l](5) return i+1End3.4.3、快速排序算法的性能主要决定于输入数组的划分是否均衡,而这与基准元素的选择密切相关。

MPI排序算法编程

MPI排序算法编程

一.实验要求1.写出原算法串行处理的思路或流程2.说明算法并行化的思路3.并且写出算法流程,最终编码实现二.实验环境硬件环境:Virtual PC 2007(XP)软件:VC++ 6.0;mpich.nt.1.2.5三.实验步骤和结果1.原插入排序原理插入排序过程示例将n个元素的数列分为已有序和无序两个部分,如下所示:{,{a2,a3,a4,…,an}}{{a1(1),a2(1)},{a3(1),a4(1) …,an(1)}}…{{a1(n-1),a2(n-1) ,…}, {an(n-1)}}每次处理就是将无序数列的第一个元素与有序数列的元素从后往前逐个进行比较,找出插入位置,将该元素插入到有序数列的合适位置中。

如果目标是把n个元素的序列升序排列,那么采用插入排序存在最好情况和最坏情况。

最好情况就是,序列已经是升序排列了,在这种情况下,需要进行的比较操作需(n-1)次即可。

最坏情况就是,序列是降序排列,那么此时需要进行的比较共有n(n-1)/2次。

插入排序的赋值操作是比较操作的次数加上 (n-1)次。

平均来说插入排序算法的时间复杂度为O(n^2)。

因而,插入排序不适合对于数据量比较大的排序应用。

2.并行化思路上文所说的插入排序在数据量大的情况下,运算量比较大,因此适合用并行计算来提高效率,下面是并行化算法的思路2.1 首先需要输入排序元素的个数,并输入需要排序的原色,用一个数组保存起来。

2.2将数组按照进程的数目平均分段,每个进程各领取一段去做插入排序。

2.3将进程排成一个二叉树的结构,每一个进程节点先将下面两个进程节点排好的数组接收进来,做一次归并排序。

再用这个归并排序结果跟进程节点再做一次归并排序。

2.4循环执行,直到树的根节点为止,排序完成。

2.5输出并行排序结果3.程序运行结果4.源代码//此算法实现插入排序的并行运算//陈仲策 2007034743011#include <stdio.h>#include <mpi.h>#include <time.h>#include <stdlib.h>int * merge(int *v1, int n1, int *v2, int n2); void swap(int *v, int i, int j);void sort(int *v, int n);double startT,stopT;double startTime;int * merge(int *v1, int n1, int *v2, int n2) {int i,j,k;int * result;result = (int *)malloc((n1+n2)*sizeof(int));i=0; j=0; k=0;while(i<n1 && j<n2)if(v1[i]<v2[j]){result[k] = v1[i];i++; k++;}else{result[k] = v2[j];j++; k++;}if(i==n1)while(j<n2){result[k] = v2[j];j++; k++;}elsewhile(i<n1){result[k] = v1[i];i++; k++;}return result;}/* 插入排序算法*/void InsertionSort(int *v, int n){int i;int j;int temp;for(i=1;i<n;i++){temp = v[i];for(j=i ; j>0 && temp < v[j-1] ; j--){v[j]=v[j-1];}v[j]=temp;}}/*主函数*/void main(int argc, char **argv){int m,n=500;int count;int *data;int *buf;int *other;int id,p;int s;int i;int step;MPI_Status status;/*MPI初始化*/MPI_Init(&argc,&argv);/*确定自己的进程标志号id*/MPI_Comm_rank(MPI_COMM_WORLD,&id);/*组内进程数是p*/MPI_Comm_size(MPI_COMM_WORLD,&p);/*根处理机(id=0)获取必要的信息,并协调各处理机工作*/if(id==0){scanf("%d",&count); //输入要排序的元素的个数n = count;int *a = (int *)malloc( count*sizeof(int));; //定义一个存放输入整数的指针,并分配空间for(int j=0;j<count;j++){scanf("%d,",&a[j]); //将输入的整数存入动态数组}int r;s = n/p;r = n%p;data = (int *)malloc((n+p-r)*sizeof(int));for(i=0;i<n;i++)data[i] = a[i];if(r!=0){for(i=n;i<n+p-r;i++)data[i]=0;s=s+1;}startT = clock();/* 从根处理机将数据序列广播到其他处理器 *//* 1表示传送的输入缓冲中的元素的个数 *//* MPI_INT表示输入元素的类型 *//* 0表示跟进程的id */MPI_Bcast(&s,1,MPI_INT,0,MPI_COMM_WORLD);buf = (int *)malloc(s*sizeof(int));MPI_Scatter(data,s,MPI_INT,buf,s,MPI_INT,0,MPI_COMM_WORLD);/*id号为0的处理器调度执行插入排序*/InsertionSort(buf,s);}else{MPI_Bcast(&s,1,MPI_INT,0,MPI_COMM_WORLD);buf = (int *)malloc(s*sizeof(int));MPI_Scatter(data,s,MPI_INT,buf,s,MPI_INT,0,MPI_COMM_WORLD);InsertionSort(buf,s);}step = 1;while(step<p){if(id%(2*step)==0){if(id+step<p){MPI_Recv(&m,1,MPI_INT,id+step,0,MPI_COMM_WORLD,&status);other = (int *)malloc(m*sizeof(int));MPI_Recv(other,m,MPI_INT,id+step,0,MPI_COMM_WORLD,&status);buf = merge(buf,s,other,m);s = s+m;}}else{int nears = id-step;MPI_Send(&s,1,MPI_INT,nears,0,MPI_COMM_WORLD);MPI_Send(buf,s,MPI_INT,nears,0,MPI_COMM_WORLD);break;}step = step*2;}if(id==0){FILE * fout;stopT = clock();/*显示执行并行运算后各个进程的总共所耗时间*/printf("element count %d ; \n %d processors cost total times; %f secs\n",count,p,(stopT-startT)/CLOCKS_PER_SEC);fout = fopen("result","w");for(i=0;i<s;i++)if (buf[i] != 0){fprintf(fout,"%d\n",buf[i]);//输出计算结果printf("%d\n",buf[i]);}fclose(fout);}MPI_Finalize();}。

MPI并行程序的设计-43页精选文档

当MPI初始化后,每一个活动进程变成了一个叫做 MPI_COMM_WORLD的通信域中的成员。通信域是一个不 透明的对象,提供了在进程之间传递消息的环境。
在一个通信域内的进程是有序的。在一个有p个进程的通信 域中,每一个进程有一个唯一的序号(ID号),取值为0~p -1。
进程可以通过调用函数MPI_Comm_rank来确定它在通信 域中的序号。
4、确定进程数量
用法:MPI_Comm_size( MPI_COMM_WORLD ,&p)
进程通过调用函数MPI_Comm_size来确定一个通信域中
的进程总数。
20PI_SEND(buf,count,datatype,dest,tag,comm)
参数说明:
2019/10/14
9
1、MPI初始化:MPI_Init函数
用法:MPI_Init(&argc , &argv)
每一个MPI进程调用的第一个MPI函数都是 MPI_Init。该函数指示系统完成所有的初始化工 作,以备对后续MPI库的调用进行处理。
2、 MPI结束:MPI_Finalize函数
处理器 内存
处理器 内存
互连网络
处理器 内存
处理器 内存
处理器 内存
处理器 内存
处理器 内存
假设底层的消息传递模型是一组处理器,每一个 处理器有自己的本地内存,并且通过互连网络实 现与其他处理器的消息传递
2019/10/14
2
MPI并行程序设计
MPI历史 机房集群环境 六个接口构成的MPI子集 MPI并行程序的两种基本模式 MPI并行程序的通信模式
三个从节点
用于从主节点接受计算任务并执行计算(返 回结果)。从节点开启SSH服务。

【MPI】并行奇偶交换排序

【MPI】并⾏奇偶交换排序typedef long long __int64;#include "mpi.h"#include <cstdio>#include <algorithm>#include <cmath>using namespace std;int Compute_partner(int phase,int my_rank,int comm_sz){//根据趟数的奇偶性以及当前编号的编号得到partner进程的编号int partner;if(!(phase&1)){if(my_rank&1){partner=my_rank-1;}else{partner=my_rank+1;}}else{if(my_rank&1){partner=my_rank+1;}else{partner=my_rank-1;}}if(partner==-1 || partner==comm_sz){partner=MPI_PROC_NULL;}return partner;}int main(int argc, char* argv[]){int my_rank=0, comm_sz=0;MPI_Init(&argc, &argv);MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);MPI_Comm_size(MPI_COMM_WORLD, &comm_sz);int np,n,local_n;//分别为进⾏奇偶交换排序的趟数和读⼊的数据总量以及分成的每段的长度FILE* fp;if(my_rank==0){fp=fopen("Sort.txt","r");fscanf(fp,"%d%d",&np,&n);local_n=n/comm_sz;}MPI_Bcast(&np,1,MPI_INT,0,MPI_COMM_WORLD);MPI_Bcast(&local_n,1,MPI_INT,0,MPI_COMM_WORLD);int* keys;int* my_keys=new int[local_n];if(my_rank==0){keys=new int[n];for(int i=0;i<n;++i){fscanf(fp,"%d",&keys[i]);}fclose(fp);}double beginTime = MPI_Wtime();MPI_Scatter(keys,local_n,MPI_INT,my_keys,local_n,MPI_INT,0,MPI_COMM_WORLD);//sort(my_keys,my_keys+local_n);//串⾏快速排序for(int i=0;i<local_n;++i){//串⾏奇偶交换排序if(!(i&1)){for(int j=0;j+1<local_n;j+=2){if(my_keys[j]>my_keys[j+1]){swap(my_keys[j],my_keys[j+1]);}}}else{for(int j=1;j+1<local_n;j+=2){if(my_keys[j]>my_keys[j+1]){swap(my_keys[j],my_keys[j+1]);}}}}int* recv_keys=new int[local_n];int* temp_keys=new int[local_n];for(int i=0;i<np;++i){int partner=Compute_partner(i, my_rank, comm_sz);if (partner != MPI_PROC_NULL){MPI_Sendrecv(my_keys, local_n, MPI_INT, partner, 0, recv_keys, local_n, MPI_INT, partner, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);if(my_rank<partner){//编号⼩的进程留下归并时较⼩的⼀半int e=0,e1=0,e2=0;int* temp_keys=new int[local_n];while(e<local_n){if(my_keys[e1]<=recv_keys[e2]){temp_keys[e]=my_keys[e1];++e;++e1;}else{temp_keys[e]=recv_keys[e2];++e;++e2;}}for(int j=0;j<local_n;++j){my_keys[j]=temp_keys[j];}}else{//编号⼤的进程留下归并时较⼤的⼀半int e=local_n-1,e1=local_n-1,e2=local_n-1;while(e>=0){if(my_keys[e1]>=recv_keys[e2]){temp_keys[e]=my_keys[e1];--e;--e1;}else{temp_keys[e]=recv_keys[e2];--e;--e2;}}for(int j=0;j<local_n;++j){my_keys[j]=temp_keys[j];}}}}MPI_Gather(my_keys, local_n, MPI_INT, keys, local_n, MPI_INT, 0, MPI_COMM_WORLD); double endTime = MPI_Wtime();if (my_rank == 0){for(int i=0;i<n;++i){printf("%d ",keys[i]);}puts("");printf("spent time = %lf second\n", endTime - beginTime);}delete[] keys;delete[] my_keys;delete[] recv_keys;delete[] temp_keys;MPI_Finalize();return 0;}。

基于MPI的分布式并行排序算法的实现

基于MPI的分布式并行排序算法的实现(信息工程学院,计算机系,计算机科学与技术专业凌广杰)(学号:2000131031)内容提要:本论文采用MPI(Message Passing Interface)在由独立处理器构成的计算机网络上实现了一个称之为IPBPS(Interconnected Processor-Based Parallel Sorting)的分布式并行排序算法。

IPBPS算法是基于一个特定的网络拓扑结构上,它的实现主要分为并行计数排序与归并排序两大模块。

采用MPI来实现IPBPS算法,利用MPI消息传递的同步机制,很好地解决了不同处理器间消息传递的同步问题。

每个处理器运行相同的程序,运行时间也几乎相同,计算负载均匀分布在网络上的每个处理器中,因此并行加速比较高。

关键词:MPI,并行排序,并行程序设计,分布式程序设计教师点评:该论文基于消息传递的并行计算模型,在多台独立互联的计算机上采用MPI(消息传递接口)实现了IPBPS分布式并行排序算法。

针对该具体问题,在任务分配、进程通信与同步、以及采用MPI构建IPBPS算法基于的虚拟网络结构上提出了自己的设想与实现方案。

解决了在由多计算机构成的分布式计算平台上不同计算机之间计算负载的均分、通信与同步等问题。

该论文具有一定的创新性,对基于MPI的并行与分布式程序设计具有一定的示范作用。

论文的表达书写也比较好。

(点评教师:陆楠。

副教授)1. 引言排序计算是计算机应用中最基本的运算之一,在20世纪60年代的计算机厂家就估计,当他们把所有的顾客都考虑在内时,在他们的计算机上,将有超过25%的时间花在排序上[3]。

排序的重要性不言而喻,传统的串行排序算法的速度已不能满足用户的要求。

为了提高排序的速度,人们普遍转向了并行排序算法的研究。

目前大部分并行排序算法的研究都是基于共享内存的多处理器或流水(Pipeline)计算机上的,但由于多处理机中微处理器的个数非常有限且通过共享内存可同时传送和访问的数据也不可能很多。

MPI实现并行奇偶排序

MPI实现并⾏奇偶排序奇偶排序使⽤ MPI 实现奇偶排序算法,并且 MPI 进程只能向其相邻进程发送消息nprocs是进程数。

每个进程拥有独⽴的⼀块数据data[0 ~ block_len-1],组合起来为整个待排序的数组。

⽅法每个阶段排序之后不进⾏check此前,在每个阶段的奇偶排序进⾏完之后,都会进⾏⼀次进程之间的信息传递,以判断排序是否完成,这个过程要进⾏约3∗nprocs次的send/recv。

现在的优化是:总共只进⾏nprocs轮排序,不再进⾏check。

这样的话,即使是⽬前在最⼩编号进程中的元素,⽽它值较⼤,本应排序到最⼤编号进程中,也可以在nprocs轮中排到正确的位置。

这样之后,⼤约有⼏⼗ms的优化。

进程之间互相传递数据,然后进⾏优化后的归并在⼀个排序阶段中,相邻进程块互相发送⾃⼰的全部数据,之后在每个块内部将两个块的数据进⾏归并,但是只保留最⼩/最⼤的block_len 个元素,将其拷贝到⾃⼰的data上。

这样可以省掉⼀半的归并时间。

这样之后⼤约有100+ms的优化。

进程之间发送全部数据之前,先发送端点处的数据进程之间发送全部数据之前,先发送端点处的数据,判断左边进程中的最⼤元素是否⼩于等于右边进程中的最⼩元素,如果是,那么⽆需进⾏后续数据的发送和归并。

这样之后⼤约有⼏⼗ms的优化。

代码#include <algorithm>#include <cassert>#include <cstdio>#include <cstdlib>#include <mpi.h>#include <cmath>#include "worker.h"using namespace std;bool is_edge(int rank, bool odd_or_even, bool last_rank){if (odd_or_even == 0){return (rank & 1) == 0 && last_rank;}else{return rank == 0 || ((rank & 1) == 1 && last_rank);}}void merge_left(float *A, int nA, float *B, int nB, float *C){ //make sure C[nA-1] is availablefloat *p1 = A, *A_end = A + nA, *p2 = B, *B_end = B + nB, *p = C, *C_end = C + nA;while( p != C_end && p1 != A_end && p2 != B_end)*(p++) = ((*p1) <= (*p2)) ? *(p1++) : *(p2++);while( p != C_end )*(p++) = *(p1++);}void merge_right(float *A, int nA, float *B, int nB, float *C){float *p1 = A + nA , *p2 = B + nB , *p = C + nB;while( p != C && p1 != A && p2 != B )*(--p) = (*(p1-1) >= *(p2-1)) ? *(--p1) : *(--p2);while( p != C )*(--p) = *(--p2);}void Worker::sort() {//data[0, block_len)if (out_of_range) return ;std::sort(data, data + block_len);//先把当前进程数据排好序if (nprocs == 1) return ;bool odd_or_even = 0; // = 0: even; = 1: odd;float *cp_data = new float [block_len];float *adj_data = new float [ceiling(n, nprocs)];int limit = nprocs;while(limit--){if(is_edge(rank, odd_or_even, last_rank)){//边界情况,没有与其他进程存在于同⼀个进程块内}else if((rank & 1) == odd_or_even){ //receive infosize_t adj_block_len = std::min(block_len, n - (rank + 1) * block_len);MPI_Request request[2];MPI_Isend(data + block_len - 1, 1, MPI_FLOAT, rank + 1, 0, MPI_COMM_WORLD, &request[0]);MPI_Irecv(adj_data, 1, MPI_FLOAT, rank + 1, 1, MPI_COMM_WORLD, &request[1]);MPI_Wait(&request[0], MPI_STATUS_IGNORE);MPI_Wait(&request[1], MPI_STATUS_IGNORE); //发送端点数据if(data [block_len - 1] > adj_data[0]) {//此时两块之间存在未排好序的数据,需要排序MPI_Sendrecv(data, block_len, MPI_FLOAT, rank + 1, 0,adj_data, adj_block_len, MPI_FLOAT, rank + 1, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE); //互相交换数据// mergemerge_left(data, (int)block_len, adj_data, (int)adj_block_len, cp_data);//进⾏归并排序,取前block_len个数据返回到cp_data中memcpy(data, cp_data, block_len * sizeof(float)); //拷贝回data}}else if ((rank & 1) == !odd_or_even){ //send infosize_t adj_block_len = ceiling(n, nprocs);MPI_Request request[2];MPI_Isend(data, 1, MPI_FLOAT, rank - 1, 1, MPI_COMM_WORLD, &request[1]);MPI_Irecv(adj_data + adj_block_len - 1, 1, MPI_FLOAT, rank- 1, 0, MPI_COMM_WORLD, &request[0]);MPI_Wait(&request[1], MPI_STATUS_IGNORE);MPI_Wait(&request[0], MPI_STATUS_IGNORE);//发送端点数据if (adj_data[adj_block_len - 1] > data[0]){//此时两块之间存在未排好序的数据,需要排序MPI_Sendrecv(data, block_len, MPI_FLOAT, rank - 1, 1,adj_data, adj_block_len, MPI_FLOAT, rank - 1, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); //互相交换数据// mergemerge_right(adj_data, (int)adj_block_len, data, (int)block_len, cp_data);//进⾏归并排序,取前block_len个数据返回到cp_data中memcpy(data, cp_data, block_len * sizeof(float)); //拷贝回data}}odd_or_even ^= 1;}delete[] cp_data;delete[] adj_data;}实验数据n N× P耗时(ms)相对单进程的加速⽐1000000001×112728.32600011000000001×26754.229000 1.8841000000001×43559.514000 3.5761000000001×82007.818000 6.3391000000001×161340.7710009.4931000000002×16870.30200014.625Processing math: 100%。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
相关文档
最新文档