-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathsynthetic-task.sh
executable file
·163 lines (121 loc) · 4.63 KB
/
synthetic-task.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
#!/bin/bash
# Version 1.02.
#
# This script helps you create simple, dummy computing tasks that
# run in a given number of child processes for a given number of iterations.
#
# The goal is to simulate a parallel-build load that you would usually start
# with a "make -j nn" command. A number of parallel processes are started,
# which then run other processes sequentially. It is not exactly the same as
# a parallel make, but it is close enough for some performance test scenarios.
#
# When the tasks are completed, the elapsed wall-clock time is reported.
#
# You would normally use this tool to test your system's behaviour under load.
# For example, I have used it to determine how background, low-priority tasks
# affect the performance of foreground, high-priority tasks.
#
# There are certainly more-advanced load and benchmark tools. However, they often
# lack the necessary flexibility for a specific test and are hard to modify,
# so this little script might prove handy after all.
#
# Copyright (c) 2014 R. Diez - Licensed under the GNU AGPLv3
set -o errexit
set -o nounset
set -o pipefail
abort ()
{
echo >&2 && echo "Error in script \"$0\": $*" >&2
exit 1
}
echo_dev_null_loop ()
{
local -i INDEX
for ((INDEX = 0; INDEX < NUMBER_OF_ITERATIONS_PER_SUBPROCESS; ++INDEX)); do
echo "Do nothing useful here." > /dev/null
done
}
empty_loop ()
{
local -i INDEX
for ((INDEX = 0; INDEX < NUMBER_OF_ITERATIONS_PER_SUBPROCESS; ++INDEX)); do
:
done
}
synthetic_task ()
{
# You could use variables CHILD_INDEX and SUBPROCESS_INVOCATION_INDEX here
# in order to select a particular task to run this time around.
# echo "Child process $CHILD_INDEX, suprocess $SUBPROCESS_INVOCATION_INDEX."
$TASK_ROUTINE_NAME
}
child_process ()
{
# echo "Child process $CHILD_INDEX starts for $NUMBER_OF_SUBPROCESS_INVOCATIONS_PER_CHILD subprocess invocations."
# declare -g not supported in Bash 4.1.17, shipped with Cygwin.
# declare -gi SUBPROCESS_INVOCATION_INDEX
if (( NUMBER_OF_SUBPROCESS_INVOCATIONS_PER_CHILD == 0 )); then
SUBPROCESS_INVOCATION_INDEX=0
synthetic_task
else
for ((SUBPROCESS_INVOCATION_INDEX = 0; SUBPROCESS_INVOCATION_INDEX < NUMBER_OF_SUBPROCESS_INVOCATIONS_PER_CHILD; ++SUBPROCESS_INVOCATION_INDEX)); do
synthetic_task &
wait "$!"
done
fi
# echo "Child process ends."
}
read_uptime ()
{
local PROC_UPTIME_CONTENTS
PROC_UPTIME_CONTENTS="$(</proc/uptime)"
local PROC_UPTIME_COMPONENTS
IFS=$' \t' read -r -a PROC_UPTIME_COMPONENTS <<< "$PROC_UPTIME_CONTENTS"
UPTIME=${PROC_UPTIME_COMPONENTS[0]}
}
# ------------ Entry point ------------
if (( $# != 4 )); then
abort "Invalid command-line arguments. See this script's source code for more information."
fi
# ---------- Command-line arguments, begin ----------
# How many processes should start in parallel. This is similar to GNU Make's -j argument.
# A value of 0 child process means the current process will run the synthetic task,
# whereas a value of 1 creates a child process that then runs the task.
declare -i NUMBER_OF_CHILD_PROCESSES="$1"
# How may times each parallel child process should run the synthetic task in a subprocess.
# Subprocesses are NOT started in parallel, but run sequentially.
# A value of 0 here means that the child processes will run the synthetic task once
# by themselves, whereas a value of 1 would make them create a child subprocess in order to
# run the task once.
declare -i NUMBER_OF_SUBPROCESS_INVOCATIONS_PER_CHILD="$2"
declare -i NUMBER_OF_ITERATIONS_PER_SUBPROCESS="$3"
# Which task to run. For example, "empty_loop" or "echo_dev_null_loop".
TASK_ROUTINE_NAME="$4"
# ---------- Command-line arguments, end ----------
read_uptime
SYSTEM_UPTIME_BEGIN="$UPTIME"
if (( NUMBER_OF_CHILD_PROCESSES == 0 )); then
echo "Starting synthetic task..."
CHILD_INDEX=0
child_process
else
echo "Starting $NUMBER_OF_CHILD_PROCESSES child process(es)..."
declare -a SUBPROCESSES
declare -i CHILD_INDEX
for ((CHILD_INDEX = 0; CHILD_INDEX < NUMBER_OF_CHILD_PROCESSES; ++CHILD_INDEX)); do
child_process &
SUBPROCESSES+=($!)
done
echo "Waiting for all child processes to finish..."
declare -i INDEX
for ((INDEX = 0; INDEX < NUMBER_OF_CHILD_PROCESSES; ++INDEX)); do
declare -i JOB_ID
JOB_ID="${SUBPROCESSES[$INDEX]}"
wait "$JOB_ID"
done
fi
read_uptime
SYSTEM_UPTIME_END="$UPTIME"
# Tool 'bc' does not print the leading zero, so that is why there is an "if" statement in the expression below.
ELAPSED_TIME="$(bc <<< "scale=2; result = $SYSTEM_UPTIME_END - $SYSTEM_UPTIME_BEGIN; if (result < 1 ) print 0; result")"
echo "All child processes finished, elapsed time: $ELAPSED_TIME seconds"