xref: /titanic_44/usr/src/lib/libshell/common/scripts/mandelbrotset1.sh (revision 8cdd6a74847b5ae6ed26727528bdf6b139cf7552)
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 2009 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 mandelbrot
75{
76	nameref result=$1
77	float   x=$2
78	float   y=$3
79	float   xx
80	float   yy
81	float   x1=$4
82	float   y1=$5
83	integer iteration=$6
84	integer max_iteration=$7
85	float   mag
86
87	for (( mag=0 ; mag < max_mag && iteration < max_iteration ; iteration++ )) ; do
88		((
89			xx=x*x ,
90			yy=y*y ,
91			mag=xx+yy ,
92			y=x*y*2+y1 ,
93			x=xx-yy+x1
94		))
95	done
96
97	(( result=iteration ))
98
99	return 0
100}
101
102# build mandelbrot image serially
103function loop_serial
104{
105	integer value
106	typeset line=""
107
108	for (( y=y_min ; y < y_max ; y+=stepwidth )) ; do
109		for (( x=x_min ; x < x_max ; x+=stepwidth )) ; do
110			mandelbrot value ${x} ${y} ${x} ${y} 1 ${symbollistlen}
111			line+="${symbollist:value:1}"
112		done
113
114		line+=$'\n'
115	done
116
117	print -r -- "${line}"
118
119	return 0
120}
121
122# build mandelbrot image using parallel worker jobs
123function loop_parallel
124{
125	integer numjobs=0
126	# the following calculation suffers from rounding errors
127	integer lines_per_job=$(( ((m_height+(numcpus-1)) / numcpus) ))
128	typeset tmpjobdir
129
130	printmsg $"# lines_per_job=${lines_per_job}"
131	printmsg $"# numcpus=${numcpus}"
132
133	# "renice" worker jobs
134	set -o bgnice
135
136	tmpjobdir="$(mktemp --default=/tmp --directory "mandelbrotset1${PPID}_$$_XXXXXX")" || fatal_error $"Could not create temporary directory."
137	trap "rm -r ${tmpjobdir}" EXIT # cleanup
138
139	# try to generate a job identifer prefix which is unique across multiple hosts
140	jobident="job_host_$(uname -n)pid_$$_ppid${PPID}"
141
142	printmsg $"## prepare..."
143	for (( y=y_min ; y < y_max ; y+=(stepwidth*lines_per_job) )) ; do
144		rm -f "${tmpjobdir}/${jobident}_child_$y.joboutput"
145
146		(( numjobs++ ))
147	done
148
149	printmsg $"## running ${numjobs} children..."
150	for (( y=y_min ; y < y_max ; y+=(stepwidth*lines_per_job) )) ; do
151		(
152			integer value
153			typeset line=""
154			# save file name since we're going to modify "y"
155			typeset filename="${tmpjobdir}/${jobident}_child_$y.joboutput"
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					line+="${symbollist:value:1}"
161				done
162
163				line+=$'\n'
164			done
165			print -r -- "${line}" >"${filename}"
166
167			exit 0
168		) &
169	done
170
171	printmsg $"## waiting for ${numjobs} children..."
172	wait
173
174	printmsg $"## output:"
175	for (( y=y_min ; y < y_max ; y+=(stepwidth*lines_per_job) )) ; do
176		print -r -- "$( < "${tmpjobdir}/${jobident}_child_$y.joboutput")"
177		# EXIT trap will cleanup temporary files
178	done
179
180	return 0
181}
182
183function usage
184{
185	OPTIND=0
186	getopts -a "${progname}" "${mandelbrotset1_usage}" OPT '-?'
187	exit 2
188}
189
190# main
191builtin basename
192builtin cat
193builtin rm
194builtin uname # loop_parallel needs the ksh93 builtin version to generate unique job file names
195builtin mktemp
196
197set -o noglob
198set -o nounset
199
200typeset progname="${ basename "${0}" ; }"
201
202float x_max
203float x_min
204float y_max
205float y_min
206float m_width
207float m_height
208float max_mag
209float stepwidth
210integer numcpus
211
212# terminal size rect
213compound termsize=(
214	integer columns=-1
215	integer lines=-1
216)
217
218get_term_size termsize || fatal_error $"Could not get terminal size."
219
220typeset symbollist='    .:0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ%#'
221typeset symbollistlen=$(( ${#symbollist} - 1))
222typeset mode="parallel"
223
224max_mag=400
225stepwidth=0.1
226numcpus=16
227
228(( m_width=termsize.columns-1 , m_height=termsize.lines-2 ))
229
230typeset -r mandelbrotset1_usage=$'+
231[-?\n@(#)\$Id: mandelbrotset1 (Roland Mainz) 2009-06-14 \$\n]
232[-author?Roland Mainz <roland.mainz@nrubsig.org>]
233[+NAME?mandelbrotset1 - generate mandelbrot set fractals with ksh93]
234[+DESCRIPTION?\bmandelbrotset1\b mandelbrot set fractal generator
235	which runs either in serial or parallel mode (using multiple worker jobs).]
236[w:width?Width of fractal.]:[width]
237[h:height?Height of fractal.]:[height]
238[s:symbols?Symbols to build the fractal from.]:[symbolstring]
239[m:mag?Magnification level.]:[magnificationlevel]
240[p:stepwidth?Width per step.]:[widthperstep]
241[S:serial?Run in serial mode.]
242[P:parallel?Run in parallel mode.]
243[M:mode?Execution mode.]:[mode]
244[C:numcpus?Number of processors used for parallel execution.]:[numcpus]
245[+SEE ALSO?\bjuliaset1\b(1), \bksh93\b(1)]
246'
247
248while getopts -a "${progname}" "${mandelbrotset1_usage}" OPT ; do
249#	printmsg "## OPT=|${OPT}|, OPTARG=|${OPTARG}|"
250	case ${OPT} in
251		w)	m_width="${OPTARG}"	;;
252		h)	m_height="${OPTARG}"	;;
253		s)	symbollist="${OPTARG}"	;;
254		m)	max_mag="${OPTARG}"	;;
255		p)	stepwidth="${OPTARG}"	;;
256		S)	mode="serial"		;;
257		+S)	mode="parallel"		;;
258		P)	mode="parallel"		;;
259		+P)	mode="serial"		;;
260		M)	mode="${OPTARG}"	;;
261		C)	numcpus="${OPTARG}"	;;
262		*)	usage			;;
263	esac
264done
265shift $((OPTIND-1))
266
267printmsg "# width=${m_width}"
268printmsg "# height=${m_height}"
269printmsg "# max_mag=${max_mag}"
270printmsg "# stepwidth=${stepwidth}"
271printmsg "# symbollist='${symbollist}'"
272printmsg "# mode=${mode}"
273
274(( symbollistlen=${#symbollist}-1 ))
275
276((
277	x_max=m_width*stepwidth/2. ,
278	x_min=-x_max ,
279	y_max=m_height*stepwidth/2. ,
280	y_min=-y_max
281))
282
283case "${mode}" in
284	parallel)	loop_parallel	; exit $? ;;
285	serial)		loop_serial	; exit $? ;;
286	*)		fatal_error $"Unknown mode \"${mode}\"." ;;
287esac
288
289fatal_error "not reached."
290# EOF.
291