#ifndef _BUCKETSORT_H #define _BUCKETSORT_H #include "def.h" #include <omp.h> template <class T> struct DATA_ARRAY_TEMP { T *pData; UINT uCount; }; int GetDoubleByteKey( int uData, UINT uKeyIndex ) ; //typedef int (*GetKey)( int uData, UINT uKeyIndex ); /** 对数组进行基数排序的分区函数 @param T *pData - 待排序数据 @param UINT uDataLen - 数据长度 @param UINT uRadix - 基数 @param UINT uKeyIndex - 关键词索引(第几个基数位) @param GETKEY GetKeyFunc - 关键词取位回调函数 @param UINT *puBoxDataCount - OUT, 记录每个箱子数据个数的数组 @param T *pOut - OUT,存放分区结束后的数据 @param DATA_ARRAY_TEMP<T> *pArray - OUT, 输出分区后的各个箱子数据信息 @return T * - 返回pOut指针 */ template <class T,class GETKEY> T *Serial_Partitioned(T *pData, UINT uDataLen,UINT uRadix, UINT uKeyIndex, GETKEY GetKeyFunc, UINT *puBoxDataCount, T *pOutData, DATA_ARRAY_TEMP<T> *pArray) { UINT i; UINT uIndex; for ( i = 0; i < uRadix; i++ ) { puBoxDataCount[i] = 0; } // 计算各个箱子中的元素数量,存放在puBoxDataCount数组中 for ( i = 0; i < uDataLen; i++ ) { uIndex = (*GetKeyFunc)(pData[i],uKeyIndex); puBoxDataCount[uIndex] += 1; } // 计算各个箱子在数组中的位置 T *p = pOutData; for ( i = 0; i < uRadix; i++ ) { pArray[i].pData = p; p += puBoxDataCount[i]; pArray[i].uCount = 0; } // 将数据分配到各个箱子里 for ( i = 0; i < uDataLen; i++ ) { uIndex = (*GetKeyFunc)(pData[i],uKeyIndex); pArray[uIndex].pData[pArray[uIndex].uCount] = pData[i]; pArray[uIndex].uCount += 1; } return pOutData; } /** 对数组进行串行基数排序的函数 排序后的结果仍然保存在pData数组中 @param T *pData - 待排序数据 @param UINT uDataLen - 数据长度 @param UINT uRadix - 基数 @param UINT uMaxKeyLen - 最大关键词长度,比如位整数以为基数时, 最大关键词长度为 @param GETKEY GetKeyFunc - 关键词取位回调函数 @return void - 无 */ template <class T,class GETKEY> void SerialRadixSort(T *pData, UINT uDataLen, UINT uRadix, UINT uMaxKeyLen, GETKEY GetKeyFunc) { UINT *puBoxDataCount = (UINT *)malloc(sizeof(UINT) * uRadix); T *pOutData = new T[uDataLen]; DATA_ARRAY_TEMP<T> *pArray = new DATA_ARRAY_TEMP<T>[uRadix]; UINT i; T *pInData; T *pTempData; pInData = pData; for ( i = 0; i < uMaxKeyLen; i++ ) { Serial_Partitioned(pInData, uDataLen, uRadix, i, GetKeyFunc, puBoxDataCount, pOutData, pArray); pTempData = pInData; pInData = pOutData; pOutData = pTempData; } if ( pInData == pData ) { delete [] pOutData; } else { for ( i = 0; i < uDataLen; i++ ) { pData[i] = pOutData[i]; } delete []pOutData; } delete [] puBoxDataCount; delete [] pArray; } /** 负载平衡的并行基数排序 对数组进行并行基数排序 @param T *ppData - 待排序数据 @param UINT uDataLen - 数据长度 @param UINT uRadix - 基数 @param UINT uMaxKeyLen - 最大关键词长度 @param GETKEY GetKeyFunc - 关键词取位回调函数 @return void - 无 */ template <class T, class GETKEY> void Parallel_RadixSort_LBR(T *pData, UINT uDataLen, UINT uRadix, UINT uMaxKeyLen, GETKEY GetKeyFunc) { int i; int nProcessors = omp_get_num_procs(); UINT uCount = uDataLen / nProcessors; if ( uDataLen - uCount * nProcessors > 2 ) { uCount++; } T *pOutData = new T[uDataLen]; DATA_ARRAY_TEMP<T> **ppDataArray = new DATA_ARRAY_TEMP<T> *[nProcessors]; UINT **ppuBoxDataCount = new UINT *[nProcessors]; for ( i = 0; i < nProcessors; i++ ) { ppDataArray[i] = new DATA_ARRAY_TEMP<T>[uRadix]; ppuBoxDataCount[i] = new UINT[uRadix]; } UINT uKeyIndex; for ( uKeyIndex = 0; uKeyIndex < uMaxKeyLen; uKeyIndex++ ) { //下面代码完成全局的一轮将数据分配到箱子里的操作 #pragma omp parallel for num_threads(nProcessors) schedule(static, 1) for ( i = 0; i < nProcessors; i++ ) { //每个线程计算自己的数据起始位置,其中end是开区间位置 UINT begin = i * uCount; UINT end = (i+1) * uCount; if ( end > uDataLen ) { end = uDataLen; } T * pOut = pOutData + begin; T *p = pData + begin; Serial_Partitioned<T>(p, end-begin, uRadix, uKeyIndex, GetKeyFunc, ppuBoxDataCount[i], pOut, ppDataArray[i]); }//for ( i = 0; i < nProcessors; i++ ) UINT *puCount = ppuBoxDataCount[0]; #pragma omp parallel for num_threads(dtn(uRadix, 4096)) //最小循环次数设为多少需要测试 for ( i = 0; i < uRadix; i++ ) { int k; for ( k = 1; k < nProcessors; k++ ) { UINT * pk = ppuBoxDataCount[k]; puCount[i] += pk[i]; } } //计算前缀和 for ( i = 1; i < uRadix; i++ ) { puCount[i] += puCount[i-1]; } //得到各段数据的起始位置 for ( i = uRadix - 1; i > 0; i-- ) { puCount[i] = puCount[i-1]; } puCount[0] = 0; //收集操作,将pOutData里的数据重新放回pData里 int m; #pragma omp parallel for private(i) schedule(dynamic) for ( m = 0; m < uRadix; m++ ) { int nIndex = puCount[m]; for ( i = 0; i < nProcessors; i++ ) { DATA_ARRAY_TEMP<T> *pDataArray = ppDataArray[i]; T *pDataTemp = pDataArray[m].pData; int k; for ( k = 0; k < pDataArray[m].uCount; k++ ) { pData[nIndex] = pDataTemp[k]; ++nIndex; } } } //进行下一轮的分配与收集操作 } //for ( uKeyIndex = 0; uKeyIndex < uMaxKeyLen; uKeyIndex++ ) //释放表 for ( i = 0; i < nProcessors; i++ ) { delete [] ppDataArray[i]; delete [] ppuBoxDataCount[i]; } delete [] ppDataArray; delete [] ppuBoxDataCount; delete [] pOutData; return; } #endif #include "bucketSort.h" /** 基数排序的关键词取位回调函数 一次取位整数中的16位,即以65536作为基数 @param UINT uData - 要取位的数据 @param UINT uKeyIndex - 第几个取位,为或,为表示取低位,表示取高位 @return UINT - 返回要取位整数的低位或高位数据 */ int GetDoubleByteKey( int uData, UINT uKeyIndex ) { UINT uRet; uRet = (uData >> (uKeyIndex << 4 )) & 0xffff; return uRet; } 看cpu的核数了,2.26*8核,4G,4000万数据 需要1.2s。