Package com.barrybecker4.simulation.fractalexplorer.algorithm

Source Code of com.barrybecker4.simulation.fractalexplorer.algorithm.FractalAlgorithm$Worker

/** Copyright by Barry G. Becker, 2000-2011. Licensed under MIT License: http://www.opensource.org/licenses/MIT  */
package com.barrybecker4.simulation.fractalexplorer.algorithm;

import com.barrybecker4.common.concurrency.Parallelizer;
import com.barrybecker4.common.geometry.Box;
import com.barrybecker4.common.geometry.IntLocation;
import com.barrybecker4.common.math.ComplexNumber;
import com.barrybecker4.common.math.ComplexNumberRange;
import com.barrybecker4.simulation.common.Profiler;

import java.util.ArrayList;
import java.util.List;

/**
* Abstract implementation common to all fractal algorithms.
* Uses concurrency when parallelized is set.
* This will give good speedup on multi-core machines.
*
* For core 2 Duo:
*  - not-parallel 32.4 seconds
*  - parallel     19.5 seconds
*
*  For i7 2600k
*     original                     after rendering optimization.
*  - not-parallel  16.0  seconds         6.1
*  - parallel       5.1 seconds          2.1
* @author Barry Becker
*/
public abstract class FractalAlgorithm {

    public static final int DEFAULT_MAX_ITERATIONS = 500;

    protected final FractalModel model;

    /** range of bounding box in complex plane. */
    private ComplexNumberRange range;

    /** Manages the worker threads. */
    private Parallelizer<Worker> parallelizer_;

    private int maxIterations_ = DEFAULT_MAX_ITERATIONS;

    private RowCalculator rowCalculator_;

    private boolean restartRequested = false;
    private boolean wasDone = false;

    private History history_ = new History();


    public FractalAlgorithm(FractalModel model, ComplexNumberRange range) {
        this.model = model;
        setRange(range);
        setParallelized(true);
        rowCalculator_ = new RowCalculator(this);
    }

    public void setRange(ComplexNumberRange range)  {

        history_.addRangeToHistory(range);
        processRange(range);
    }

    /**
     * Back up one step in the history
     */
    public void goBack() {
        ComplexNumberRange newRange = history_.popLastRange();
        if (range.equals(newRange)) {
            newRange = history_.popLastRange();
        }
        processRange(newRange);
    }

    private void processRange(ComplexNumberRange range) {
        this.range = range;
        restartRequested = true;
    }

    public boolean isParallelized() {
        return (parallelizer_.getNumThreads() > 1);
    }

    public void setParallelized(boolean parallelized) {
        if (parallelizer_ == null || parallelized != isParallelized()) {

            parallelizer_ =
                 parallelized ? new Parallelizer<Worker>() : new Parallelizer<Worker>(1);
            model.setCurrentRow(0);
        }
    }

    public int getMaxIterations() {
        return maxIterations_;
    }

    public void setMaxIterations(int value) {
        if (value != maxIterations_)  {
            maxIterations_ = value;
            model.setCurrentRow(0);
        }
    }

    public boolean getUseRunLengthOptimization() {
        return rowCalculator_.getUseRunLengthOptimization();
    }

    public void setUseRunLengthOptimization(boolean value) {
        rowCalculator_.setUseRunLengthOptimization(value);
    }

    public FractalModel getModel() {
        return model;
    }

    /**
     * @param timeStep number of rows to compute on this timestep.
     * @return true when done computing whole model.
     */
    public boolean timeStep(double timeStep) {

        if (restartRequested) {
            model.setCurrentRow(0);
            restartRequested = false;
        }
        if (model.isDone()) {
            showProfileInfoWhenFinished();
            return true// we are done.
        }

        int numProcs = parallelizer_.getNumThreads();
        List<Runnable> workers = new ArrayList<Runnable>(numProcs);

        // we calculate a little more each "timestep"
        int currentRow = model.getCurrentRow();
        startProfileTimeIfNeeded(currentRow);

        int height = model.getHeight();
        int computeToRow = Math.min(height, currentRow + (int)timeStep * numProcs);

        int diff = computeToRow - currentRow;
        if (diff == 0) return true;

        int chunk = Math.max(1, diff / numProcs);

        for (int i = 0; i < numProcs; i++) {
            int nextRow = Math.min(height, currentRow + chunk);
            workers.add(new Worker(currentRow, nextRow));
            //System.out.println("creating worker ("+i+") to compute " + chunk +" rows.");
            currentRow = nextRow;
        }

        // blocks until all Callables are done running.
        parallelizer_.invokeAllRunnables(workers);

        model.setCurrentRow(currentRow);

        return false;
    }


    /**
     * @return a number between 0 and 1.
     * Typically corresponds to the number times we had to iterate before the point escaped (or not).
     */
    public abstract double getFractalValue(ComplexNumber seed);

    /**
     * Converts from screen coordinates to data coordinates.
     * @param x
     * @param y
     * @return corresponding position in complex number plane represented by the model.
     */
    public ComplexNumber getComplexPosition(int x, int y) {
        return range.getInterpolatedPosition((double)x / model.getWidth(), (double)y / model.getHeight());
    }

    public ComplexNumber getComplexPosition(IntLocation loc) {
        return getComplexPosition(loc.getX(), loc.getY());
    }

    public ComplexNumberRange getRange(Box box)  {
        ComplexNumber firstCorner = getComplexPosition(box.getTopLeftCorner());
        ComplexNumber secondCorner =getComplexPosition(box.getBottomRightCorner());
        return new ComplexNumberRange(firstCorner, secondCorner);
    }


    private void startProfileTimeIfNeeded(int currentRow) {
        if (currentRow == 0) {
            Profiler.getInstance().startCalculationTime();
            wasDone = false;
        }
    }

    private void showProfileInfoWhenFinished() {
        if (!wasDone) {
            Profiler prof = Profiler.getInstance();
            prof.stopCalculationTime();
            prof.print();
            prof.resetAll();
            wasDone = true;
        }
    }

    /**
     * Runs one of the chunks.
     */
    private class Worker implements Runnable {

        private int fromRow_;
        private int toRow_;

        public Worker(int fromRow, int toRow) {

            fromRow_ = fromRow;
            toRow_ = toRow;
        }

        public void run() {
            computeChunk(fromRow_, toRow_);
        }

        /**
         * Do a chunk of work (i.e. compute the specified rows)
         */
        private void computeChunk(int fromRow, int toRow) {

            int width = model.getWidth();
            for (int y = fromRow; y < toRow; y++)   {
                rowCalculator_.calculateRow(width, y);
            }
        }
    }
}
TOP

Related Classes of com.barrybecker4.simulation.fractalexplorer.algorithm.FractalAlgorithm$Worker

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.