Path: blob/master/test/jdk/java/util/Arrays/SortingIntBenchmarkTestJMH.java
41152 views
/*1* Copyright 2015 Goldman Sachs.2* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.3* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.4*5* This code is free software; you can redistribute it and/or modify it6* under the terms of the GNU General Public License version 2 only, as7* published by the Free Software Foundation.8*9* This code is distributed in the hope that it will be useful, but WITHOUT10* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or11* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License12* version 2 for more details (a copy is included in the LICENSE file that13* accompanied this code).14*15* You should have received a copy of the GNU General Public License version16* 2 along with this work; if not, write to the Free Software Foundation,17* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.18*19* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA20* or visit www.oracle.com if you need additional information or have any21* questions.22*/2324import org.openjdk.jmh.annotations.Benchmark;25import org.openjdk.jmh.annotations.BenchmarkMode;26import org.openjdk.jmh.annotations.Measurement;27import org.openjdk.jmh.annotations.Mode;28import org.openjdk.jmh.annotations.OutputTimeUnit;29import org.openjdk.jmh.annotations.Param;30import org.openjdk.jmh.annotations.Scope;31import org.openjdk.jmh.annotations.Setup;32import org.openjdk.jmh.annotations.State;33import org.openjdk.jmh.annotations.Warmup;3435import java.util.ArrayList;36import java.util.Arrays;37import java.util.HashSet;38import java.util.List;39import java.util.Random;40import java.util.Set;41import java.util.concurrent.TimeUnit;4243@State(Scope.Thread)44@BenchmarkMode(Mode.Throughput)45@OutputTimeUnit(TimeUnit.SECONDS)46public class SortingIntBenchmarkTestJMH {47private static final int QUICKSORT_THRESHOLD = 286;48private static final int MAX_RUN_COUNT = 67;49private static final int INSERTION_SORT_THRESHOLD = 47;50public static final int MAX_VALUE = 1_000_000;5152@Param({"pairFlipZeroPairFlip", "pairFlipOneHundredPairFlip"53, "zeroHi", "hiZeroLow", "hiFlatLow", "identical",54"randomDups", "randomNoDups", "sortedReversedSorted", "pairFlip", "endLessThan"})5556public String listType;5758private int[] array;59private static final int LIST_SIZE = 10_000_000;60public static final int NUMBER_OF_ITERATIONS = 10;6162@Setup63public void setUp() {64Random random = new Random(123456789012345L);65this.array = new int[LIST_SIZE];66int threeQuarters = (int) (LIST_SIZE * 0.75);67if ("zeroHi".equals(this.listType)) {68for (int i = 0; i < threeQuarters; i++) {69this.array[i] = 0;70}71int k = 1;72for (int i = threeQuarters; i < LIST_SIZE; i++) {73this.array[i] = k;74k++;75}76}77else if ("hiFlatLow".equals(this.listType)) {78int oneThird = LIST_SIZE / 3;79for (int i = 0; i < oneThird; i++) {80this.array[i] = i;81}82int twoThirds = oneThird * 2;83int constant = oneThird - 1;84for (int i = oneThird; i < twoThirds; i++) {85this.array[i] = constant;86}87for (int i = twoThirds; i < LIST_SIZE; i++) {88this.array[i] = constant - i + twoThirds;89}90}91else if ("hiZeroLow".equals(this.listType)) {92int oneThird = LIST_SIZE / 3;93for (int i = 0; i < oneThird; i++) {94this.array[i] = i;95}96int twoThirds = oneThird * 2;97for (int i = oneThird; i < twoThirds; i++) {98this.array[i] = 0;99}100for (int i = twoThirds; i < LIST_SIZE; i++) {101this.array[i] = oneThird - i + twoThirds;102}103}104else if ("identical".equals(this.listType)) {105for (int i = 0; i < LIST_SIZE; i++) {106this.array[i] = 0;107}108}109else if ("randomDups".equals(this.listType)) {110for (int i = 0; i < LIST_SIZE; i++) {111this.array[i] = random.nextInt(1000);112}113}114else if ("randomNoDups".equals(this.listType)) {115Set<Integer> set = new HashSet();116while (set.size() < LIST_SIZE + 1) {117set.add(random.nextInt());118}119List<Integer> list = new ArrayList<>(LIST_SIZE);120list.addAll(set);121for (int i = 0; i < LIST_SIZE; i++) {122this.array[i] = list.get(i);123}124}125else if ("sortedReversedSorted".equals(this.listType)) {126for (int i = 0; i < LIST_SIZE / 2; i++) {127this.array[i] = i;128}129int num = 0;130for (int i = LIST_SIZE / 2; i < LIST_SIZE; i++) {131this.array[i] = LIST_SIZE - num;132num++;133}134}135else if ("pairFlip".equals(this.listType)) {136for (int i = 0; i < LIST_SIZE; i++) {137this.array[i] = i;138}139for (int i = 0; i < LIST_SIZE; i += 2) {140int temp = this.array[i];141this.array[i] = this.array[i + 1];142this.array[i + 1] = temp;143}144}145else if ("endLessThan".equals(this.listType)) {146for (int i = 0; i < LIST_SIZE - 1; i++) {147this.array[i] = 3;148}149this.array[LIST_SIZE - 1] = 1;150}151else if ("pairFlipZeroPairFlip".equals(this.listType)) {152//pairflip153for (int i = 0; i < 64; i++) {154this.array[i] = i;155}156for (int i = 0; i < 64; i += 2) {157int temp = this.array[i];158this.array[i] = this.array[i + 1];159this.array[i + 1] = temp;160}161//zero162for (int i = 64; i < this.array.length - 64; i++) {163this.array[i] = 0;164}165//pairflip166for (int i = this.array.length - 64; i < this.array.length; i++) {167this.array[i] = i;168}169for (int i = this.array.length - 64; i < this.array.length; i += 2) {170int temp = this.array[i];171this.array[i] = this.array[i + 1];172this.array[i + 1] = temp;173}174}175else if ("pairFlipOneHundredPairFlip".equals(this.listType)) {176//10, 5177for (int i = 0; i < 64; i++) {178if (i % 2 == 0) {179this.array[i] = 10;180}181else {182this.array[i] = 5;183}184}185186//100187for (int i = 64; i < this.array.length - 64; i++) {188this.array[i] = 100;189}190191//10, 5192for (int i = this.array.length - 64; i < this.array.length; i++) {193if (i % 2 == 0) {194this.array[i] = 10;195}196else {197this.array[i] = 5;198}199}200}201}202203@Warmup(iterations = 20)204@Measurement(iterations = 10)205@Benchmark206public void sortNewWay() {207for (int i = 0; i < NUMBER_OF_ITERATIONS; i++) {208SortingIntTestJMH.sort(this.array, 0, this.array.length - 1, null, 0, 0);209}210}211212@Warmup(iterations = 20)213@Measurement(iterations = 10)214@Benchmark215public void sortCurrentWay() {216for (int i = 0; i < NUMBER_OF_ITERATIONS; i++) {217Arrays.sort(this.array);218}219}220221static void sort(int[] a, int left, int right,222int[] work, int workBase, int workLen) {223// Use Quicksort on small arrays224if (right - left < QUICKSORT_THRESHOLD) {225SortingIntTestJMH.sort(a, left, right, true);226return;227}228229/*230* Index run[i] is the start of i-th run231* (ascending or descending sequence).232*/233int[] run = new int[MAX_RUN_COUNT + 1];234int count = 0;235run[0] = left;236237// Check if the array is nearly sorted238for (int k = left; k < right; run[count] = k) {239while (k < right && a[k] == a[k + 1])240k++;241if (k == right) break;242if (a[k] < a[k + 1]) { // ascending243while (++k <= right && a[k - 1] <= a[k]) ;244}245else if (a[k] > a[k + 1]) { // descending246while (++k <= right && a[k - 1] >= a[k]) ;247for (int lo = run[count] - 1, hi = k; ++lo < --hi; ) {248int t = a[lo];249a[lo] = a[hi];250a[hi] = t;251}252}253if (run[count] > left && a[run[count]] >= a[run[count] - 1]) {254count--;255}256/*257* The array is not highly structured,258* use Quicksort instead of merge sort.259*/260if (++count == MAX_RUN_COUNT) {261sort(a, left, right, true);262return;263}264}265266// Check special cases267// Implementation note: variable "right" is increased by 1.268if (run[count] == right++) {269run[++count] = right;270}271if (count <= 1) { // The array is already sorted272return;273}274275// Determine alternation base for merge276byte odd = 0;277for (int n = 1; (n <<= 1) < count; odd ^= 1) {278}279280// Use or create temporary array b for merging281int[] b; // temp array; alternates with a282int ao, bo; // array offsets from 'left'283int blen = right - left; // space needed for b284if (work == null || workLen < blen || workBase + blen > work.length) {285work = new int[blen];286workBase = 0;287}288if (odd == 0) {289System.arraycopy(a, left, work, workBase, blen);290b = a;291bo = 0;292a = work;293ao = workBase - left;294}295else {296b = work;297ao = 0;298bo = workBase - left;299}300301// Merging302for (int last; count > 1; count = last) {303for (int k = (last = 0) + 2; k <= count; k += 2) {304int hi = run[k], mi = run[k - 1];305for (int i = run[k - 2], p = i, q = mi; i < hi; ++i) {306if (q >= hi || p < mi && a[p + ao] <= a[q + ao]) {307b[i + bo] = a[p++ + ao];308}309else {310b[i + bo] = a[q++ + ao];311}312}313run[++last] = hi;314}315if ((count & 1) != 0) {316for (int i = right, lo = run[count - 1]; --i >= lo;317b[i + bo] = a[i + ao]318) {319}320run[++last] = right;321}322int[] t = a;323a = b;324b = t;325int o = ao;326ao = bo;327bo = o;328}329}330331private static void sort(int[] a, int left, int right, boolean leftmost) {332int length = right - left + 1;333334// Use insertion sort on tiny arrays335if (length < INSERTION_SORT_THRESHOLD) {336if (leftmost) {337/*338* Traditional (without sentinel) insertion sort,339* optimized for server VM, is used in case of340* the leftmost part.341*/342for (int i = left, j = i; i < right; j = ++i) {343int ai = a[i + 1];344while (ai < a[j]) {345a[j + 1] = a[j];346if (j-- == left) {347break;348}349}350a[j + 1] = ai;351}352}353else {354/*355* Skip the longest ascending sequence.356*/357do {358if (left >= right) {359return;360}361}362while (a[++left] >= a[left - 1]);363364/*365* Every element from adjoining part plays the role366* of sentinel, therefore this allows us to avoid the367* left range check on each iteration. Moreover, we use368* the more optimized algorithm, so called pair insertion369* sort, which is faster (in the context of Quicksort)370* than traditional implementation of insertion sort.371*/372for (int k = left; ++left <= right; k = ++left) {373int a1 = a[k], a2 = a[left];374375if (a1 < a2) {376a2 = a1;377a1 = a[left];378}379while (a1 < a[--k]) {380a[k + 2] = a[k];381}382a[++k + 1] = a1;383384while (a2 < a[--k]) {385a[k + 1] = a[k];386}387a[k + 1] = a2;388}389int last = a[right];390391while (last < a[--right]) {392a[right + 1] = a[right];393}394a[right + 1] = last;395}396return;397}398399// Inexpensive approximation of length / 7400int seventh = (length >> 3) + (length >> 6) + 1;401402/*403* Sort five evenly spaced elements around (and including) the404* center element in the range. These elements will be used for405* pivot selection as described below. The choice for spacing406* these elements was empirically determined to work well on407* a wide variety of inputs.408*/409int e3 = (left + right) >>> 1; // The midpoint410int e2 = e3 - seventh;411int e1 = e2 - seventh;412int e4 = e3 + seventh;413int e5 = e4 + seventh;414415// Sort these elements using insertion sort416if (a[e2] < a[e1]) {417int t = a[e2];418a[e2] = a[e1];419a[e1] = t;420}421422if (a[e3] < a[e2]) {423int t = a[e3];424a[e3] = a[e2];425a[e2] = t;426if (t < a[e1]) {427a[e2] = a[e1];428a[e1] = t;429}430}431if (a[e4] < a[e3]) {432int t = a[e4];433a[e4] = a[e3];434a[e3] = t;435if (t < a[e2]) {436a[e3] = a[e2];437a[e2] = t;438if (t < a[e1]) {439a[e2] = a[e1];440a[e1] = t;441}442}443}444if (a[e5] < a[e4]) {445int t = a[e5];446a[e5] = a[e4];447a[e4] = t;448if (t < a[e3]) {449a[e4] = a[e3];450a[e3] = t;451if (t < a[e2]) {452a[e3] = a[e2];453a[e2] = t;454if (t < a[e1]) {455a[e2] = a[e1];456a[e1] = t;457}458}459}460}461462// Pointers463int less = left; // The index of the first element of center part464int great = right; // The index before the first element of right part465466if (a[e1] != a[e2] && a[e2] != a[e3] && a[e3] != a[e4] && a[e4] != a[e5]) {467/*468* Use the second and fourth of the five sorted elements as pivots.469* These values are inexpensive approximations of the first and470* second terciles of the array. Note that pivot1 <= pivot2.471*/472int pivot1 = a[e2];473int pivot2 = a[e4];474475/*476* The first and the last elements to be sorted are moved to the477* locations formerly occupied by the pivots. When partitioning478* is complete, the pivots are swapped back into their final479* positions, and excluded from subsequent sorting.480*/481a[e2] = a[left];482a[e4] = a[right];483484/*485* Skip elements, which are less or greater than pivot values.486*/487while (a[++less] < pivot1) {488}489while (a[--great] > pivot2) {490}491492/*493* Partitioning:494*495* left part center part right part496* +--------------------------------------------------------------+497* | < pivot1 | pivot1 <= && <= pivot2 | ? | > pivot2 |498* +--------------------------------------------------------------+499* ^ ^ ^500* | | |501* less k great502*503* Invariants:504*505* all in (left, less) < pivot1506* pivot1 <= all in [less, k) <= pivot2507* all in (great, right) > pivot2508*509* Pointer k is the first index of ?-part.510*/511outer:512for (int k = less - 1; ++k <= great; ) {513int ak = a[k];514if (ak < pivot1) { // Move a[k] to left part515a[k] = a[less];516/*517* Here and below we use "a[i] = b; i++;" instead518* of "a[i++] = b;" due to performance issue.519*/520a[less] = ak;521++less;522}523else if (ak > pivot2) { // Move a[k] to right part524while (a[great] > pivot2) {525if (great-- == k) {526break outer;527}528}529if (a[great] < pivot1) { // a[great] <= pivot2530a[k] = a[less];531a[less] = a[great];532++less;533}534else { // pivot1 <= a[great] <= pivot2535a[k] = a[great];536}537/*538* Here and below we use "a[i] = b; i--;" instead539* of "a[i--] = b;" due to performance issue.540*/541a[great] = ak;542--great;543}544}545546// Swap pivots into their final positions547a[left] = a[less - 1];548a[less - 1] = pivot1;549a[right] = a[great + 1];550a[great + 1] = pivot2;551552// Sort left and right parts recursively, excluding known pivots553SortingIntTestJMH.sort(a, left, less - 2, leftmost);554SortingIntTestJMH.sort(a, great + 2, right, false);555556/*557* If center part is too large (comprises > 4/7 of the array),558* swap internal pivot values to ends.559*/560if (less < e1 && e5 < great) {561/*562* Skip elements, which are equal to pivot values.563*/564while (a[less] == pivot1) {565++less;566}567568while (a[great] == pivot2) {569--great;570}571572/*573* Partitioning:574*575* left part center part right part576* +----------------------------------------------------------+577* | == pivot1 | pivot1 < && < pivot2 | ? | == pivot2 |578* +----------------------------------------------------------+579* ^ ^ ^580* | | |581* less k great582*583* Invariants:584*585* all in (*, less) == pivot1586* pivot1 < all in [less, k) < pivot2587* all in (great, *) == pivot2588*589* Pointer k is the first index of ?-part.590*/591outer:592for (int k = less - 1; ++k <= great; ) {593int ak = a[k];594if (ak == pivot1) { // Move a[k] to left part595a[k] = a[less];596a[less] = ak;597++less;598}599else if (ak == pivot2) { // Move a[k] to right part600while (a[great] == pivot2) {601if (great-- == k) {602break outer;603}604}605if (a[great] == pivot1) { // a[great] < pivot2606a[k] = a[less];607/*608* Even though a[great] equals to pivot1, the609* assignment a[less] = pivot1 may be incorrect,610* if a[great] and pivot1 are floating-point zeros611* of different signs. Therefore in float and612* double sorting methods we have to use more613* accurate assignment a[less] = a[great].614*/615a[less] = pivot1;616++less;617}618else { // pivot1 < a[great] < pivot2619a[k] = a[great];620}621a[great] = ak;622--great;623}624}625}626627// Sort center part recursively628SortingIntTestJMH.sort(a, less, great, false);629}630else { // Partitioning with one pivot631/*632* Use the third of the five sorted elements as pivot.633* This value is inexpensive approximation of the median.634*/635int pivot = a[e3];636637/*638* Partitioning degenerates to the traditional 3-way639* (or "Dutch National Flag") schema:640*641* left part center part right part642* +-------------------------------------------------+643* | < pivot | == pivot | ? | > pivot |644* +-------------------------------------------------+645* ^ ^ ^646* | | |647* less k great648*649* Invariants:650*651* all in (left, less) < pivot652* all in [less, k) == pivot653* all in (great, right) > pivot654*655* Pointer k is the first index of ?-part.656*/657for (int k = less; k <= great; ++k) {658if (a[k] == pivot) {659continue;660}661int ak = a[k];662if (ak < pivot) { // Move a[k] to left part663a[k] = a[less];664a[less] = ak;665++less;666}667else { // a[k] > pivot - Move a[k] to right part668while (a[great] > pivot) {669--great;670}671if (a[great] < pivot) { // a[great] <= pivot672a[k] = a[less];673a[less] = a[great];674++less;675}676else { // a[great] == pivot677/*678* Even though a[great] equals to pivot, the679* assignment a[k] = pivot may be incorrect,680* if a[great] and pivot are floating-point681* zeros of different signs. Therefore in float682* and double sorting methods we have to use683* more accurate assignment a[k] = a[great].684*/685a[k] = pivot;686}687a[great] = ak;688--great;689}690}691692/*693* Sort left and right parts recursively.694* All elements from center part are equal695* and, therefore, already sorted.696*/697SortingIntTestJMH.sort(a, left, less - 1, leftmost);698SortingIntTestJMH.sort(a, great + 1, right, false);699}700}701702private static void swap(int[] arr, int i, int j) {703int tmp = arr[i];704arr[i] = arr[j];705arr[j] = tmp;706}707}708709710