1#!/usr/bin/ksh93 2 3# 4# CDDL HEADER START 5# 6# The contents of this file are subject to the terms of the 7# Common Development and Distribution License (the "License"). 8# You may not use this file except in compliance with the License. 9# 10# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 11# or http://www.opensolaris.org/os/licensing. 12# See the License for the specific language governing permissions 13# and limitations under the License. 14# 15# When distributing Covered Code, include this CDDL HEADER in each 16# file and include the License file at usr/src/OPENSOLARIS.LICENSE. 17# If applicable, add the following below this CDDL HEADER, with the 18# fields enclosed by brackets "[]" replaced with your own identifying 19# information: Portions Copyright [yyyy] [name of copyright owner] 20# 21# CDDL HEADER END 22# 23 24# 25# Copyright 2008 Sun Microsystems, Inc. All rights reserved. 26# Use is subject to license terms. 27# 28 29# 30# mandelbrotset1 - a simple mandelbrot set generation and 31# parallel execution demo 32# 33 34# Solaris needs /usr/xpg6/bin:/usr/xpg4/bin because the tools in /usr/bin are not POSIX-conformant 35export PATH=/usr/xpg6/bin:/usr/xpg4/bin:/bin:/usr/bin 36 37# Make sure all math stuff runs in the "C" locale to avoid problems 38# with alternative # radix point representations (e.g. ',' instead of 39# '.' in de_DE.*-locales). This needs to be set _before_ any 40# floating-point constants are defined in this script). 41if [[ "${LC_ALL}" != "" ]] ; then 42 export \ 43 LC_MONETARY="${LC_ALL}" \ 44 LC_MESSAGES="${LC_ALL}" \ 45 LC_COLLATE="${LC_ALL}" \ 46 LC_CTYPE="${LC_ALL}" 47 unset LC_ALL 48fi 49export LC_NUMERIC=C 50 51function printmsg 52{ 53 print -u2 "$*" 54} 55 56function fatal_error 57{ 58 print -u2 "${progname}: $*" 59 exit 1 60} 61 62# Get terminal size and put values into a compound variable with the integer 63# members "columns" and "lines" 64function get_term_size 65{ 66 nameref rect=$1 67 68 rect.columns=${ tput cols ; } || return 1 69 rect.lines=${ tput lines ; } || return 1 70 71 return 0 72} 73 74function print_color 75{ 76 print -r -n -- "${symbollist:${1}:1}" 77 return 0 78} 79 80function mandelbrot 81{ 82 nameref result=$1 83 float x=$2 84 float y=$3 85 float xx 86 float yy 87 float x1=$4 88 float y1=$5 89 integer iteration=$6 90 integer max_iteration=$7 91 float mag 92 93 for (( mag=0 ; mag < max_mag && iteration < max_iteration ; iteration++ )) ; do 94 (( 95 xx=x*x , 96 yy=y*y , 97 mag=xx+yy , 98 y=x*y*2+y1 , 99 x=xx-yy+x1 100 )) 101 done 102 103 (( result=iteration )) 104 105 return 0 106} 107 108# build mandelbrot image serially 109function loop_serial 110{ 111 integer value 112 113 for (( y=y_min ; y < y_max ; y+=stepwidth )) ; do 114 for (( x=x_min ; x < x_max ; x+=stepwidth )) ; do 115 mandelbrot value ${x} ${y} ${x} ${y} 1 ${symbollistlen} 116 print_color ${value} 117 done 118 119 print 120 done 121 122 return 0 123} 124 125# build mandelbrot image using parallel worker jobs 126function loop_parallel 127{ 128 integer numjobs=0 129 # the following calculation suffers from rounding errors 130 integer lines_per_job=$(( ((m_height+(numcpus-1)) / numcpus) )) 131 132 printmsg $"# lines_per_job=${lines_per_job}" 133 printmsg $"# numcpus=${numcpus}" 134 135 # "renice" worker jobs 136 set -o bgnice 137 138 if [[ "${TMPDIR}" == "" ]] ; then 139 TMPDIR="/tmp" 140 fi 141 142 # try to generate a job identifer prefix which is unique across multiple hosts 143 jobident="job_host_$(uname -n)pid_$$_ppid${PPID}" 144 145 printmsg $"## prepare..." 146 for (( y=y_min ; y < y_max ; y+=(stepwidth*lines_per_job) )) ; do 147 rm -f "${TMPDIR}/mandelbrot_${jobident}_child_$y.joboutput" 148 149 (( numjobs++ )) 150 done 151 152 printmsg $"## running ${numjobs} children..." 153 for (( y=y_min ; y < y_max ; y+=(stepwidth*lines_per_job) )) ; do 154 ( 155 integer value 156 157 for (( ; y < y_max && lines_per_job-- > 0 ; y+=stepwidth )) ; do 158 for (( x=x_min ; x < x_max ; x+=stepwidth )) ; do 159 mandelbrot value ${x} ${y} ${x} ${y} 1 ${symbollistlen} 160 print_color ${value} 161 done 162 163 print 164 done >"${TMPDIR}/mandelbrot_${jobident}_child_$y.joboutput" 165 ) & 166 done 167 168 printmsg $"## waiting for ${numjobs} children..." 169 wait 170 171 printmsg $"## output:" 172 for (( y=y_min ; y < y_max ; y+=(stepwidth*lines_per_job) )) ; do 173 print -- "$( < "${TMPDIR}/mandelbrot_${jobident}_child_$y.joboutput")" 174 rm "${TMPDIR}/mandelbrot_${jobident}_child_$y.joboutput" 175 done 176 177 return 0 178} 179 180function usage 181{ 182 OPTIND=0 183 getopts -a "${progname}" "${mandelbrotset1_usage}" OPT '-?' 184 exit 2 185} 186 187# main 188builtin basename 189builtin cat 190builtin rm 191builtin uname # loop_parallel needs the ksh93 builtin version to generate unique job file names 192 193typeset progname="${ basename "${0}" ; }" 194 195float x_max 196float x_min 197float y_max 198float y_min 199float m_width 200float m_height 201float max_mag 202float stepwidth 203integer numcpus 204 205# terminal size rect 206typeset -C termsize=( 207 integer columns=-1 208 integer lines=-1 209) 210 211get_term_size termsize || fatal_error $"Could not get terminal size." 212 213typeset symbollist=' .:0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ%#' 214typeset symbollistlen=$(( ${#symbollist} - 1)) 215typeset mode="parallel" 216 217max_mag=400 218stepwidth=0.1 219numcpus=16 220 221(( m_width=termsize.columns-1 , m_height=termsize.lines-2 )) 222 223typeset -r mandelbrotset1_usage=$'+ 224[-?\n@(#)\$Id: mandelbrotset1 (Roland Mainz) 2008-11-04 \$\n] 225[-author?Roland Mainz <roland.mainz@nrubsig.org>] 226[+NAME?mandelbrotset1 - generate mandelbrot set fractals with ksh93] 227[+DESCRIPTION?\bmandelbrotset1\b mandelbrot set fractal generator 228 which runs either in serial or parallel mode (using multiple worker jobs).] 229[w:width?Width of fractal.]:[width] 230[h:height?Height of fractal.]:[height] 231[s:symbols?Symbols to build the fractal from.]:[symbolstring] 232[m:mag?Magnification level.]:[magnificationlevel] 233[p:stepwidth?Width per step.]:[widthperstep] 234[S:serial?Run in serial mode.] 235[P:parallel?Run in parallel mode.] 236[M:mode?Execution mode.]:[mode] 237[C:numcpus?Number of processors used for parallel execution.]:[numcpus] 238[+SEE ALSO?\bjuliaset1\b(1), \bksh93\b(1)] 239' 240 241while getopts -a "${progname}" "${mandelbrotset1_usage}" OPT ; do 242# printmsg "## OPT=|${OPT}|, OPTARG=|${OPTARG}|" 243 case ${OPT} in 244 w) m_width="${OPTARG}" ;; 245 h) m_height="${OPTARG}" ;; 246 s) symbollist="${OPTARG}" ;; 247 m) max_mag="${OPTARG}" ;; 248 p) stepwidth="${OPTARG}" ;; 249 S) mode="serial" ;; 250 P) mode="parallel" ;; 251 M) mode="${OPTARG}" ;; 252 C) numcpus="${OPTARG}" ;; 253 *) usage ;; 254 esac 255done 256shift $((OPTIND-1)) 257 258printmsg "# width=${m_width}" 259printmsg "# height=${m_height}" 260printmsg "# max_mag=${max_mag}" 261printmsg "# stepwidth=${stepwidth}" 262printmsg "# symbollist='${symbollist}'" 263printmsg "# mode=${mode}" 264 265(( symbollistlen=${#symbollist}-1 )) 266 267(( 268 x_max=m_width*stepwidth/2. , 269 x_min=-x_max , 270 y_max=m_height*stepwidth/2. , 271 y_min=-y_max 272)) 273 274case "${mode}" in 275 parallel) loop_parallel ; exit $? ;; 276 serial) loop_serial ; exit $? ;; 277 *) fatal_error $"Unknown mode \"${mode}\"." ;; 278esac 279 280fatal_error "not reached." 281# EOF. 282