在第五讲中我们学习了GPU三个重要的基础并行算法: Reduce, Scan 和 Histogram,分析了 其作用与串并行实现方法。 在第六讲中,本文以冒泡排序 Bubble Sort、归并排序 Merge Sort 和排序网络中的双调排序 Bitonic Sort 为例, 讲解如何从数据结构课上学的串行并行排序方法转换到并行排序,并附GPU实现代码。
在并行方法中,我们将考虑到并行方法需要注意的特点进行设计,希望大家在读完本文后对GPU上并行算法的设计有一些粗浅的认识。需注意的特点如下:
1. 充分发挥硬件能力(尽量不要有空闲且一直处于等待状态的SM)
2. 限制branch divergence(见CUDA系列学习(二))
3. 尽量保证内存集中访问(即防止不命中)
( 而我们在数据结构课上学习的sort算法往往不注意这几点。)
CUDA系列学习目录:
CUDA系列学习(一)An Introduction to GPU and CUDA
CUDA系列学习(二)CUDA memory & variables - different memory and variable types
CUDA系列学习(三)GPU设计与结构QA & coding练习
CUDA系列学习(四)Parallel Task类型 与 Memory Allocation
CUDA系列学习(五)GPU基础算法: Reduce, Scan, Histogram
冒泡排序,相信大家再熟悉不过了。经典冒泡排序通过n轮有序冒泡(n为待排序的数组长度)得到有序序列, 其空间复杂度O(1), 时间复杂度O(n^2)。
那么如何将冒泡排序算法改成并行算法呢? 这里就需要解除一些依赖关系, 比如是否能解除n轮冒泡间的串行依赖 & 是否能解除每一轮冒泡内部的串行依赖, 使得同样的n^2次冒泡操作可以通过并行, 降低step complexity。
1996年, J Kornerup针对这些问题提出了odd-even sort算法,并在论文中证明了其排序正确性。
先来看一下odd-even sort的排序方法:
上图为odd-even sort的基本方法。
奇数步中, array中奇数项array[i]与右边的item(array[i + 1])比较;
偶数步中, array中奇数项array[i]与左边的item(array[i - 1]) 比较;
这样,同一个step中的各个相邻比较就可以并行化了。
PS: 对于array中有偶数个元素的数组也是一样:
在odd-even sort的算法下, 原本O(n^2)的总比较次数不变,但是由于并行,时间复杂度降到O(n), 即
step complexity = O(n)
work complexity = O(n^2)
code详见 < Bubble sort and its variants >
看过odd-even sort后,我们来看如何将归并排序并行化。数据结构课上我们都学过经典归并排序: 基于divide & conquer 思想, 每次将一个array分拆成两部分, 分别排序后合并两个有序序列。 可以通过 T(n)=2T(n/2)+n 得到, 其complexity = O(nlogn)。 和I.1节类似, 我们看看merge sort中的哪些步是可以并行的。
这里可以将基于merge sort的大规模数据排序分为三部分。 经过divide步之后, 数据分布如图所示:
最下端的为 大量-短序列 的合并;
中间一块为中等数量-中等长度序列 的合并;
最上端的为少量-长序列 的合并;
我们分这三部分进行并行化。 之后大家会明白为啥要这么分~
这一部分,每一部分序列合并的代价很小, 而有众多这样的任务。 所以我们采取给每个merge一个thread去执行, thread 内部就用串行merge的方法。
在这一阶段, 有中等数量的task, 每个task的工作量也有一定增长。 所以我们采取给每个merge一个SM去执行, 每个block上运行的多个thread并行merge的方法。 和step1中的主要区别就是block内部merge从串行改成了并行。 那么怎样去做呢?
如下图所示,假如现在有两个长为4个元素的array, 想对其进行排序, 将merge排序结果index 0 - 7 写入数据下方方块。
做法:
对于array中的每个数字, 看两个位置:
1. 自己所在序列的位置: 看它前面有几个元素
2. 另一个序列的位置: 另一个序列中有多少个元素比它小(采用二分搜索)
这样做来, 第一步O(1)可得到, 第二步二分查找O(logn)可得到; 整个merge过程用shared memory存结果。
第三个环节中,也就是merge的顶端(最后一部分),每个merge任务的元素很多, 但是merge任务数很少。 这种情况下, 如果采用Step2的方法, 最坏的情况就是只有一个很大的task在跑, 此时只有1个SM在忙, 其他空闲SM却没法利用, 所以这里我们尝试将一个任务分给多个SM执行。
方法: 如图2.3所示, 将每个序列以256个元素为单位分段, 得到两个待merge序列In1和In2。然后,对这些端节点排序,如EABFCGDH。 如step2中的方法, 我们计算每个段节点在另一个短序列(长256)中的位置, 然后只对中间那些不确定的部分进行merge排序, 每个merge分配一个SM。
如E~A的部分,
1. 计算出E在In1中的位置posE1, A在In2中的位置posA2
2. merge In1中 posE1~A 和 In2中 E~posA2的元素
以上面step 1的merge sort为例, 其gpu代码中kernel函数如下:
其中temp为排好序的序列, 每次排序两个大小为sortedsize的block,为temp赋值2 * sortedsize个元素。
所以实际上,sortedsize就是一个sorted block的大小。
<code class="hljs objectivec has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">__global__ <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> mergeBlocks(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> *a, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> *temp, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> sortedsize) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">id</span> = blockIdx<span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">.x</span>; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> index1 = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">id</span> * <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">2</span> * sortedsize; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> endIndex1 = index1 + sortedsize; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> index2 = endIndex1; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> endIndex2 = index2 + sortedsize; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> targetIndex = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">id</span> * <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">2</span> * sortedsize; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> done = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">while</span> (!done) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> ((index1 == endIndex1) && (index2 < endIndex2)) temp[targetIndex++] = a[index2++]; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> ((index2 == endIndex2) && (index1 < endIndex1)) temp[targetIndex++] = a[index1++]; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (a[index1] < a[index2]) temp[targetIndex++] = a[index1++]; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> temp[targetIndex++] = a[index2++]; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> ((index1 == endIndex1) && (index2 == endIndex2)) done = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>; } } </code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li></ul>
主函数中,定义block大小并调用kernel function:
<code class="hljs cs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> blocks = BLOCKS/<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">2</span>; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> sortedsize = THREADS; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">while</span> (blocks > <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>) { mergeBlocks<<<blocks,<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>>>>(dev_a, dev_temp, sortedsize); cudaMemcpy(dev_a, dev_temp, N*<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">sizeof</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span>), cudaMemcpyDeviceToDevice); blocks /= <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">2</span>; sortedsize *= <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">2</span>; } cudaMemcpy(a, dev_a, N*<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">sizeof</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span>), cudaMemcpyDeviceToHost); </code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li></ul>
不同于以上两种排序方法, 现在我们要接触的 双调排序 是排序网络方法中的一种。 想起当年在浙大面试某导师的实验室时就是让实现的双调排序, 并不断优化, 不过当时土得一坨, 就没听说过这个算法。。。 最后写出个多线程就结束了, 后来也没再整理。 现在我们来看看bitonic sort是个什么鬼。
双调排序是排序网络中最快的方法之一。所谓的排序网络是data-independent的排序, 即网络比较顺序与数据无关的排序方法, 所以特别适合硬件做并行化。
在了解双调排序算法之前,我们先来看看什么是双调序列。 双调序列是一个先单调递增后单调递减 或者 先单调递减后单调递增的序列。
假如我们现在拿到了双调序列, 怎样对它按照从小到大进行排序呢?形象一点来看, 我们将一个双调序列切成两半, 每一段的单调性统一, 然后如下图图3.1所示, 将两段叠放起来, 进行两两比较, 这样一定能够在左右两段分别得到一个双调序列(想想为什么得到的是两个双调序列), 且左边的双调序列中元素全部小于右侧得到的双调序列的所有元素。 迭代这个过程, 每次都能将序列二分成两个子双调序列, 直到这个子双调序列的长度为2, 也就变成了一个单调子序列, 这个过程排序后原先的长双调序列就变为有序了 。 整个过程如下图图3.2所示。
好,III.2中讲了怎样对双调序列进行排序, 那问题来了,怎样从任意序列生成双调序列呢? 这里可以看看本文最后的参考文献3, 写得很详细。 这个过程叫Bitonic merge, 实际上也是divide and conquer的思路。 和III.2中的思路正相反, 我们可以将两个相邻的,单调性相反的单调序列看作一个双调序列, 每次将这两个相邻的,单调性相反的单调序列merge生成一个新的双调序列, 然后排序(同III.2)。 这样只要每次两个相邻长度为n的序列的单调性相反, 就可以通过连接得到一个长度为2n的双调序列。 n开始为1, 每次翻倍,直到等于数组长度, 就只需要一遍单方向(单调性)排序了。
以16个元素的array为例,
1. 相邻两个元素合并形成8个单调性相反的单调序列,
2. 两两序列合并,形成4个双调序列,分别按相反单调性排序
3. 4个长度为4的相反单调性单调序列,相邻两个合并,生成两个长度为8的双调序列,分别排序
4. 2个长度为8的相反单调性单调序列,相邻两个合并,生成1个长度为16的双调序列,排序
总算讲完了,那么肿么实现呢? 我们看这个过程需要控制哪些地方? 如上图所示, 我们可以将len=16的array的双调排序分成4部分,每一部分结束都会形成若干长度为 i 的单调序列。 在每一部分中,用 j 表示比较的间隔,如下图所示每一时刻的i和j。
本着“talk is cheap, show me the code”的优良作风, 拿粗来双调排序的GPU实现代码share如下:
<code class="hljs cpp has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* * Author: Rachel * <[email protected]> * * File: bitonic_sort.cu * Create Date: 2015-08-05 17:10:44 * */</span> <span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#include<iostream></span> <span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#include<stdio.h></span> <span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#include<stdlib.h></span> <span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#include"gputimer.h"</span> <span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#include<time.h></span> <span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#define NThreads 8</span> <span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#define NBlocks 4</span> <span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#define Num NThreads*NBlocks</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">using</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">namespace</span> Gadgetron; __device__ <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> swap(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> &a, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> &b){ <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> t = a; a = b; b = t; } __global__ <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> bitonic_sort(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span>* arr){ <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">extern</span> __shared__ <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> shared_arr[]; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">const</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">unsigned</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> tid = blockIdx.x * blockDim.x + threadIdx.x; <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//const unsigned int tid = threadIdx.x;</span> shared_arr[tid] = arr[tid]; __syncthreads(); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//for(int i=2; i<=blociDim.x; i<<=1){</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">unsigned</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> i=<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">2</span>; i<=Num; i<<=<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>){ <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">unsigned</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> j=i>><span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>; j><span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>; j>>=<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>){ <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">unsigned</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> tid_comp = tid ^ j; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span>(tid_comp > tid){ <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span>((tid & i)==<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>){ <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//ascending</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span>(shared_arr[tid]>shared_arr[tid_comp]){ swap(shared_arr[tid],shared_arr[tid_comp]); } } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span>{ <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//desending</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span>(shared_arr[tid]<shared_arr[tid_comp]){ swap(shared_arr[tid],shared_arr[tid_comp]); } } } __syncthreads(); } } arr[tid] = shared_arr[tid]; } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> main(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> argc, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">char</span>* argv[]) { GPUTimer timer; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span>* arr= (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span>*) <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">malloc</span>(Num*<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">sizeof</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span>)); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//init array value</span> time_t t; srand((<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">unsigned</span>)time(&t)); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> i=<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>;i<Num;i++){ arr[i] = rand() % <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1000</span>; } <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//init device variable</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span>* ptr; cudaMalloc((<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>**)&ptr,Num*<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">sizeof</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span>)); cudaMemcpy(ptr,arr,Num*<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">sizeof</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span>),cudaMemcpyHostToDevice); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> i=<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>;i<Num;i++){ <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">printf</span>(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"%d\t"</span>,arr[i]); } <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">printf</span>(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"\n"</span>); dim3 blocks(NBlocks,<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>); dim3 threads(NThreads,<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>); timer.start(); bitonic_sort<<<blocks,threads,Num*<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">sizeof</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span>)>>>(ptr); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//bitonic_sort<<<1,Num,Num*sizeof(int)>>>(ptr);</span> timer.stop(); cudaMemcpy(arr,ptr,Num*<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">sizeof</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span>),cudaMemcpyDeviceToHost); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> i=<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>;i<Num;i++){ <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">printf</span>(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"%d\t"</span>,arr[i]); } <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">printf</span>(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"\n"</span>); cudaFree(ptr); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>; } </code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li><li style="box-sizing: border-box; padding: 0px 5px;">41</li><li style="box-sizing: border-box; padding: 0px 5px;">42</li><li style="box-sizing: border-box; padding: 0px 5px;">43</li><li style="box-sizing: border-box; padding: 0px 5px;">44</li><li style="box-sizing: border-box; padding: 0px 5px;">45</li><li style="box-sizing: border-box; padding: 0px 5px;">46</li><li style="box-sizing: border-box; padding: 0px 5px;">47</li><li style="box-sizing: border-box; padding: 0px 5px;">48</li><li style="box-sizing: border-box; padding: 0px 5px;">49</li><li style="box-sizing: border-box; padding: 0px 5px;">50</li><li style="box-sizing: border-box; padding: 0px 5px;">51</li><li style="box-sizing: border-box; padding: 0px 5px;">52</li><li style="box-sizing: border-box; padding: 0px 5px;">53</li><li style="box-sizing: border-box; padding: 0px 5px;">54</li><li style="box-sizing: border-box; padding: 0px 5px;">55</li><li style="box-sizing: border-box; padding: 0px 5px;">56</li><li style="box-sizing: border-box; padding: 0px 5px;">57</li><li style="box-sizing: border-box; padding: 0px 5px;">58</li><li style="box-sizing: border-box; padding: 0px 5px;">59</li><li style="box-sizing: border-box; padding: 0px 5px;">60</li><li style="box-sizing: border-box; padding: 0px 5px;">61</li><li style="box-sizing: border-box; padding: 0px 5px;">62</li><li style="box-sizing: border-box; padding: 0px 5px;">63</li><li style="box-sizing: border-box; padding: 0px 5px;">64</li><li style="box-sizing: border-box; padding: 0px 5px;">65</li><li style="box-sizing: border-box; padding: 0px 5px;">66</li><li style="box-sizing: border-box; padding: 0px 5px;">67</li><li style="box-sizing: border-box; padding: 0px 5px;">68</li><li style="box-sizing: border-box; padding: 0px 5px;">69</li><li style="box-sizing: border-box; padding: 0px 5px;">70</li><li style="box-sizing: border-box; padding: 0px 5px;">71</li><li style="box-sizing: border-box; padding: 0px 5px;">72</li><li style="box-sizing: border-box; padding: 0px 5px;">73</li><li style="box-sizing: border-box; padding: 0px 5px;">74</li><li style="box-sizing: border-box; padding: 0px 5px;">75</li><li style="box-sizing: border-box; padding: 0px 5px;">76</li><li style="box-sizing: border-box; padding: 0px 5px;">77</li><li style="box-sizing: border-box; padding: 0px 5px;">78</li><li style="box-sizing: border-box; padding: 0px 5px;">79</li><li style="box-sizing: border-box; padding: 0px 5px;">80</li><li style="box-sizing: border-box; padding: 0px 5px;">81</li><li style="box-sizing: border-box; padding: 0px 5px;">82</li><li style="box-sizing: border-box; padding: 0px 5px;">83</li><li style="box-sizing: border-box; padding: 0px 5px;">84</li><li style="box-sizing: border-box; padding: 0px 5px;">85</li><li style="box-sizing: border-box; padding: 0px 5px;">86</li><li style="box-sizing: border-box; padding: 0px 5px;">87</li><li style="box-sizing: border-box; padding: 0px 5px;">88</li><li style="box-sizing: border-box; padding: 0px 5px;">89</li><li style="box-sizing: border-box; padding: 0px 5px;">90</li><li style="box-sizing: border-box; padding: 0px 5px;">91</li><li style="box-sizing: border-box; padding: 0px 5px;">92</li><li style="box-sizing: border-box; padding: 0px 5px;">93</li><li style="box-sizing: border-box; padding: 0px 5px;">94</li><li style="box-sizing: border-box; padding: 0px 5px;">95</li><li style="box-sizing: border-box; padding: 0px 5px;">96</li><li style="box-sizing: border-box; padding: 0px 5px;">97</li><li style="box-sizing: border-box; padding: 0px 5px;">98</li><li style="box-sizing: border-box; padding: 0px 5px;">99</li></ul>
code中,
tid^j用于单方向判断, 防止同一元素比较两次;
tid & i == 0 用于判断这个部分应该是单增还是单减, 因为方向在每个长为i的单调序列中是一致的, 所以选用i判断单调方向。
参考文献:
1. Bubble sort and its variants
2. nvidia的mergesort实现
3. 我用过的浅显易懂的Bitonic sort文档
from: http://blog.csdn.net/abcjennifer/article/details/47110991