edu.mit.streamjit.test.apps.bitonicsort.BitonicSort.java Source code

Java tutorial

Introduction

Here is the source code for edu.mit.streamjit.test.apps.bitonicsort.BitonicSort.java

Source

/*
 * Copyright (c) 2013-2014 Massachusetts Institute of Technology
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package edu.mit.streamjit.test.apps.bitonicsort;

import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.jeffreybosboom.serviceproviderprocessor.ServiceProvider;
import edu.mit.streamjit.api.Filter;
import edu.mit.streamjit.api.Input;
import edu.mit.streamjit.api.Pipeline;
import edu.mit.streamjit.api.RoundrobinJoiner;
import edu.mit.streamjit.api.RoundrobinSplitter;
import edu.mit.streamjit.api.Splitjoin;
import edu.mit.streamjit.api.StreamCompiler;
import edu.mit.streamjit.impl.blob.Buffer;
import edu.mit.streamjit.impl.common.InputBufferFactory;
import edu.mit.streamjit.impl.compiler2.Compiler2StreamCompiler;
import edu.mit.streamjit.test.SuppliedBenchmark;
import edu.mit.streamjit.test.Benchmark;
import edu.mit.streamjit.test.Datasets;
import edu.mit.streamjit.test.Benchmark.Dataset;
import edu.mit.streamjit.test.BenchmarkProvider;
import edu.mit.streamjit.test.Benchmarker;
import java.nio.ByteOrder;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

/**
 * Moved from StreamIt's asplos06 benchmark. Refer STREAMIT_HOME/apps/benchmarks/asplos06/bitonic-sort/streamit/BitonicSort2.str for
 * original implementations.
 * @author Sumanan sumanan@mit.edu
 * @since Mar 12, 2013
 */

/**
 * BitonicSort.java - Batcher's bitonic sort network Implementation works only
 * for power-of-2 sizes starting from 2.
 *
 * <b>Note:</b>
 * <ol>
 * <li>Each input element is also referred to as a key in the comments in this
 * file.
 * <li>BitonicSort of N keys is done using logN merge stages and each merge
 * stage is made up of lopP steps (P goes like 2, 4, ... N for the logN merge
 * stages)
 * </ol>
 *
 * See Knuth "The Art of Computer Programming" Section 5.3.4 -
 * "Networks for Sorting" (particularly the diagram titled "A nonstandard
 * sorting network based on bitonic sorting" in the First Set of Exercises - Fig
 * 56 in second edition) Here is an online reference:
 * http://www.iti.fh-flensburg
 * .de/lang/algorithmen/sortieren/bitonic/bitonicen.htm
 */
@ServiceProvider(BenchmarkProvider.class)
public final class BitonicSort implements BenchmarkProvider {
    public static void main(String[] args) throws InterruptedException {
        StreamCompiler sc = new Compiler2StreamCompiler().maxNumCores(8).multiplier(64);
        Benchmarker.runBenchmark(Iterables.getLast(new BitonicSort()), sc).get(0).print(System.out);
    }

    private static final int COPIES = 300;

    @Override
    public Iterator<Benchmark> iterator() {
        List<Benchmark> benchmarks = new ArrayList<>();
        for (int n : new int[] { 4, 8, 16, 32 })
            for (boolean ascending : new boolean[] { true, false }) {
                String filename = "BitonicSort" + n + ".in";
                Input<Integer> input = Input.fromBinaryFile(Paths.get("data/" + filename), Integer.class,
                        ByteOrder.LITTLE_ENDIAN);
                input = Datasets.nCopies(COPIES, input);
                //            Input<Integer> output = Datasets.transformAll(new GroupSorter(n, ascending), input);
                Dataset data = new Dataset(filename, input);//.withOutput(output);
                String benchmarkName = String.format("BitonicSort (N = %d, %s)", n, ascending ? "asc" : "desc");
                benchmarks.add(new SuppliedBenchmark(benchmarkName, BitonicSortKernel.class,
                        ImmutableList.of(n, ascending), data));
            }
        return benchmarks.iterator();
    }

    private static final class GroupSorter implements Function<Input<? super Integer>, Input<Integer>> {
        private final int n;
        private final boolean ascending;

        private GroupSorter(int n, boolean ascending) {
            this.n = n;
            this.ascending = ascending;
        }

        @Override
        public Input<Integer> apply(Input<? super Integer> input) {
            Buffer buffer = InputBufferFactory.unwrap(input).createReadableBuffer(n);
            Object[] items = new Object[n];
            List<Integer> retval = new ArrayList<>();
            while (buffer.readAll(items)) {
                if (ascending)
                    Arrays.sort(items);
                else
                    Arrays.sort(items, Collections.reverseOrder());
                for (Object o : items)
                    retval.add((Integer) o);
            }
            return Input.fromIterable(retval);
        }
    }

    /**
     * Compares the two input keys and exchanges their order if they are not
     * sorted.
     *
     * sortdir determines if the sort is nondecreasing (UP) or nonincreasing
     * (DOWN). 'true' indicates UP sort and 'false' indicates DOWN sort.
     */
    private static final class CompareExchange extends Filter<Integer, Integer> {
        private final boolean sortdir;

        private CompareExchange(boolean sortdir) {
            super(2, 2);
            this.sortdir = sortdir;
        }

        @Override
        public void work() {
            /* the input keys and min,max keys */
            int k1, k2, mink, maxk;

            k1 = pop();
            k2 = pop();
            if (k1 <= k2) {
                mink = k1;
                maxk = k2;
            } else /* k1 > k2 */
            {
                mink = k2;
                maxk = k1;
            }

            if (sortdir == true) {
                /* UP sort */
                push(mink);
                push(maxk);
            } else /* sortdir == false */
            {
                /* DOWN sort */
                push(maxk);
                push(mink);
            }
        }

    }

    /**
     * Partition the input bitonic sequence of length L into two bitonic
     * sequences of length L/2, with all numbers in the first sequence <= all
     * numbers in the second sequence if sortdir is UP (similar case for DOWN
     * sortdir)
     *
     * Graphically, it is a bunch of CompareExchanges with same sortdir,
     * clustered together in the sort network at a particular step (of some
     * merge stage).
     */
    private static final class PartitionBitonicSequence extends Splitjoin<Integer, Integer> {
        /* Each CompareExchange examines keys that are L/2 elements apart */
        private PartitionBitonicSequence(int L, boolean sortdir) {
            super(new RoundrobinSplitter<Integer>(), new RoundrobinJoiner<Integer>());
            for (int i = 0; i < (L / 2); i++) {
                add(new CompareExchange(sortdir));
            }
        }
    }

    /**
     * One step of a particular merge stage (used by all merge stages except the
     * last)
     *
     * dircnt determines which step we are in the current merge stage (which in
     * turn is determined by <L, numseqp>)
     */
    private static final class StepOfMerge extends Splitjoin<Integer, Integer> {
        private final int L;
        private final int numseqp;
        private final int dircnt;

        private StepOfMerge(int L, int numseqp, int dircnt) {
            super(new RoundrobinSplitter<Integer>(L), new RoundrobinJoiner<Integer>(L));
            this.L = L;
            this.numseqp = numseqp;
            this.dircnt = dircnt;
            addFilters();
        }

        private void addFilters() {
            boolean curdir;
            for (int j = 0; j < numseqp; j++) {
                /*
                 * finding out the curdir is a bit tricky - the direction
                 * depends only on the subsequence number during the FIRST step.
                 * So to determine the FIRST step subsequence to which this
                 * sequence belongs, divide this sequence's number j by dircnt
                 * (bcoz 'dircnt' tells how many subsequences of the current
                 * step make up one subsequence of the FIRST step). Then, test
                 * if that result is even or odd to determine if curdir is UP or
                 * DOWN respec.
                 */
                curdir = ((j / dircnt) % 2 == 0);
                /*
                 * The last step needs special care to avoid splitjoins with
                 * just one branch.
                 */
                if (L > 2)
                    add(new PartitionBitonicSequence(this.L, curdir));
                else
                    /*
                     * PartitionBitonicSequence of the last step (L=2) is simply
                     * a CompareExchange
                     */
                    add(new CompareExchange(curdir));
            }
        }
    }

    /**
     * One step of the last merge stage
     *
     * Main difference form StepOfMerge is the direction of sort. It is always
     * in the same direction - sortdir.
     */
    private static final class StepOfLastMerge extends Splitjoin<Integer, Integer> {
        private final int L;
        private final int numseqp;
        private final boolean sortdir;

        private StepOfLastMerge(int L, int numseqp, boolean sortdir) {
            super(new RoundrobinSplitter<Integer>(L), new RoundrobinJoiner<Integer>(L));
            this.L = L;
            this.numseqp = numseqp;
            this.sortdir = sortdir;
            addFilters();
        }

        private void addFilters() {
            for (int j = 0; j < numseqp; j++) {
                /*
                 * The last step needs special care to avoid splitjoins with
                 * just one branch.
                 */
                if (L > 2)
                    add(new PartitionBitonicSequence(L, sortdir));
                else
                    /*
                     * PartitionBitonicSequence of the last step (L=2) is simply
                     * a CompareExchange
                     */
                    add(new CompareExchange(sortdir));
            }
        }
    }

    /*
     * Divide the input sequence of length N into subsequences of length P and
     * sort each of them (either UP or DOWN depending on what subsequence number
     * [0 to N/P-1] they get - All even subsequences are sorted UP and all odd
     * subsequences are sorted DOWN) In short, a MergeStage is N/P Bitonic
     * Sorters of order P each.
     *
     * But, this MergeStage is implemented *iteratively* as logP STEPS.
     */
    private static final class MergeStage extends Pipeline<Integer, Integer> {
        private final int P;
        private final int N;

        private MergeStage(int P, int N) {
            this.P = P;
            this.N = N;
            addFilters();
        }

        private void addFilters() {
            int L, numseqp, dircnt;
            /*
             * for each of the lopP steps (except the last step) of this merge
             * stage
             */
            for (int i = 1; i < P; i = i * 2) {
                /*
                 * length of each sequence for the current step - goes like
                 * P,P/2,...,2
                 */
                L = P / i;
                /*
                 * numseqp is the number of PartitionBitonicSequence-rs in this
                 * step
                 */
                numseqp = (N / P) * i;
                dircnt = i;

                add(new StepOfMerge(L, numseqp, dircnt));
            }
        }

    }

    /**
     * The LastMergeStage is basically one Bitonic Sorter of order N i.e., it
     * takes the bitonic sequence produced by the previous merge stages and
     * applies a bitonic merge on it to produce the final sorted sequence.
     *
     * This is implemented iteratively as logN steps
     */
    private static final class LastMergeStage extends Pipeline<Integer, Integer> {
        private final int N;
        private final boolean sortdir;

        private LastMergeStage(int N, boolean sortdir) {
            this.N = N;
            this.sortdir = sortdir;
            addFilter();
        }

        private void addFilter() {
            int L, numseqp;
            /*
             * for each of the logN steps (except the last step) of this merge
             * stage
             */
            for (int i = 1; i < N; i = i * 2) {
                /*
                 * length of each sequence for the current step - goes like
                 * N,N/2,...,2
                 */
                L = N / i;
                /*
                 * numseqp is the number of PartitionBitonicSequence-rs in this
                 * step
                 */
                numseqp = i;
                add(new StepOfLastMerge(L, numseqp, sortdir));
            }
        }
    }

    /**
     * The top-level kernel of bitonic-sort (iterative version) - It has logN
     * merge stages and all merge stages except the last progressively builds a
     * bitonic sequence out of the input sequence. The last merge stage acts on
     * the resultant bitonic sequence to produce the final sorted sequence
     * (sortdir determines if it is UP or DOWN).
     */
    public static final class BitonicSortKernel extends Pipeline<Integer, Integer> {
        private final int N;
        private final boolean sortdir;

        public BitonicSortKernel(int N, boolean sortdir) {
            this.N = N;
            this.sortdir = sortdir;
            addFilter();
        }

        private void addFilter() {
            for (int i = 2; i <= (N / 2); i = 2 * i) {
                add(new MergeStage(i, N));
            }
            add(new LastMergeStage(N, sortdir));
        }
    }
}