Advertisement
Guest User

Untitled

a guest
Dec 20th, 2014
202
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 21.31 KB | None | 0 0
  1. package net.coderodde.util;
  2.  
  3. import java.util.ArrayList;
  4. import java.util.Collections;
  5. import java.util.Comparator;
  6. import java.util.List;
  7. import java.util.Objects;
  8.  
  9. public class CoderoddeArrays {
  10.  
  11. private static final int BITS_PER_BUCKET = 8;
  12. private static final int BUCKETS = 1 << BITS_PER_BUCKET;
  13. private static final int BUCKET_MASK = BUCKETS - 1;
  14. private static final long SIGN_MASK = 1L << 63;
  15. private static final int THREAD_THRESHOLD = 65536;
  16. private static final int MERGESORT_THRESHOLD = 4096;
  17.  
  18. public static <E> void parallelSort(final Entry<E>[] array) {
  19. parallelSort(array, 0, array.length);
  20. }
  21.  
  22. public static <E> void parallelSort(final Entry<E>[] array,
  23. final int fromIndex,
  24. final int toIndex) {
  25. final int RANGE_LENGTH = toIndex - fromIndex;
  26.  
  27. if (RANGE_LENGTH < 2) {
  28. return;
  29. }
  30.  
  31. final Entry<E>[] buffer = array.clone();
  32. final int threads = Math.min(RANGE_LENGTH / THREAD_THRESHOLD,
  33. Runtime.getRuntime()
  34. .availableProcessors());
  35. parallelSortImpl(array, buffer, threads, 0, fromIndex, toIndex);
  36. }
  37.  
  38. public static final <E> boolean areEqual(final Entry<E>[]... arrays) {
  39. for (int i = 0; i < arrays.length - 1; ++i) {
  40. if (arrays[i].length != arrays[i + 1].length) {
  41. return false;
  42. }
  43. }
  44.  
  45. for (int i = 0; i < arrays[0].length; ++i) {
  46. for (int j = 0; j < arrays.length - 1; ++j) {
  47. if (!Objects.equals(arrays[j][i], arrays[j + 1][i])) {
  48. return false;
  49. }
  50. }
  51. }
  52.  
  53. return true;
  54. }
  55.  
  56. public static final <E extends Comparable<? super E>>
  57. boolean isSorted(final E[] array,
  58. final int fromIndex,
  59. final int toIndex) {
  60. for (int i = fromIndex; i < toIndex - 1; ++i) {
  61. if (array[i].compareTo(array[i + 1]) > 0) {
  62. return false;
  63. }
  64. }
  65.  
  66. return true;
  67. }
  68.  
  69. public static final <E extends Comparable<? super E>>
  70. boolean isSorted(final E[] array) {
  71. return isSorted(array, 0, array.length);
  72. }
  73.  
  74. private static final <E> void sortImpl(final Entry<E>[] source,
  75. final Entry<E>[] target,
  76. final int recursionDepth,
  77. final int fromIndex,
  78. final int toIndex) {
  79. // Try merge sort.
  80. if (toIndex - fromIndex <= MERGESORT_THRESHOLD) {
  81. mergesortAndCleanUp(source,
  82. target,
  83. recursionDepth,
  84. fromIndex,
  85. toIndex);
  86. return;
  87. }
  88.  
  89. final int[] bucketSizeMap = new int[BUCKETS];
  90. final int[] startIndexMap = new int[BUCKETS];
  91. final int[] processedMap = new int[BUCKETS];
  92.  
  93. // Compute the size of each bucket.
  94. for (int i = fromIndex; i < toIndex; ++i) {
  95. bucketSizeMap[getBucket(source[i].key(), recursionDepth)]++;
  96. }
  97.  
  98. // Initialize the start index map.
  99. startIndexMap[0] = fromIndex;
  100.  
  101. // Compute the start index map in its entirety.
  102. for (int i = 1; i != BUCKETS; ++i) {
  103. startIndexMap[i] = startIndexMap[i - 1] +
  104. bucketSizeMap[i - 1];
  105. }
  106.  
  107. // Insert the entries from 'source' into their respective 'target'.
  108. for (int i = fromIndex; i < toIndex; ++i) {
  109. final Entry<E> e = source[i];
  110. final int index = getBucket(source[i].key(), recursionDepth);
  111. target[startIndexMap[index] + processedMap[index]++] = e;
  112. }
  113.  
  114. if (recursionDepth == 7) {
  115. // There is nowhere to recur, return.
  116. return;
  117. }
  118.  
  119. // Recur to sort each bucket.
  120. for (int i = 0; i != BUCKETS; ++i) {
  121. if (bucketSizeMap[i] != 0) {
  122. sortImpl(target,
  123. source,
  124. recursionDepth + 1,
  125. startIndexMap[i],
  126. startIndexMap[i] + bucketSizeMap[i]);
  127. }
  128. }
  129. }
  130.  
  131. private static final <E> boolean mergesort(final Entry<E>[] source,
  132. final Entry<E>[] target,
  133. final int fromIndex,
  134. final int toIndex) {
  135. final int RANGE_LENGTH = toIndex - fromIndex;
  136.  
  137. Entry<E>[] s = source;
  138. Entry<E>[] t = target;
  139.  
  140. int passes = 0;
  141.  
  142. for (int width = 1; width < RANGE_LENGTH; width <<= 1) {
  143. ++passes;
  144. int c = 0;
  145.  
  146. for (; c < RANGE_LENGTH / width; c += 2) {
  147. int left = fromIndex + c * width;
  148. int right = left + width;
  149. int i = left;
  150.  
  151. final int leftBound = right;
  152. final int rightBound = Math.min(toIndex, right + width);
  153.  
  154. while (left < leftBound && right < rightBound) {
  155. t[i++] = s[right].key() < s[left].key() ?
  156. s[right++] :
  157. s[left++];
  158. }
  159.  
  160. while (left < leftBound) { t[i++] = s[left++]; }
  161. while (right < rightBound) { t[i++] = s[right++]; }
  162. }
  163.  
  164. if (c * width < RANGE_LENGTH) {
  165. for (int i = fromIndex + c * width; i < toIndex; ++i) {
  166. t[i] = s[i];
  167. }
  168. }
  169.  
  170. final Entry<E>[] tmp = s;
  171. s = t;
  172. t = tmp;
  173. }
  174.  
  175. return (passes & 1) == 0;
  176. }
  177.  
  178. private static final <E>
  179. void mergesortAndCleanUp(final Entry<E>[] source,
  180. final Entry<E>[] target,
  181. final int recursionDepth,
  182. final int fromIndex,
  183. final int toIndex) {
  184. final boolean even = mergesort(source, target, fromIndex, toIndex);
  185.  
  186. if (even) {
  187. // source contains the sorted range.
  188. if ((recursionDepth & 1) == 1) {
  189. // source is buffer, copy to target.
  190. System.arraycopy(source,
  191. fromIndex,
  192. target,
  193. fromIndex,
  194. toIndex - fromIndex);
  195. }
  196. } else {
  197. // target contains the sorted range.
  198. if ((recursionDepth & 1) == 0) {
  199. // target is buffer, copy to source.
  200. System.arraycopy(target,
  201. fromIndex,
  202. source,
  203. fromIndex,
  204. toIndex - fromIndex);
  205. }
  206. }
  207. }
  208.  
  209. private static final class BucketSizeCounter<E> extends Thread {
  210.  
  211. int[] localBucketSizeMap;
  212. private final Entry<E>[] source;
  213. private final int recursionDepth;
  214. private final int fromIndex;
  215. private final int toIndex;
  216.  
  217. BucketSizeCounter(final Entry<E>[] source,
  218. final int recursionDepth,
  219. final int fromIndex,
  220. final int toIndex) {
  221. this.source = source;
  222. this.recursionDepth = recursionDepth;
  223. this.fromIndex = fromIndex;
  224. this.toIndex = toIndex;
  225. }
  226.  
  227. @Override
  228. public void run() {
  229. this.localBucketSizeMap = new int[BUCKETS];
  230.  
  231. for (int i = fromIndex; i < toIndex; ++i) {
  232. localBucketSizeMap[getBucket(source[i].key(),
  233. recursionDepth)]++;
  234. }
  235. }
  236. }
  237.  
  238. private static final class BucketInserter<E> extends Thread {
  239.  
  240. private final int[] startIndexMap;
  241. private final int[] processedMap;
  242. private final Entry<E>[] source;
  243. private final Entry<E>[] target;
  244. private final int recursionDepth;
  245. private final int fromIndex;
  246. private final int toIndex;
  247.  
  248. BucketInserter(final int[] startIndexMap,
  249. final int[] processedMap,
  250. final Entry<E>[] source,
  251. final Entry<E>[] target,
  252. final int recursionDepth,
  253. final int fromIndex,
  254. final int toIndex) {
  255. this.startIndexMap = startIndexMap;
  256. this.processedMap = processedMap;
  257. this.source = source;
  258. this.target = target;
  259. this.recursionDepth = recursionDepth;
  260. this.fromIndex = fromIndex;
  261. this.toIndex = toIndex;
  262. }
  263.  
  264. @Override
  265. public void run() {
  266. for (int i = fromIndex; i < toIndex; ++i) {
  267. final Entry<E> e = source[i];
  268. final int index = getBucket(e.key(), recursionDepth);
  269. target[startIndexMap[index] + processedMap[index]++] = e;
  270. }
  271. }
  272. }
  273.  
  274. private static final class Sorter<E> extends Thread {
  275.  
  276. private final List<Task<E>> taskList;
  277.  
  278. Sorter(final List<Task<E>> taskList) {
  279. this.taskList = taskList;
  280. }
  281.  
  282. @Override
  283. public void run() {
  284. for (final Task task : taskList) {
  285. // Choose parallel or sequential.
  286. if (task.threads > 1) {
  287. parallelSortImpl(task.source,
  288. task.target,
  289. task.threads,
  290. task.recursionDepth,
  291. task.fromIndex,
  292. task.toIndex);
  293. } else {
  294. sortImpl(task.source,
  295. task.target,
  296. task.recursionDepth,
  297. task.fromIndex,
  298. task.toIndex);
  299. }
  300. }
  301. }
  302. }
  303.  
  304. private static final class Task<E> {
  305.  
  306. private final Entry<E>[] source;
  307. private final Entry<E>[] target;
  308. private final int threads;
  309. private final int recursionDepth;
  310. private final int fromIndex;
  311. private final int toIndex;
  312.  
  313. Task(final Entry<E>[] source,
  314. final Entry<E>[] target,
  315. final int threads,
  316. final int recursionDepth,
  317. final int fromIndex,
  318. final int toIndex) {
  319. this.source = source;
  320. this.target = target;
  321. this.threads = threads;
  322. this.recursionDepth = recursionDepth;
  323. this.fromIndex = fromIndex;
  324. this.toIndex = toIndex;
  325. }
  326. }
  327.  
  328. private static final <E> void parallelSortImpl(final Entry<E>[] source,
  329. final Entry<E>[] target,
  330. final int threads,
  331. final int recursionDepth,
  332. final int fromIndex,
  333. final int toIndex) {
  334. final int RANGE_LENGTH = toIndex - fromIndex;
  335.  
  336. if (RANGE_LENGTH <= MERGESORT_THRESHOLD) {
  337. mergesortAndCleanUp(source,
  338. target,
  339. recursionDepth,
  340. fromIndex,
  341. toIndex);
  342. return;
  343. }
  344.  
  345. if (threads < 2) {
  346. sortImpl(source, target, recursionDepth, fromIndex, toIndex);
  347. return;
  348. }
  349.  
  350. // Create the bucket size counter threads.
  351. final BucketSizeCounter[] counters = new BucketSizeCounter[threads];
  352. final int SUB_RANGE_LENGTH = RANGE_LENGTH / threads;
  353. int start = fromIndex;
  354.  
  355. for (int i = 0; i != threads - 1; ++i, start += SUB_RANGE_LENGTH) {
  356. counters[i] = new BucketSizeCounter<>(source,
  357. recursionDepth,
  358. start,
  359. start + SUB_RANGE_LENGTH);
  360. counters[i].start();
  361. }
  362.  
  363. counters[threads - 1] =
  364. new BucketSizeCounter<>(source,
  365. recursionDepth,
  366. start,
  367. toIndex);
  368.  
  369. // Run the last counter in this thread while other are already on their
  370. // way.
  371. counters[threads - 1].run();
  372.  
  373. try {
  374. for (int i = 0; i != threads - 1; ++i) {
  375. counters[i].join();
  376. }
  377. } catch (final InterruptedException ie) {
  378. ie.printStackTrace();
  379. return;
  380. }
  381.  
  382. final int[] bucketSizeMap = new int[BUCKETS];
  383. final int[] startIndexMap = new int[BUCKETS];
  384.  
  385. // Count the size of each processed bucket.
  386. for (int i = 0; i != threads; ++i) {
  387. for (int j = 0; j != BUCKETS; ++j) {
  388. bucketSizeMap[j] += counters[i].localBucketSizeMap[j];
  389. }
  390. }
  391.  
  392. // Prepare the starting indices of each bucket.
  393. startIndexMap[0] = fromIndex;
  394.  
  395. for (int i = 1; i != BUCKETS; ++i) {
  396. startIndexMap[i] = startIndexMap[i - 1] +
  397. bucketSizeMap[i - 1];
  398. }
  399.  
  400. // Create the inserter threads.
  401. final BucketInserter<E>[] inserters = new BucketInserter[threads - 1];
  402. final int[][] processedMaps = new int[threads][BUCKETS];
  403.  
  404. // Make processedMaps of each thread independent of the other.
  405. for (int i = 1; i != threads; ++i) {
  406. int[] partialBucketSizeMap = counters[i - 1].localBucketSizeMap;
  407.  
  408. for (int j = 0; j != BUCKETS; ++j) {
  409. processedMaps[i][j] =
  410. processedMaps[i - 1][j] + partialBucketSizeMap[j];
  411. }
  412. }
  413.  
  414. int startIndex = fromIndex;
  415.  
  416. for (int i = 0; i != threads - 1; ++i, startIndex += SUB_RANGE_LENGTH) {
  417. inserters[i] =
  418. new BucketInserter<>(startIndexMap,
  419. processedMaps[i],
  420. source,
  421. target,
  422. recursionDepth,
  423. startIndex,
  424. startIndex + SUB_RANGE_LENGTH);
  425. inserters[i].start();
  426. }
  427.  
  428. // Run the last inserter in this thread while other are on their ways.
  429. new BucketInserter<>(startIndexMap,
  430. processedMaps[threads - 1],
  431. source,
  432. target,
  433. recursionDepth,
  434. startIndex,
  435. toIndex).run();
  436.  
  437. try {
  438. for (int i = 0; i != threads - 1; ++i) {
  439. inserters[i].join();
  440. }
  441. } catch (final InterruptedException ie) {
  442. ie.printStackTrace();
  443. return;
  444. }
  445.  
  446. if (recursionDepth == 7) {
  447. // Nowhere to recur.
  448. return;
  449. }
  450.  
  451. int nonEmptyBucketAmount = 0;
  452.  
  453. for (int i : bucketSizeMap) {
  454. if (i != 0) {
  455. ++nonEmptyBucketAmount;
  456. }
  457. }
  458.  
  459. final int SPAWN_DEGREE = Math.min(nonEmptyBucketAmount, threads);
  460. final List<Integer>[] bucketIndexListArray = new List[SPAWN_DEGREE];
  461.  
  462. for (int i = 0; i != SPAWN_DEGREE; ++i) {
  463. bucketIndexListArray[i] = new ArrayList<>(nonEmptyBucketAmount);
  464. }
  465.  
  466. final int[] threadCountMap = new int[SPAWN_DEGREE];
  467.  
  468. for (int i = 0; i != SPAWN_DEGREE; ++i) {
  469. threadCountMap[i] = threads / SPAWN_DEGREE;
  470. }
  471.  
  472. for (int i = 0; i != threads % SPAWN_DEGREE; ++i) {
  473. ++threadCountMap[i];
  474. }
  475.  
  476. final List<Integer> nonEmptyBucketIndices =
  477. new ArrayList<>(nonEmptyBucketAmount);
  478.  
  479.  
  480. for (int i = 0; i != BUCKETS; ++i) {
  481. if (bucketSizeMap[i] != 0) {
  482. nonEmptyBucketIndices.add(i);
  483. }
  484. }
  485.  
  486. Collections.sort(nonEmptyBucketIndices,
  487. new BucketSizeComparator(bucketSizeMap));
  488.  
  489. final int OPTIMAL_SUBRANGE_LENGTH = RANGE_LENGTH / SPAWN_DEGREE;
  490. int listIndex = 0;
  491. int packed = 0;
  492. int f = 0;
  493. int j = 0;
  494.  
  495. while (j < nonEmptyBucketIndices.size()) {
  496. int tmp = bucketSizeMap[nonEmptyBucketIndices.get(j++)];
  497. packed += tmp;
  498.  
  499. if (packed >= OPTIMAL_SUBRANGE_LENGTH
  500. || j == nonEmptyBucketIndices.size()) {
  501. packed = 0;
  502.  
  503. for (int i = f; i < j; ++i) {
  504. bucketIndexListArray[listIndex]
  505. .add(nonEmptyBucketIndices.get(i));
  506. }
  507.  
  508. ++listIndex;
  509. f = j;
  510. }
  511. }
  512.  
  513. final Sorter[] sorters = new Sorter[SPAWN_DEGREE];
  514. final List<List<Task<E>>> llt = new ArrayList<>(SPAWN_DEGREE);
  515.  
  516. for (int i = 0; i != SPAWN_DEGREE; ++i) {
  517. final List<Task<E>> lt = new ArrayList<>();
  518.  
  519. for (int idx : bucketIndexListArray[i]) {
  520. lt.add(new Task<>(target,
  521. source,
  522. threadCountMap[i],
  523. recursionDepth + 1,
  524. startIndexMap[idx],
  525. startIndexMap[idx] + bucketSizeMap[idx]));
  526. }
  527.  
  528. llt.add(lt);
  529. }
  530.  
  531. for (int i = 0; i != SPAWN_DEGREE - 1; ++i) {
  532. sorters[i] = new Sorter<>(llt.get(i));
  533. sorters[i].start();
  534. }
  535.  
  536. new Sorter<>(llt.get(SPAWN_DEGREE - 1)).run();
  537.  
  538. try {
  539. for (int i = 0; i != SPAWN_DEGREE - 1; ++i) {
  540. sorters[i].join();
  541. }
  542. } catch (final InterruptedException ie) {
  543. ie.printStackTrace();
  544. return;
  545. }
  546. }
  547.  
  548. private static final class BucketSizeComparator
  549. implements Comparator<Integer> {
  550. private final int[] bucketSizeMap;
  551.  
  552. BucketSizeComparator(final int[] bucketSizeMap) {
  553. this.bucketSizeMap = bucketSizeMap;
  554. }
  555.  
  556. @Override
  557. public int compare(final Integer i1, final Integer i2) {
  558. final int sz1 = bucketSizeMap[i1];
  559. final int sz2 = bucketSizeMap[i2];
  560. return sz2 - sz1;
  561. }
  562. }
  563.  
  564. private static final int getBucket(final long key,
  565. final int recursionDepth) {
  566. final int bitShift = 64 - (recursionDepth + 1) * BITS_PER_BUCKET;
  567. return (int)((key ^ SIGN_MASK) >>> bitShift) & BUCKET_MASK;
  568. }
  569. }
  570.  
  571. package net.coderodde.util;
  572.  
  573. public final class Entry<E> implements Comparable<Entry<E>> {
  574.  
  575. private final long key;
  576. private final E satelliteData;
  577.  
  578. public Entry(final long key, final E satelliteData) {
  579. this.key = key;
  580. this.satelliteData = satelliteData;
  581. }
  582.  
  583. public long key() {
  584. return key;
  585. }
  586.  
  587. public E satelliteData() {
  588. return satelliteData;
  589. }
  590.  
  591. @Override
  592. public int compareTo(Entry<E> o) {
  593. return Long.compare(key, o.key);
  594. }
  595. }
  596.  
  597. package net.coderodde.util;
  598.  
  599. import java.util.Arrays;
  600. import java.util.Random;
  601.  
  602. public class Demo {
  603.  
  604. private static final int N = 10000000;
  605.  
  606. public static void main(final String... args) {
  607. final long seed = System.currentTimeMillis();
  608. final Random rnd = new Random(seed);
  609. final Entry<Integer>[] array1 = getRandomEntryArray(N, rnd);
  610. final Entry<Integer>[] array2 = array1.clone();
  611. final Entry<Integer>[] array3 = array1.clone();
  612.  
  613. System.out.println("Seed: " + seed);
  614.  
  615. long ta = System.currentTimeMillis();
  616. net.coderodde.util.CoderoddeArrays.parallelSort(array1);
  617. long tb = System.currentTimeMillis();
  618.  
  619. System.out.println("net.coderodde.util.CoderoddeArrays.parallelSort " +
  620. "in " + (tb - ta) + " ms.");
  621.  
  622. ta = System.currentTimeMillis();
  623. Arrays.parallelSort(array2);
  624. tb = System.currentTimeMillis();
  625.  
  626. System.out.println("java.util.Arrays.parallelSort in " +
  627. (tb - ta) + " ms.");
  628.  
  629. ta = System.currentTimeMillis();
  630. Arrays.sort(array3);
  631. tb = System.currentTimeMillis();
  632.  
  633. System.out.println("java.util.Arrays.sort in " + (tb - ta) + " ms.");
  634.  
  635. System.out.println("Arrays are equal: " +
  636. CoderoddeArrays.areEqual(array1, array2, array3));
  637. System.out.println("Sorted: " + CoderoddeArrays.isSorted(array1));
  638. }
  639.  
  640. private static Entry<Integer>[] getRandomEntryArray(final int size,
  641. final Random rnd) {
  642. final Entry<Integer>[] array = new Entry[size];
  643.  
  644. for (int i = 0; i < size; ++i) {
  645. array[i] = new Entry<>(rnd.nextLong(), null);
  646. }
  647.  
  648. return array;
  649. }
  650. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement