/// <summary> /// Divide-and-Conquer Merge of two ranges of source array src[ p1 .. r1 ] and src[ p2 .. r2 ] into destination array starting at index p3. /// </summary> /// <typeparam name="T">data type of each array element</typeparam> /// <param name="src">source array</param> /// <param name="p1">starting index of the first segment, inclusive</param> /// <param name="r1">ending index of the first segment, inclusive</param> /// <param name="p2">starting index of the second segment, inclusive</param> /// <param name="r2">ending index of the second segment, inclusive</param> /// <param name="dst">destination array</param> /// <param name="p3">starting index of the result</param> /// <param name="comparer">method to compare array elements</param> internal static void MergeInnerPar <T>(T[] src, Int32 p1, Int32 r1, Int32 p2, Int32 r2, T[] dst, Int32 p3, IComparer <T> comparer = null) { //Console.WriteLine("#1 " + p1 + " " + r1 + " " + p2 + " " + r2); Int32 length1 = r1 - p1 + 1; Int32 length2 = r2 - p2 + 1; if (length1 < length2) { Algorithm.Swap(ref p1, ref p2); Algorithm.Swap(ref r1, ref r2); Algorithm.Swap(ref length1, ref length2); } if (length1 == 0) { return; } if ((length1 + length2) <= MergeParallelArrayThreshold) { // 8192 threshold is much better than 16 (is it for C#) //Console.WriteLine("#3 " + p1 + " " + length1 + " " + p2 + " " + length2 + " " + p3); HPCsharp.Algorithm.Merge <T>(src, p1, length1, src, p2, length2, dst, p3, comparer); // in Dr. Dobb's Journal paper } else { Int32 q1 = (p1 + r1) / 2; Int32 q2 = Algorithm.BinarySearch(src[q1], src, p2, r2, comparer); Int32 q3 = p3 + (q1 - p1) + (q2 - p2); dst[q3] = src[q1]; Parallel.Invoke( () => { MergeInnerPar <T>(src, p1, q1 - 1, p2, q2 - 1, dst, p3, comparer); }, () => { MergeInnerPar <T>(src, q1 + 1, r1, q2, r2, dst, q3 + 1, comparer); } ); } }
// TODO: Figure out why this is so slow and does not accelerate /// <summary> /// Divide-and-Conquer Merge of two ranges of source List src[ p1 .. r1 ] and src[ p2 .. r2 ] into destination List starting at index p3. /// </summary> /// <typeparam name="T">data type of each List element</typeparam> /// <param name="src">source List</param> /// <param name="p1">starting index of the first segment, inclusive</param> /// <param name="r1">ending index of the first segment, inclusive</param> /// <param name="p2">starting index of the second segment, inclusive</param> /// <param name="r2">ending index of the second segment, inclusive</param> /// <param name="dst">destination List</param> /// <param name="p3">starting index of the result</param> /// <param name="comparer">method to compare array elements</param> public static void MergeParallel <T>(List <T> src, Int32 p1, Int32 r1, Int32 p2, Int32 r2, List <T> dst, Int32 p3, Comparer <T> comparer = null) { Int32 length1 = r1 - p1 + 1; Int32 length2 = r2 - p2 + 1; if (length1 < length2) { Exchange(ref p1, ref p2); Exchange(ref r1, ref r2); Exchange(ref length1, ref length2); } if (length1 == 0) { return; } if ((length1 + length2) <= MergeParallelListThreshold) { // 8192 threshold is much better than 16 (is it for C#) HPCsharp.Algorithm.Merge <T>(src, p1, p1 + length1, src, p2, p2 + length2, dst, p3, comparer); // in DDJ paper } else { Int32 q1 = (p1 + r1) / 2; Int32 q2 = Algorithm.BinarySearch(src[q1], src, p2, r2); Int32 q3 = p3 + (q1 - p1) + (q2 - p2); dst[q3] = src[q1]; Parallel.Invoke( () => { MergeParallel <T>(src, p1, q1 - 1, p2, q2 - 1, dst, p3, comparer); }, () => { MergeParallel <T>(src, q1 + 1, r1, q2, r2, dst, q3 + 1, comparer); } ); } }
/// <summary> /// Divide-and-Conquer Merge of two ranges of source array src[ p1 .. r1 ] and src[ p2 .. r2 ] into destination array starting at index p3. /// This merge is not stable. /// </summary> /// <typeparam name="T">data type of each array element</typeparam> /// <param name="src">source array</param> /// <param name="aStart">starting index of the first segment, inclusive</param> /// <param name="aEnd">ending index of the first segment, inclusive</param> /// <param name="bStart">starting index of the second segment, inclusive</param> /// <param name="bEnd">ending index of the second segment, inclusive</param> /// <param name="dst">destination array</param> /// <param name="p3">starting index of the result</param> /// <param name="comparer">method to compare array elements</param> public static void MergeDivideAndConquer <T>(T[] src, Int32 aStart, Int32 aEnd, Int32 bStart, Int32 bEnd, T[] dst, Int32 p3, IComparer <T> comparer = null) { //Console.WriteLine("#1 " + aStart + " " + aEnd + " " + bStart + " " + bEnd); Int32 length1 = aEnd - aStart + 1; Int32 length2 = bEnd - bStart + 1; if (length1 < length2) { HPCsharp.ParallelAlgorithm.Exchange(ref aStart, ref bStart); HPCsharp.ParallelAlgorithm.Exchange(ref aEnd, ref bEnd); HPCsharp.ParallelAlgorithm.Exchange(ref length1, ref length2); } if (length1 == 0) { return; } if ((length1 + length2) <= MergeArrayThreshold) { //Console.WriteLine("Merge: aStart = {0} length1 = {1} bStart = {2} length2 = {3} p3 = {2}", aStart, length1, bStart, length2, p3); Merge <T>(src, aStart, length1, src, bStart, length2, dst, p3, comparer); // in Dr. Dobb's Journal paper } else { Int32 q1 = (aStart + aEnd) / 2; Int32 q2 = Algorithm.BinarySearch(src[q1], src, bStart, bEnd, comparer); Int32 q3 = p3 + (q1 - aStart) + (q2 - bStart); dst[q3] = src[q1]; MergeDivideAndConquer(src, aStart, q1 - 1, bStart, q2 - 1, dst, p3, comparer); // lower half MergeDivideAndConquer(src, q1 + 1, aEnd, q2, bEnd, dst, q3 + 1, comparer); // upper half } }
/// <summary> /// Divide-and-Conquer Merge of two ranges of source array src[ p1 .. r1 ] and src[ p2 .. r2 ] into destination array starting at index p3. /// </summary> /// <typeparam name="T">data type of each array element</typeparam> /// <param name="src">source array</param> /// <param name="p1">starting index of the first segment, inclusive</param> /// <param name="r1">ending index of the first segment, inclusive</param> /// <param name="p2">starting index of the second segment, inclusive</param> /// <param name="r2">ending index of the second segment, inclusive</param> /// <param name="dst">destination array</param> /// <param name="p3">starting index of the result</param> /// <param name="comparer">method to compare array elements</param> internal static void MergeInnerFasterPar <T>(T[] src, Int32 p1, Int32 r1, Int32 p2, Int32 r2, T[] dst, Int32 p3, IComparer <T> comparer = null, Int32 mergeParallelThreshold = 128 * 1024) { //Console.WriteLine("#1 " + p1 + " " + r1 + " " + p2 + " " + r2); Int32 length1 = r1 - p1 + 1; Int32 length2 = r2 - p2 + 1; if (length1 < length2) { Algorithm.Swap(ref p1, ref p2); Algorithm.Swap(ref r1, ref r2); Algorithm.Swap(ref length1, ref length2); } if (length1 == 0) { return; } if ((length1 + length2) <= mergeParallelThreshold) { //Console.WriteLine("#3 " + p1 + " " + length1 + " " + p2 + " " + length2 + " " + p3); HPCsharp.Algorithm.MergeFaster <T>(src, p1, length1, src, p2, length2, dst, p3, comparer); } else { Int32 q1 = (p1 + r1) / 2; Int32 q2 = Algorithm.BinarySearch(src[q1], src, p2, r2, comparer); Int32 q3 = p3 + (q1 - p1) + (q2 - p2); dst[q3] = src[q1]; Parallel.Invoke( () => { MergeInnerFasterPar <T>(src, p1, q1 - 1, p2, q2 - 1, dst, p3, comparer, mergeParallelThreshold); }, () => { MergeInnerFasterPar <T>(src, q1 + 1, r1, q2, r2, dst, q3 + 1, comparer, mergeParallelThreshold); } ); } }
// Merge two ranges of source array T[ l .. m, m+1 .. r ] in-place. // Based on not-in-place algorithm in 3rd ed. of "Introduction to Algorithms" p. 798-802, extending it to be in-place // and my Dr. Dobb's paper https://www.drdobbs.com/parallel/parallel-in-place-merge/240008783 public static void MergeDivideAndConquerInPlacePar <T>(T[] arr, int startIndex, int midIndex, int endIndex, IComparer <T> comparer = null, int threshold0 = 16 * 1024, int threshold1 = 16 * 1024) { //Console.WriteLine("MergeDivideAndConquerInPlacePar: start = {0}, mid = {1}, end = {2}", startIndex, midIndex, endIndex); int length1 = midIndex - startIndex + 1; int length2 = endIndex - midIndex; if (length1 >= length2) { if (length2 <= 0) { return; // if the smaller segment has zero elements, then nothing to merge } int q1 = (startIndex + midIndex) / 2; // q1 is mid-point of the larger segment. length1 >= length2 > 0 int q2 = Algorithm.BinarySearch(arr[q1], arr, midIndex + 1, endIndex, comparer); // q2 is q1 partitioning element within the smaller sub-array (and q2 itself is part of the sub-array that does not move) int q3 = q1 + (q2 - midIndex - 1); //BlockSwapReversalPar(arr, q1, midIndex, q2 - 1, threshold0); //Algorithm.BlockSwapReversal(arr, q1, midIndex, q2 - 1); Algorithm.BlockSwapGriesMills(arr, q1, midIndex, q2 - 1); if (length1 < threshold1) { MergeDivideAndConquerInPlacePar(arr, startIndex, q1 - 1, q3 - 1, comparer); MergeDivideAndConquerInPlacePar(arr, q3 + 1, q2 - 1, endIndex, comparer); } else { Parallel.Invoke( () => { MergeDivideAndConquerInPlacePar(arr, startIndex, q1 - 1, q3 - 1, comparer); }, // note that q3 is now in its final place and no longer participates in further processing () => { MergeDivideAndConquerInPlacePar(arr, q3 + 1, q2 - 1, endIndex, comparer); } ); } } else { // length1 < length2 if (length1 <= 0) { return; // if the smaller segment has zero elements, then nothing to merge } int q1 = (midIndex + 1 + endIndex) / 2; // q1 is mid-point of the larger segment. length2 > length1 > 0 int q2 = Algorithm.BinarySearch(arr[q1], arr, startIndex, midIndex, comparer); // q2 is q1 partitioning element within the smaller sub-array (and q2 itself is part of the sub-array that does not move) int q3 = q2 + (q1 - midIndex - 1); //BlockSwapReversalPar(arr, q2, midIndex, q1, threshold0); //Algorithm.BlockSwapReversal(arr, q2, midIndex, q1); Algorithm.BlockSwapGriesMills(arr, q2, midIndex, q1); if (length1 < threshold1) { MergeDivideAndConquerInPlacePar(arr, startIndex, q2 - 1, q3 - 1, comparer); MergeDivideAndConquerInPlacePar(arr, q3 + 1, q1, endIndex, comparer); } else { Parallel.Invoke( () => { MergeDivideAndConquerInPlacePar(arr, startIndex, q2 - 1, q3 - 1, comparer); }, // note that q3 is now in its final place and no longer participates in further processing () => { MergeDivideAndConquerInPlacePar(arr, q3 + 1, q1, endIndex, comparer); } ); } } }
/// <summary> /// Divide-and-Conquer Merge of two ranges of source array src[ p1 .. r1 ] and src[ p2 .. r2 ] into destination array starting at index p3. /// </summary> /// <typeparam name="T">data type of each array element</typeparam> /// <param name="srcKeys">source array of keys used for sorting</param> /// <param name="srcItems">source array of items that will be sorted along with keys</param> /// <param name="p1">starting index of the first segment, inclusive</param> /// <param name="r1">ending index of the first segment, inclusive</param> /// <param name="p2">starting index of the second segment, inclusive</param> /// <param name="r2">ending index of the second segment, inclusive</param> /// <param name="dstKeys">destination array of sorted keys</param> /// <param name="dstItems">destination array of sorted items</param> /// <param name="p3">starting index of the result</param> /// <param name="comparer">method to compare array elements</param> internal static void MergeInnerPar <T1, T2>(T1[] srcKeys, T2[] srcItems, Int32 p1, Int32 r1, Int32 p2, Int32 r2, T1[] dstKeys, T2[] dstItems, Int32 p3, IComparer <T1> comparer = null) { //Console.WriteLine("merge: #1 " + p1 + " " + r1 + " " + p2 + " " + r2); Int32 length1 = r1 - p1 + 1; Int32 length2 = r2 - p2 + 1; if (length1 < length2) { Algorithm.Swap(ref p1, ref p2); Algorithm.Swap(ref r1, ref r2); Algorithm.Swap(ref length1, ref length2); } if (length1 == 0) { return; } if ((length1 + length2) <= MergeParallelArrayThreshold) { //Console.WriteLine("merge: #3 " + p1 + " " + length1 + " " + p2 + " " + length2 + " " + p3); HPCsharp.Algorithm.Merge <T1, T2>(srcKeys, srcItems, p1, length1, srcKeys, srcItems, p2, length2, dstKeys, dstItems, p3, comparer); } else { Int32 q1 = (p1 + r1) / 2; Int32 q2 = Algorithm.BinarySearch(srcKeys[q1], srcKeys, p2, r2, comparer); Int32 q3 = p3 + (q1 - p1) + (q2 - p2); dstKeys[q3] = srcKeys[q1]; dstItems[q3] = srcItems[q1]; Parallel.Invoke( () => { MergeInnerPar <T1, T2>(srcKeys, srcItems, p1, q1 - 1, p2, q2 - 1, dstKeys, dstItems, p3, comparer); }, () => { MergeInnerPar <T1, T2>(srcKeys, srcItems, q1 + 1, r1, q2, r2, dstKeys, dstItems, q3 + 1, comparer); } ); } }