#!/usr/bin/ksh93 # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. # # # mandelbrotset1 - a simple mandelbrot set generation and # parallel execution demo # # Solaris needs /usr/xpg6/bin:/usr/xpg4/bin because the tools in /usr/bin are not POSIX-conformant export PATH=/usr/xpg6/bin:/usr/xpg4/bin:/bin:/usr/bin # Make sure all math stuff runs in the "C" locale to avoid problems # with alternative # radix point representations (e.g. ',' instead of # '.' in de_DE.*-locales). This needs to be set _before_ any # floating-point constants are defined in this script). if [[ "${LC_ALL}" != "" ]] ; then export \ LC_MONETARY="${LC_ALL}" \ LC_MESSAGES="${LC_ALL}" \ LC_COLLATE="${LC_ALL}" \ LC_CTYPE="${LC_ALL}" unset LC_ALL fi export LC_NUMERIC=C function printmsg { print -u2 "$*" } function fatal_error { print -u2 "${progname}: $*" exit 1 } # Get terminal size and put values into a compound variable with the integer # members "columns" and "lines" function get_term_size { nameref rect=$1 rect.columns=${ tput cols ; } || return 1 rect.lines=${ tput lines ; } || return 1 return 0 } function mandelbrot { nameref result=$1 float x=$2 float y=$3 float xx float yy float x1=$4 float y1=$5 integer iteration=$6 integer max_iteration=$7 float mag for (( mag=0 ; mag < max_mag && iteration < max_iteration ; iteration++ )) ; do (( xx=x*x , yy=y*y , mag=xx+yy , y=x*y*2+y1 , x=xx-yy+x1 )) done (( result=iteration )) return 0 } # build mandelbrot image serially function loop_serial { integer value typeset line="" for (( y=y_min ; y < y_max ; y+=stepwidth )) ; do for (( x=x_min ; x < x_max ; x+=stepwidth )) ; do mandelbrot value ${x} ${y} ${x} ${y} 1 ${symbollistlen} line+="${symbollist:value:1}" done line+=$'\n' done print -r -- "${line}" return 0 } # build mandelbrot image using parallel worker jobs function loop_parallel { integer numjobs=0 # the following calculation suffers from rounding errors integer lines_per_job=$(( ((m_height+(numcpus-1)) / numcpus) )) typeset tmpjobdir printmsg $"# lines_per_job=${lines_per_job}" printmsg $"# numcpus=${numcpus}" # "renice" worker jobs set -o bgnice tmpjobdir="$(mktemp --default=/tmp --directory "mandelbrotset1${PPID}_$$_XXXXXX")" || fatal_error $"Could not create temporary directory." trap "rm -r ${tmpjobdir}" EXIT # cleanup # try to generate a job identifer prefix which is unique across multiple hosts jobident="job_host_$(uname -n)pid_$$_ppid${PPID}" printmsg $"## prepare..." for (( y=y_min ; y < y_max ; y+=(stepwidth*lines_per_job) )) ; do rm -f "${tmpjobdir}/${jobident}_child_$y.joboutput" (( numjobs++ )) done printmsg $"## running ${numjobs} children..." for (( y=y_min ; y < y_max ; y+=(stepwidth*lines_per_job) )) ; do ( integer value typeset line="" # save file name since we're going to modify "y" typeset filename="${tmpjobdir}/${jobident}_child_$y.joboutput" for (( ; y < y_max && lines_per_job-- > 0 ; y+=stepwidth )) ; do for (( x=x_min ; x < x_max ; x+=stepwidth )) ; do mandelbrot value ${x} ${y} ${x} ${y} 1 ${symbollistlen} line+="${symbollist:value:1}" done line+=$'\n' done print -r -- "${line}" >"${filename}" exit 0 ) & done printmsg $"## waiting for ${numjobs} children..." wait printmsg $"## output:" for (( y=y_min ; y < y_max ; y+=(stepwidth*lines_per_job) )) ; do print -r -- "$( < "${tmpjobdir}/${jobident}_child_$y.joboutput")" # EXIT trap will cleanup temporary files done return 0 } function usage { OPTIND=0 getopts -a "${progname}" "${mandelbrotset1_usage}" OPT '-?' exit 2 } # main builtin basename builtin cat builtin rm builtin uname # loop_parallel needs the ksh93 builtin version to generate unique job file names builtin mktemp set -o noglob set -o nounset typeset progname="${ basename "${0}" ; }" float x_max float x_min float y_max float y_min float m_width float m_height float max_mag float stepwidth integer numcpus # terminal size rect compound termsize=( integer columns=-1 integer lines=-1 ) get_term_size termsize || fatal_error $"Could not get terminal size." typeset symbollist=' .:0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ%#' typeset symbollistlen=$(( ${#symbollist} - 1)) typeset mode="parallel" (( max_mag=400 )) (( stepwidth=0.1 )) # calculate number of worker CPUs and use 3 as fallback (( numcpus=$(getconf NPROCESSORS_ONLN || print "3") )) (( numcpus=numcpus*4 )) (( m_width=termsize.columns-1 , m_height=termsize.lines-2 )) typeset -r mandelbrotset1_usage=$'+ [-?\n@(#)\$Id: mandelbrotset1 (Roland Mainz) 2010-03-31 \$\n] [-author?Roland Mainz ] [+NAME?mandelbrotset1 - generate mandelbrot set fractals with ksh93] [+DESCRIPTION?\bmandelbrotset1\b mandelbrot set fractal generator which runs either in serial or parallel mode (using multiple worker jobs).] [w:width?Width of fractal.]:[width] [h:height?Height of fractal.]:[height] [s:symbols?Symbols to build the fractal from.]:[symbolstring] [m:mag?Magnification level.]:[magnificationlevel] [p:stepwidth?Width per step.]:[widthperstep] [S:serial?Run in serial mode.] [P:parallel?Run in parallel mode.] [M:mode?Execution mode.]:[mode] [C:numcpus?Number of processors used for parallel execution.]:[numcpus] [+SEE ALSO?\bjuliaset1\b(1), \bksh93\b(1)] ' while getopts -a "${progname}" "${mandelbrotset1_usage}" OPT ; do # printmsg "## OPT=|${OPT}|, OPTARG=|${OPTARG}|" case ${OPT} in w) m_width="${OPTARG}" ;; h) m_height="${OPTARG}" ;; s) symbollist="${OPTARG}" ;; m) max_mag="${OPTARG}" ;; p) stepwidth="${OPTARG}" ;; S) mode="serial" ;; +S) mode="parallel" ;; P) mode="parallel" ;; +P) mode="serial" ;; M) mode="${OPTARG}" ;; C) numcpus="${OPTARG}" ;; *) usage ;; esac done shift $((OPTIND-1)) printmsg "# width=${m_width}" printmsg "# height=${m_height}" printmsg "# max_mag=${max_mag}" printmsg "# stepwidth=${stepwidth}" printmsg "# symbollist='${symbollist}'" printmsg "# mode=${mode}" (( symbollistlen=${#symbollist}-1 )) (( x_max=m_width*stepwidth/2. , x_min=-x_max , y_max=m_height*stepwidth/2. , y_min=-y_max )) case "${mode}" in parallel) loop_parallel ; exit $? ;; serial) loop_serial ; exit $? ;; *) fatal_error $"Unknown mode \"${mode}\"." ;; esac fatal_error "not reached." # EOF.