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 (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. 26# 27 28# 29# mandelbrotset1 - a simple mandelbrot set generation and 30# parallel execution demo 31# 32 33# Solaris needs /usr/xpg6/bin:/usr/xpg4/bin because the tools in /usr/bin are not POSIX-conformant 34export PATH=/usr/xpg6/bin:/usr/xpg4/bin:/bin:/usr/bin 35 36# Make sure all math stuff runs in the "C" locale to avoid problems 37# with alternative # radix point representations (e.g. ',' instead of 38# '.' in de_DE.*-locales). This needs to be set _before_ any 39# floating-point constants are defined in this script). 40if [[ "${LC_ALL}" != "" ]] ; then 41 export \ 42 LC_MONETARY="${LC_ALL}" \ 43 LC_MESSAGES="${LC_ALL}" \ 44 LC_COLLATE="${LC_ALL}" \ 45 LC_CTYPE="${LC_ALL}" 46 unset LC_ALL 47fi 48export LC_NUMERIC=C 49 50function printmsg 51{ 52 print -u2 "$*" 53} 54 55function fatal_error 56{ 57 print -u2 "${progname}: $*" 58 exit 1 59} 60 61# Get terminal size and put values into a compound variable with the integer 62# members "columns" and "lines" 63function get_term_size 64{ 65 nameref rect=$1 66 67 rect.columns=${ tput cols ; } || return 1 68 rect.lines=${ tput lines ; } || return 1 69 70 return 0 71} 72 73function mandelbrot 74{ 75 nameref result=$1 76 float x=$2 77 float y=$3 78 float xx 79 float yy 80 float x1=$4 81 float y1=$5 82 integer iteration=$6 83 integer max_iteration=$7 84 float mag 85 86 for (( mag=0 ; mag < max_mag && iteration < max_iteration ; iteration++ )) ; do 87 (( 88 xx=x*x , 89 yy=y*y , 90 mag=xx+yy , 91 y=x*y*2+y1 , 92 x=xx-yy+x1 93 )) 94 done 95 96 (( result=iteration )) 97 98 return 0 99} 100 101# build mandelbrot image serially 102function loop_serial 103{ 104 integer value 105 typeset line="" 106 107 for (( y=y_min ; y < y_max ; y+=stepwidth )) ; do 108 for (( x=x_min ; x < x_max ; x+=stepwidth )) ; do 109 mandelbrot value ${x} ${y} ${x} ${y} 1 ${symbollistlen} 110 line+="${symbollist:value:1}" 111 done 112 113 line+=$'\n' 114 done 115 116 print -r -- "${line}" 117 118 return 0 119} 120 121# build mandelbrot image using parallel worker jobs 122function loop_parallel 123{ 124 integer numjobs=0 125 # the following calculation suffers from rounding errors 126 integer lines_per_job=$(( ((m_height+(numcpus-1)) / numcpus) )) 127 typeset tmpjobdir 128 129 printmsg $"# lines_per_job=${lines_per_job}" 130 printmsg $"# numcpus=${numcpus}" 131 132 # "renice" worker jobs 133 set -o bgnice 134 135 tmpjobdir="$(mktemp --default=/tmp --directory "mandelbrotset1${PPID}_$$_XXXXXX")" || fatal_error $"Could not create temporary directory." 136 trap "rm -r ${tmpjobdir}" EXIT # cleanup 137 138 # try to generate a job identifer prefix which is unique across multiple hosts 139 jobident="job_host_$(uname -n)pid_$$_ppid${PPID}" 140 141 printmsg $"## prepare..." 142 for (( y=y_min ; y < y_max ; y+=(stepwidth*lines_per_job) )) ; do 143 rm -f "${tmpjobdir}/${jobident}_child_$y.joboutput" 144 145 (( numjobs++ )) 146 done 147 148 printmsg $"## running ${numjobs} children..." 149 for (( y=y_min ; y < y_max ; y+=(stepwidth*lines_per_job) )) ; do 150 ( 151 integer value 152 typeset line="" 153 # save file name since we're going to modify "y" 154 typeset filename="${tmpjobdir}/${jobident}_child_$y.joboutput" 155 156 for (( ; y < y_max && lines_per_job-- > 0 ; y+=stepwidth )) ; do 157 for (( x=x_min ; x < x_max ; x+=stepwidth )) ; do 158 mandelbrot value ${x} ${y} ${x} ${y} 1 ${symbollistlen} 159 line+="${symbollist:value:1}" 160 done 161 162 line+=$'\n' 163 done 164 print -r -- "${line}" >"${filename}" 165 166 exit 0 167 ) & 168 done 169 170 printmsg $"## waiting for ${numjobs} children..." 171 wait 172 173 printmsg $"## output:" 174 for (( y=y_min ; y < y_max ; y+=(stepwidth*lines_per_job) )) ; do 175 print -r -- "$( < "${tmpjobdir}/${jobident}_child_$y.joboutput")" 176 # EXIT trap will cleanup temporary files 177 done 178 179 return 0 180} 181 182function usage 183{ 184 OPTIND=0 185 getopts -a "${progname}" "${mandelbrotset1_usage}" OPT '-?' 186 exit 2 187} 188 189# main 190builtin basename 191builtin cat 192builtin rm 193builtin uname # loop_parallel needs the ksh93 builtin version to generate unique job file names 194builtin mktemp 195 196set -o noglob 197set -o nounset 198 199typeset progname="${ basename "${0}" ; }" 200 201float x_max 202float x_min 203float y_max 204float y_min 205float m_width 206float m_height 207float max_mag 208float stepwidth 209integer numcpus 210 211# terminal size rect 212compound termsize=( 213 integer columns=-1 214 integer lines=-1 215) 216 217get_term_size termsize || fatal_error $"Could not get terminal size." 218 219typeset symbollist=' .:0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ%#' 220typeset symbollistlen=$(( ${#symbollist} - 1)) 221typeset mode="parallel" 222 223(( max_mag=400 )) 224(( stepwidth=0.1 )) 225 226# calculate number of worker CPUs and use 3 as fallback 227(( numcpus=$(getconf NPROCESSORS_ONLN || print "3") )) 228(( numcpus=numcpus*4 )) 229 230(( m_width=termsize.columns-1 , m_height=termsize.lines-2 )) 231 232typeset -r mandelbrotset1_usage=$'+ 233[-?\n@(#)\$Id: mandelbrotset1 (Roland Mainz) 2010-03-31 \$\n] 234[-author?Roland Mainz <roland.mainz@nrubsig.org>] 235[+NAME?mandelbrotset1 - generate mandelbrot set fractals with ksh93] 236[+DESCRIPTION?\bmandelbrotset1\b mandelbrot set fractal generator 237 which runs either in serial or parallel mode (using multiple worker jobs).] 238[w:width?Width of fractal.]:[width] 239[h:height?Height of fractal.]:[height] 240[s:symbols?Symbols to build the fractal from.]:[symbolstring] 241[m:mag?Magnification level.]:[magnificationlevel] 242[p:stepwidth?Width per step.]:[widthperstep] 243[S:serial?Run in serial mode.] 244[P:parallel?Run in parallel mode.] 245[M:mode?Execution mode.]:[mode] 246[C:numcpus?Number of processors used for parallel execution.]:[numcpus] 247[+SEE ALSO?\bjuliaset1\b(1), \bksh93\b(1)] 248' 249 250while getopts -a "${progname}" "${mandelbrotset1_usage}" OPT ; do 251# printmsg "## OPT=|${OPT}|, OPTARG=|${OPTARG}|" 252 case ${OPT} in 253 w) m_width="${OPTARG}" ;; 254 h) m_height="${OPTARG}" ;; 255 s) symbollist="${OPTARG}" ;; 256 m) max_mag="${OPTARG}" ;; 257 p) stepwidth="${OPTARG}" ;; 258 S) mode="serial" ;; 259 +S) mode="parallel" ;; 260 P) mode="parallel" ;; 261 +P) mode="serial" ;; 262 M) mode="${OPTARG}" ;; 263 C) numcpus="${OPTARG}" ;; 264 *) usage ;; 265 esac 266done 267shift $((OPTIND-1)) 268 269printmsg "# width=${m_width}" 270printmsg "# height=${m_height}" 271printmsg "# max_mag=${max_mag}" 272printmsg "# stepwidth=${stepwidth}" 273printmsg "# symbollist='${symbollist}'" 274printmsg "# mode=${mode}" 275 276(( symbollistlen=${#symbollist}-1 )) 277 278(( 279 x_max=m_width*stepwidth/2. , 280 x_min=-x_max , 281 y_max=m_height*stepwidth/2. , 282 y_min=-y_max 283)) 284 285case "${mode}" in 286 parallel) loop_parallel ; exit $? ;; 287 serial) loop_serial ; exit $? ;; 288 *) fatal_error $"Unknown mode \"${mode}\"." ;; 289esac 290 291fatal_error "not reached." 292# EOF. 293